lib

package module
v0.56.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 28, 2026 License: BSD-2-Clause Imports: 13 Imported by: 0

Documentation

Index

Constants

View Source
const CreateTaskCommandOperation base.CommandOperation = "create-task"

CreateTaskCommandOperation is the Kafka command operation for creating a new vault task. The controller materializes a task file at the standard vault location for the given task_identifier. If a file already exists for that identifier, the command is a no-op.

View Source
const IncrementFrontmatterCommandOperation base.CommandOperation = "increment-frontmatter"

IncrementFrontmatterCommandOperation is the Kafka command operation for atomically incrementing a single frontmatter field by a delta. Published by the executor on agent-task-v1-request; handled by the controller.

View Source
const UpdateFrontmatterCommandOperation base.CommandOperation = "update-frontmatter"

UpdateFrontmatterCommandOperation is the Kafka command operation for atomically setting specific frontmatter keys without touching other keys.

Variables

View Source
var CDBSchemaIDs = cdb.SchemaIDs{
	TaskV1SchemaID,
}
View Source
var TaskV1SchemaID = cdb.SchemaID{
	Group:   "agent",
	Kind:    "task",
	Version: "v1",
}

Functions

func ExtractSection added in v0.54.0

func ExtractSection[T any](ctx context.Context, md *Markdown, heading string) (*T, error)

ExtractSection reads `heading` from the markdown, finds the first ```json fence inside its body, unmarshals into T, and returns a typed pointer.

Errors are formatted for use as needs_input messages:

  • "<heading> section missing" — heading not found
  • "<heading>: json block missing" — no ```json fence in section
  • "<heading>: json malformed: <detail>" — unmarshal failed

func ExtractSectionMap added in v0.54.0

func ExtractSectionMap(ctx context.Context, md *Markdown, heading string) (map[string]any, error)

ExtractSectionMap is the untyped variant — useful when the schema is not known statically.

func PrintResult added in v0.54.0

func PrintResult(result *Result) error

PrintResult marshals a framework Result to JSON and prints to stdout. nil result is a no-op (returns nil error). Used by agent main.go entry points to surface the terminal step outcome on stderr/stdout for log aggregators and the K8s Job exit observer.

Types

type AIParser added in v0.54.0

type AIParser interface {
	// Parse reads taskContent (markdown) and populates target (a pointer
	// to a typed struct).
	Parse(ctx context.Context, taskContent string, target any) error
}

AIParser is the boundary translator: fuzzy markdown → typed Go struct.

Concrete implementations wrap Gemini structured output, Claude with JSON mode, or any other LLM that produces structured outputs. They derive a JSON schema from the target type and instruct the LLM to emit conforming output.

Concrete impls live alongside their AI provider (e.g. lib/gemini for Gemini-backed parser). The interface lives here so framework code (ParseStep) can compose any provider without coupling.

type Agent added in v0.54.0

type Agent struct {
	// contains filtered or unexported fields
}

Agent is a composed list of phases. Build via NewAgent.

func NewAgent added in v0.54.0

func NewAgent(phases ...Phase) *Agent

NewAgent constructs an Agent from one or more phases.

Phase names must be unique. Duplicates are rejected at Run time.

func (*Agent) Run added in v0.54.0

func (a *Agent) Run(
	ctx context.Context,
	phaseName domain.TaskPhase,
	taskContent string,
	deliverer ResultDeliverer,
) (*Result, error)

Run dispatches by phase and walks the matching step list.

phaseName is the requested phase from the K8s Job env (PHASE) or the CLI flag. Unknown or empty phaseName produces a Failed result via the deliverer (fail-loud sentinel — never a silent escalation).

taskContent is parsed once into *Markdown; the parsed Markdown is mutated by successive steps and re-serialized for each save.

type AgentResultInfo added in v0.54.0

type AgentResultInfo struct {
	Status  AgentStatus
	Output  string // body content (typically heading + fenced JSON)
	Message string // human-readable status; used by failure/needs_input paths
	// NextPhase is the task phase the agent requests the controller to write
	// when Status == AgentStatusDone. Ignored on Failed/NeedsInput (failure
	// paths always escalate to human_review). Empty means "use default"
	// (phase: done on Status: done). Valid values are vault-cli TaskPhase
	// enum strings: planning, in_progress, ai_review, human_review, done.
	NextPhase string
}

AgentResultInfo holds the minimum fields a deliverer needs to publish a step's result. ResultDeliverer.DeliverResult takes this directly.

type AgentStatus added in v0.54.0

type AgentStatus string

AgentStatus represents the outcome status of a step (or single-shot agent).

const (
	// AgentStatusDone indicates the step completed successfully.
	// On the last step of a phase, set NextPhase to advance.
	// On a mid-phase step, leave NextPhase empty (in-place save).
	AgentStatusDone AgentStatus = "done"

	// AgentStatusInProgress indicates the step completed and saved partial state,
	// but the phase is not yet complete. Phase frontmatter is preserved.
	// Used by multi-step phases for in-place progress saves between steps.
	// NextPhase is ignored on this status.
	AgentStatusInProgress AgentStatus = "in_progress"

	// AgentStatusFailed indicates a transient infrastructure failure.
	// Controller retries (trigger_count++); after max_triggers, escalates.
	AgentStatusFailed AgentStatus = "failed"

	// AgentStatusNeedsInput indicates a semantic problem in the task body.
	// Routed straight to human_review — retrying won't help.
	AgentStatusNeedsInput AgentStatus = "needs_input"
)

type BodySection added in v0.53.1

type BodySection struct {
	Heading string `json:"heading"`
	Section string `json:"section"`
}

BodySection describes an idempotent body-section write: the controller's UpdateFrontmatterExecutor calls ReplaceOrAppendSection(content, Heading, Section). Heading MUST include the markdown prefix (e.g. "## Failure"). Section MUST include the heading as its first line and a trailing newline.

type CreateTaskCommand added in v0.55.0

type CreateTaskCommand struct {
	TaskIdentifier TaskIdentifier  `json:"taskIdentifier"`
	Frontmatter    TaskFrontmatter `json:"frontmatter"`
	Body           string          `json:"body,omitempty"`
}

CreateTaskCommand is the payload for CreateTaskCommandOperation. The controller creates a new vault task file at the standard path for TaskIdentifier, writing the supplied Frontmatter and optional Body. If a file for TaskIdentifier already exists the command is a strict no-op (idempotent). Frontmatter MUST include at minimum "assignee" and "status" keys; the executor rejects the command with a validation error if either is absent.

type IncrementFrontmatterCommand added in v0.53.1

type IncrementFrontmatterCommand struct {
	TaskIdentifier TaskIdentifier `json:"taskIdentifier"`
	Field          string         `json:"field"`
	Delta          int            `json:"delta"`
}

IncrementFrontmatterCommand is the payload for IncrementFrontmatterCommandOperation. The controller reads the current value of Field from disk, adds Delta, and writes the result atomically — so the write is never idempotent.

type Markdown added in v0.54.0

type Markdown struct {
	Frontmatter TaskFrontmatter
	Preamble    string
	Sections    []Section
}

Markdown is a parsed task document: frontmatter + preamble (text before the first section) + ordered list of sections.

Steps mutate Markdown in place via the methods below. The framework re-serializes Markdown via Marshal after each step's Run and publishes the new content via the deliverer.

func ParseMarkdown added in v0.54.0

func ParseMarkdown(_ context.Context, content string) (*Markdown, error)

ParseMarkdown parses raw markdown into a Markdown document.

Best-effort parsing: invalid YAML returns an empty Frontmatter without error. Sections are split at every "# " or "## " heading; "### " and deeper sub-headings are part of the parent section's Body.

func (*Markdown) AddSection added in v0.54.0

func (m *Markdown) AddSection(section Section)

AddSection appends a section to the end of the section list.

Use ReplaceSection if a section with the same heading might already exist; AddSection does not deduplicate.

func (*Markdown) FindSection added in v0.54.0

func (m *Markdown) FindSection(heading string) (*Section, bool)

FindSection returns a pointer to the first section matching heading, and a bool indicating presence. Mutating the returned section's fields updates the Markdown in-place.

func (*Markdown) InsertSection added in v0.54.0

func (m *Markdown) InsertSection(pos int, section Section)

InsertSection inserts a section at the given position. Out-of-range positions clamp to [0, len(Sections)].

func (*Markdown) Marshal added in v0.54.0

func (m *Markdown) Marshal(ctx context.Context) (string, error)

Marshal serializes the Markdown back to a markdown string.

Output: "---\n<yaml>\n---\n<preamble><section><section>..." Each section is rendered as "<heading>\n\n<body>\n" if body is non-empty, or "<heading>\n" if body is empty.

func (*Markdown) ReplaceSection added in v0.54.0

func (m *Markdown) ReplaceSection(section Section)

ReplaceSection replaces the existing section with the same Heading, or appends if no match exists. Idempotent for "save my output" steps.

type ParseStep added in v0.54.0

type ParseStep[T any] struct {
	// contains filtered or unexported fields
}

ParseStep wraps an AIParser as a Step.

Boundary translator: markdown → typed Go struct → ## Section JSON. Use this for the planning phase of code-driven agents that take fuzzy human-written tasks.

func NewParseStep added in v0.54.0

func NewParseStep[T any](
	name string,
	parser AIParser,
	heading string,
	nextPhase string,
) *ParseStep[T]

NewParseStep constructs a ParseStep[T].

name: step name for logs (e.g. "parse-plan") parser: the AI parser implementation (Gemini structured output, etc.) heading: the body section to write the typed result to (e.g. "## Plan") nextPhase: the phase to advance to on success

func (*ParseStep[T]) Name added in v0.54.0

func (s *ParseStep[T]) Name() string

Name implements Step.

func (*ParseStep[T]) Run added in v0.54.0

func (s *ParseStep[T]) Run(ctx context.Context, md *Markdown) (*Result, error)

Run invokes the parser, marshals the typed result as a Section, returns Result.

func (*ParseStep[T]) ShouldRun added in v0.54.0

func (s *ParseStep[T]) ShouldRun(_ context.Context, md *Markdown) (bool, error)

ShouldRun returns false if the target section already exists.

type Phase added in v0.54.0

type Phase struct {
	Name  domain.TaskPhase
	Steps []Step
}

Phase ties a phase name to an ordered list of steps.

Compose with NewAgent:

NewAgent(
    NewPhase("planning",   NewParseStep(parser, "## Plan", "in_progress")),
    NewPhase("in_progress", NewExecuteStep(runner, fetcher)),
    NewPhase("ai_review",  NewVerifyStep(checker)),
)

func NewPhase added in v0.54.0

func NewPhase(name domain.TaskPhase, steps ...Step) Phase

NewPhase constructs a Phase. Variadic steps for ergonomics.

type Result added in v0.54.0

type Result struct {
	// Status: Done | InProgress | Failed | NeedsInput.
	Status AgentStatus

	// NextPhase advances the phase frontmatter on Status: Done. Empty
	// means "stay in current phase" — used for in-place saves between
	// steps in a multi-step phase.
	NextPhase string

	// Message is a human-readable status. Required for Failed/NeedsInput.
	Message string

	// ContinueToNext signals whether the StepRunner should proceed to
	// the next step in the same Job invocation (true) or exit and let
	// the controller re-trigger the same phase (false).
	//
	// Default is exit-after-save. Multi-step phases set this to true on
	// intermediate steps and let the last step decide.
	ContinueToNext bool
}

Result tells the StepRunner what status to deliver and whether to advance.

Body and frontmatter changes are NOT in Result — they're applied by mutating *Markdown in Run. This keeps the durability model clear: at any point during Run, the Markdown IS the durable view.

type ResultDeliverer added in v0.54.0

type ResultDeliverer interface {
	DeliverResult(ctx context.Context, result AgentResultInfo) error
}

ResultDeliverer publishes an agent step result back to the task controller.

Implementations live in lib/delivery: NoopResultDeliverer (tests), FileResultDeliverer (local CLI), KafkaResultDeliverer (production K8s).

type Section added in v0.54.0

type Section struct {
	Heading string
	Body    string
}

Section is one heading-bounded block in a parsed markdown document.

Heading is the exact heading line including '#' characters, e.g. "## Plan". Body is the content between this heading and the next at the same or higher level (no trailing newline).

Section is the parsed structural unit. The CQRS BodySection (in agent_task-commands.go) is a different concept: it carries the full serialized section text including the heading line, used as a partial frontmatter+body update payload.

func MarshalSectionTyped added in v0.54.0

func MarshalSectionTyped[T any](ctx context.Context, heading string, value T) (Section, error)

MarshalSectionTyped renders a typed value as a Section ready for markdown.AddSection or markdown.ReplaceSection.

Output Section.Body format:

```json
{
  "field": "value"
}
```

Round-trips with ExtractSection for the same heading and type.

type Step added in v0.54.0

type Step interface {
	// Name identifies the step. Convention: lower-kebab-case.
	Name() string

	// ShouldRun returns true if the step should execute. Inspects markdown
	// state (frontmatter, sections) and returns false if the step has
	// already completed (idempotency guard).
	//
	// Guards must be cheap — no expensive I/O. Use existing markdown state.
	ShouldRun(ctx context.Context, md *Markdown) (bool, error)

	// Run performs the step's work, mutating markdown in-place. Returns a
	// Result describing status + phase transition. Body content changes
	// flow through markdown.AddSection / ReplaceSection; frontmatter
	// changes flow through direct map mutation.
	//
	// The framework re-serializes markdown via Marshal after Run returns
	// and publishes the new content via the deliverer.
	Run(ctx context.Context, md *Markdown) (*Result, error)
}

Step is one unit of work within a phase. Always code; may wrap AI calls.

Three responsibilities:

  • Name: identifies the step in logs and tests.
  • ShouldRun: cheap guard that inspects task state to decide skip vs run.
  • Run: performs work, mutating markdown in-place. Returns a Result describing status + phase transition (NOT body content — body changes happen via markdown.AddSection / ReplaceSection / Frontmatter mutation).

type StepRunner added in v0.54.0

type StepRunner struct {
	// contains filtered or unexported fields
}

StepRunner walks an ordered list of steps for a single phase invocation.

On each iteration:

  1. step.ShouldRun(markdown) — skip if false
  2. step.Run(markdown) — mutates markdown in-place, returns Result
  3. markdown.Marshal() → newContent
  4. deliverer.DeliverResult(newContent, status, nextPhase)
  5. Decides whether to continue based on Status, NextPhase, ContinueToNext

Resume semantics: the runner does NOT track "current step" state. On re-invocation, all steps are re-walked; their ShouldRun guards skip completed work based on saved markdown state. Markdown state IS the resume cursor.

func NewStepRunner added in v0.54.0

func NewStepRunner(deliverer ResultDeliverer, steps ...Step) *StepRunner

NewStepRunner constructs a StepRunner with the given step list and deliverer.

func (*StepRunner) Run added in v0.54.0

func (r *StepRunner) Run(ctx context.Context, md *Markdown) (*Result, error)

Run walks the step list, calling guards, running, marshaling, and saving. Returns the last delivered Result, or nil if no step executed.

type Task

type Task struct {
	base.Object[base.Identifier]
	TaskIdentifier TaskIdentifier  `json:"taskIdentifier"`
	Frontmatter    TaskFrontmatter `json:"frontmatter"`
	Content        TaskContent     `json:"content"`
}

Task is the payload published by an agent when it finishes a task. task/controller consumes this from agent-task-v1-request and writes it to the vault file. Frontmatter is a generic map — task/controller serializes it to YAML without interpreting individual fields. Content is the markdown body after the frontmatter closing delimiter. The agent owns the content transformation (status, phase, Result section, etc.).

func (Task) Ptr

func (t Task) Ptr() *Task

func (Task) Validate

func (t Task) Validate(ctx context.Context) error

type TaskAssignee

type TaskAssignee string

TaskAssignee identifies which agent type handles this task. Matched against Config CRD spec.assignee.

func (TaskAssignee) String

func (t TaskAssignee) String() string

func (TaskAssignee) Validate

func (t TaskAssignee) Validate(ctx context.Context) error

type TaskContent

type TaskContent string

TaskContent is the markdown body of a task after the frontmatter closing delimiter.

func (TaskContent) String

func (t TaskContent) String() string

func (TaskContent) Validate

func (t TaskContent) Validate(ctx context.Context) error

type TaskFrontmatter

type TaskFrontmatter map[string]interface{}

TaskFrontmatter is a generic map of frontmatter key-value pairs. Serializable as JSON (Kafka) and YAML (vault file). Typed accessors provide type-safe access to well-known fields.

func (TaskFrontmatter) Assignee

func (f TaskFrontmatter) Assignee() TaskAssignee

func (TaskFrontmatter) CurrentJob added in v0.37.0

func (f TaskFrontmatter) CurrentJob() string

CurrentJob returns the K8s Job name recorded when the executor spawned a Job for this task. Returns an empty string when not set.

func (TaskFrontmatter) Int added in v0.54.0

func (f TaskFrontmatter) Int(key string) (int, bool)

Int reads an integer field by key, accepting both int (JSON-decoded) and float64 (YAML-decoded) underlying types. ok is false when the key is absent or holds a non-numeric value. Generic accessor for ad-hoc fields without dedicated typed methods.

func (TaskFrontmatter) MaxRetries added in v0.37.0

func (f TaskFrontmatter) MaxRetries() int

MaxRetries returns the maximum number of failures allowed before escalation. Returns 3 when the field is absent (spec default).

func (TaskFrontmatter) MaxTriggers added in v0.53.1

func (f TaskFrontmatter) MaxTriggers() int

MaxTriggers returns the maximum number of spawn-trigger events allowed for this task. Returns 3 if the field is absent, matching the default for max_retries.

func (TaskFrontmatter) Phase

func (f TaskFrontmatter) Phase() *domain.TaskPhase

func (TaskFrontmatter) RetryCount added in v0.37.0

func (f TaskFrontmatter) RetryCount() int

RetryCount returns the number of failed attempts recorded in frontmatter. Returns 0 when the field is absent.

func (TaskFrontmatter) SpawnNotification added in v0.37.0

func (f TaskFrontmatter) SpawnNotification() bool

SpawnNotification returns true when this result is a job-spawn tracking update rather than an agent outcome. The controller skips the retry counter for these.

func (TaskFrontmatter) Stage

func (f TaskFrontmatter) Stage() string

Stage returns the execution stage from the "stage" key. Returns "prod" if the key is absent or empty.

func (TaskFrontmatter) Status

func (f TaskFrontmatter) Status() domain.TaskStatus

func (TaskFrontmatter) String added in v0.54.0

func (f TaskFrontmatter) String(key string) (string, bool)

String reads a string field by key. ok is false when the key is absent or holds a non-string value. Generic accessor for ad-hoc fields without dedicated typed methods.

func (TaskFrontmatter) TriggerCount added in v0.53.1

func (f TaskFrontmatter) TriggerCount() int

TriggerCount returns the number of spawn-trigger events that have fired for this task. Returns 0 if the field is absent.

type TaskIdentifier

type TaskIdentifier string

TaskIdentifier uniquely identifies an agent task.

func (TaskIdentifier) Bytes

func (t TaskIdentifier) Bytes() []byte

func (TaskIdentifier) Equal

func (t TaskIdentifier) Equal(identifier base.ObjectIdentifier) bool

func (TaskIdentifier) Ptr

func (t TaskIdentifier) Ptr() *TaskIdentifier

func (TaskIdentifier) String

func (t TaskIdentifier) String() string

func (TaskIdentifier) Validate

func (t TaskIdentifier) Validate(ctx context.Context) error

type TaskIdentifierGenerator

type TaskIdentifierGenerator base.IdentifierGenerator[TaskIdentifier]

TaskIdentifierGenerator generates unique task identifiers.

type TaskIdentifiers

type TaskIdentifiers []TaskIdentifier

TaskIdentifiers is a slice of TaskIdentifier.

func (TaskIdentifiers) Contains

func (t TaskIdentifiers) Contains(value TaskIdentifier) bool

Contains returns true if the slice contains the given identifier.

type UpdateFrontmatterCommand added in v0.53.1

type UpdateFrontmatterCommand struct {
	TaskIdentifier TaskIdentifier  `json:"taskIdentifier"`
	Updates        TaskFrontmatter `json:"updates"`
	Body           *BodySection    `json:"body,omitempty"`
}

UpdateFrontmatterCommand is the payload for UpdateFrontmatterCommandOperation. Merges Updates into the existing frontmatter (partial merge — absent keys preserved). When Body is set, its section is appended to (or replaced in) the task body via lib/delivery.ReplaceOrAppendSection. Unset Body means frontmatter-only update.

Directories

Path Synopsis
Code generated by counterfeiter.
Code generated by counterfeiter.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL