Documentation
¶
Overview ¶
Package taskgraph is a plan the model keeps for itself: a DAG of tasks a person can review, shared by a main agent and the sub-agents it hands parts to. It holds the nodes, the edges they wait on, the rules for a status change, the one answer to "can this start", an opaque owner per node, the `plan` tool (Tool), and what each write changed and for whom (Diff, Apply).
It is optional, like files and loopdetector: a host mounts it; agent does not import it. It imports llm (the tool definition) and stdlib only.
Where the line is. A plan the model keeps decides nothing about which session runs, when, or why, so it is not orchestration. Everything that is stays with the host: storage (Store), what an owner is and which session it maps to, who may change what (WithMayChange), approval or a plan mode, delegation (spawning the agent a part is assigned to), scheduling, and how a change reaches an owner (WithOnChange, Apply).
Index ¶
- Constants
- func Clip(s string, max int) string
- func Tool(st Store, actor string, opts ...Option) (llm.ToolDef, func(ctx context.Context, args string) string)
- type Change
- type Graph
- func (g *Graph) Add(n Node) (*Node, error)
- func (g *Graph) Assign(id, owner string) error
- func (g *Graph) Brief(owner string) string
- func (g *Graph) CanStart(id string) (bool, []string)
- func (g *Graph) Clone() Graph
- func (g *Graph) Dependents(id string) []*Node
- func (g *Graph) Detail(n *Node) string
- func (g *Graph) Get(id string) *Node
- func (g *Graph) Line(n *Node) string
- func (g *Graph) List() string
- func (g *Graph) Open() int
- func (g *Graph) Ready(owner string) []*Node
- func (g *Graph) ReadyLine(owner string, max int) string
- func (g *Graph) SetStatus(id string, to Status, why string) error
- func (g *Graph) Subtree(id string) []*Node
- func (g *Graph) Tree() []Row
- func (g *Graph) Update(id string, p Patch) error
- func (g *Graph) Waiting(id string) []string
- type Node
- type Option
- type Patch
- type Row
- type Status
- type Store
Constants ¶
const ToolName = "plan"
ToolName is the model's one tool for the plan.
Variables ¶
This section is empty.
Functions ¶
func Tool ¶
func Tool(st Store, actor string, opts ...Option) (llm.ToolDef, func(ctx context.Context, args string) string)
Tool is the plan tool for actor, over st: its definition and its handler. actor is whom the calls are for ("" when the host has none to tell apart); a node an actor adds is owned by it.
Types ¶
type Change ¶
type Change struct {
ID string
// Before is the node as it was; nil when the write added it.
Before *Node
// After is the node as it is now; nil when the write removed it (nothing
// in this package removes a node, but an fn given to Apply may).
After *Node
// Ready: the node could not start before the write, and can now.
Ready bool
// Tell is the owners that should hear of it, in a stable order, without
// the actor. "" is an owner like any other: what it means is the host's.
Tell []string
}
Change is one node a write changed.
func Apply ¶
Apply runs fn on the plan in st as actor and returns what it changed. It is the one write path: the plan tool goes through it, and so should every write the host makes itself (a person's edit on a screen, an assignment), or those changes are never told to the owners they touch.
The changes are computed inside Update, from the plan Update hands fn, and returned only if Update succeeds: a write the store refused or failed to save tells no one.
type Graph ¶
type Graph struct {
Nodes []*Node `json:"nodes"`
// Seq numbers the next node; ids are never reused.
Seq int `json:"seq,omitempty"`
}
Graph is the whole plan.
func (*Graph) Add ¶
Add makes a node from n's Title, Goal, Needs, Owner, DoneWhen, Notes, Files and Replaces. It starts as todo.
func (*Graph) Assign ¶
Assign gives the subtree of id to owner. It refuses a subtree with a node someone else already owns.
func (*Graph) Brief ¶
Brief is the line a turn carries while the plan has open nodes: where it stands, the node in progress, the next ready ones, what is stuck. At most five lines; "" when nothing is open. owner is the agent it is for.
func (*Graph) CanStart ¶
CanStart is the one answer to "can this node start": it is todo and nothing holds it. The ready set, the tool, the screen and the turn's line all read this and no other.
func (*Graph) Dependents ¶
Dependents are the nodes that need id directly, in node order.
func (*Graph) SetStatus ¶
SetStatus moves node id to a status, if the table allows it. why is the account for done and the reason for blocked and dropped; each needs one.
func (*Graph) Subtree ¶
Subtree is id and every node that hangs from it: whatever needs it, directly or not. It is the unit a sub-agent is given.
func (*Graph) Tree ¶
Tree lays the graph out for a person: a node with no needs is a root, and a node hangs under each node it needs, so one with several parents shows under each, marked as a repeat after the first. Siblings are in node order.
type Node ¶
type Node struct {
ID string `json:"id"`
Title string `json:"title"`
Goal string `json:"goal,omitempty"`
// Status is never "waiting": see Status.
Status Status `json:"status"`
// Needs are the ids this node waits on, until each is done.
Needs []string `json:"needs,omitempty"`
// Owner is an opaque string the host gives meaning to, kept as it is; "" is
// nobody in particular. Ready filters on it.
Owner string `json:"owner,omitempty"`
// DoneWhen is what done means, set when the node is made.
DoneWhen string `json:"done_when"`
Notes string `json:"notes,omitempty"`
// Files are the paths the node expects to change. Nothing enforces it yet;
// parallel runs will need it, to keep siblings off one file.
Files []string `json:"files,omitempty"`
// Replaces is a dropped node this one stands in for. It does not move the
// dropped node's dependents: an update of their needs does.
Replaces string `json:"replaces,omitempty"`
// Why is the reason a node is blocked or dropped.
Why string `json:"why,omitempty"`
// Account is how DoneWhen was met.
Account string `json:"account,omitempty"`
}
Node is one task.
type Option ¶
type Option func(*options)
Option shapes Tool for its host.
func WithMayChange ¶
WithMayChange sets who may change a node: no hook, anyone. An actor that may not gets a result saying so. It also gates what a new node may need.
func WithNote ¶
WithNote appends host sentences to the tool's description: what the plan means here (a mode, files a node lists) is the host's to say.
func WithOnChange ¶
WithOnChange gives the host every write the tool made: the changes (see Diff for who each names in Tell) after the store kept them, never for a refused or failed write, and only when there is at least one. It runs on the tool call's goroutine, outside Update, so it may View the store; it should hand the notices off (agent.Session.Inject) rather than run a turn.
type Patch ¶
Patch is the fields of a node an update may change; nil leaves one alone. Status is todo or doing only: done, blocked and dropped carry what they need (an account, a reason) through SetStatus.
type Row ¶
type Row struct {
Node *Node
Depth int
// Repeat: the node already showed under another parent, so this row
// is a pointer to it and its dependents are not shown again.
Repeat bool
}
Row is one line of the graph as a tree.
type Status ¶
type Status string
Status is where a node stands.
blocked is only ever somebody saying "stuck", with a reason. Waiting on other nodes is computed from the edges (Waiting) and never stored, so a node cannot go stale when a prerequisite moves.
type Store ¶
type Store interface {
View(ctx context.Context) (Graph, error)
Update(ctx context.Context, fn func(g *Graph) error) error
}
Store is where a plan lives. The integration provides it; this package ships no implementation, the same as agent.Store. A file beside a session, a row in a database and a map under a mutex are all the host's choice.
The contract, which this package cannot enforce:
- One Store per plan, shared by every session that works the plan. A main agent and its sub-agents call the same Store; a copy per sub-agent loses updates with no error, since each Update sees only its own copy.
- Update is atomic against every other Update and View of the same plan: fn reads the plan as it is, and its change is kept whole or not at all.
- Update keeps the change only if fn returns nil and the store's own write succeeds; either failure is Update's error and the plan is as it was.
- fn may be called more than once (a store that retries a conflicting transaction), so fn must do nothing but change g. Apply relies on this.
- View is a copy: a caller may change what it returns.