Documentation
¶
Overview ¶
Package subagent runs one bounded child agent conversation in a private, memory-only session and reports its outcome as a bounded Result.
Index ¶
- Constants
- type ActivitySink
- type Choice
- type HeldMove
- type Journal
- func (j *Journal) AbandonMutation(target string, parentDir *os.File)
- func (j *Journal) Absorb(child *Journal) error
- func (j *Journal) BeginDelete(target string, parentDir *os.File) error
- func (j *Journal) BeginMutation(target string, observed, next filetools.JournalState) error
- func (j *Journal) Close()
- func (j *Journal) Covers(target string) bool
- func (j *Journal) DirLifecycle(target, ancestor string) fsutil.DirLifecycle
- func (j *Journal) Finish(reason Reason) JournalOutcome
- func (j *Journal) MarkTransferReady() error
- func (j *Journal) MaxStateBytes() int64
- func (j *Journal) MoveLifecycle(source, destination string) fsutil.MoveLifecycle
- func (j *Journal) Path() string
- func (j *Journal) ReconcilePending()
- func (j *Journal) RemovePublished(target string, parentDir *os.File, published filetools.JournalState)
- func (j *Journal) ResolveMutation(target string, parentDir *os.File, published filetools.JournalState)
- type JournalConfig
- type JournalOutcome
- type LeftAloneJournal
- type Reason
- type Report
- type Request
- type Result
- type RetainedJournal
- type Unresolved
Constants ¶
const DefaultStepCap = 12
DefaultStepCap bounds a child that no caller sized itself: enough rounds to read, change, and verify a small piece of work, and few enough that a looping child ends.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ActivitySink ¶
type ActivitySink interface {
ChildAgentStarted(start events.ChildAgentStart)
ChildAgentCalls(invocation string, lines []string)
ChildAgentEnded(end events.ChildAgentEnd)
}
ActivitySink observes one child agent while it is still running. A tool round is attributed by the invocation id passed beside it, never by having arrived between a start and an end, so children whose lifetimes overlap stay separable.
type Choice ¶
type Choice string
Choice is the answer to one path recovery cannot put back on its own. Defer is the safe answer: nothing is written and the path is offered again at the next launch.
type HeldMove ¶
Resumes marks a move whose linked undo had already begun, so a later recovery continues it rather than leaving it to be fixed by hand.
type Journal ¶
type Journal struct {
// contains filtered or unexported fields
}
Journal is the durable all-or-nothing record of one child agent's declared scope. It holds the dedicated journal directory open for the whole transaction: every record rewrite, every rollback step, and the final cleanup unlink run relative to that descriptor, so a directory swapped in at the path after the child was dispatched can neither redirect an update nor suppress one.
func OpenJournal ¶
func OpenJournal(cfg JournalConfig) (*Journal, error)
OpenJournal snapshots every declared path and writes the transaction's intent to disk before the caller dispatches the child, so a parent that dies without unwinding still leaves a recoverable record. Every refusal here — an unsafe journal location, a path of a kind its declaration cannot restore, or any of the fixed caps — happens before both the child and the journal storage exist.
func (*Journal) AbandonMutation ¶
AbandonMutation resolves a pending record whose publication never happened. The pending state is then a state no one published, so a file holding it is another writer's — content equality cannot stand in for ownership here the way it can after a real publish, and absorbing it would let the rollback restore over, or remove, a file this child never wrote. Only the durable last-known state clears the record; anything else is reported rather than claimed.
func (*Journal) Absorb ¶
Absorb folds a transfer-ready descendant's state into this journal and then deletes it. The parent takes over the child's last-known states and created directories so its own comparison reads the grandchild's writes as child-owned rather than as an external edit.
func (*Journal) BeginDelete ¶
func (*Journal) BeginMutation ¶
func (j *Journal) BeginMutation(target string, observed, next filetools.JournalState) error
func (*Journal) Close ¶
func (j *Journal) Close()
Close removes nothing: a journal is deleted only by a committed or fully rolled-back outcome.
func (*Journal) DirLifecycle ¶
func (j *Journal) DirLifecycle(target, ancestor string) fsutil.DirLifecycle
DirLifecycle records each segment against the path's snapshotted ancestor, which rollback descends from. EnsureDirAt names segments from ancestor, the directory the create started in, and that is deeper than the snapshotted one once an earlier act made or moved in a directory above the target.
func (*Journal) Finish ¶
func (j *Journal) Finish(reason Reason) JournalOutcome
Finish resolves the transaction. A completed child's work stands only when every unresolved declared path still matches the durable last-known child state; every other terminal reason puts the declared scope back as the child found it. A path that matches neither is a third outcome: it is left alone and the journal survives naming it.
func (*Journal) MarkTransferReady ¶
MarkTransferReady hands a completed nested child's journal to its parent. The child never deletes its own journal on completion: until the parent has durably absorbed it, it is the only record of which writes belong to the child rather than to an outside writer.
func (*Journal) MaxStateBytes ¶
func (*Journal) MoveLifecycle ¶
func (j *Journal) MoveLifecycle(source, destination string) fsutil.MoveLifecycle
func (*Journal) ReconcilePending ¶
func (j *Journal) ReconcilePending()
ReconcilePending resolves every outstanding pending mutation without a live publish in hand. It is the entry point recovery must run before it compares any unresolved path: only this comparison can tell a crash before publication from a crash after it. Each path's captured target parent is reopened and identity-verified before it is synced and read.
func (*Journal) RemovePublished ¶
func (*Journal) ResolveMutation ¶
func (j *Journal) ResolveMutation(target string, parentDir *os.File, published filetools.JournalState)
ResolveMutation settles the pending record against what the publish actually left. published names the inode the caller put at the path, or is the zero value when it has none to name. A pending record is written before publication and so cannot name the inode it will create; adopting the caller's is what keeps a byte-identical file another writer put at the name in the publish-to- reconcile window from being read as this child's own work. Recovery, which arrives after a crash with no such identity to offer, still falls back to comparing content alone.
type JournalConfig ¶
type JournalConfig struct {
Home string
ProjectRoot string
Invocation string
Declared []string
DeclaredDelete []string
DeclaredMoves []permissions.MovePair
Parent *Journal
}
Declared, DeclaredDelete, and DeclaredMoves must be the canonical scopes resolved before dispatch, not the incomplete record of what a run turned out to touch.
type JournalOutcome ¶
type JournalOutcome struct {
Restored []string
Unrecovered []string
JournalPath string
HeldMoves []HeldMove
}
JournalOutcome is what a terminal transaction did. Unrecovered is an outcome in its own right, distinct from both a clean commit and a clean rollback: the named paths are left as they are and the journal survives for recovery or the user to resolve.
type LeftAloneJournal ¶
type Report ¶
type Report struct {
Restored []string
Kept []string
Retained []RetainedJournal
LeftAlone []LeftAloneJournal
}
func Recover ¶
func Recover(home, projectRoot string, ask func(Unresolved) Choice) Report
Recover puts back the declared writes of every child agent whose codemeld process died before it could unwind them, for the project folder being opened. A journal whose recorded process cannot be proved gone belongs to another possibly running codemeld and is read but never written. ask decides the paths that match neither the state the child left nor the state captured before it ran; a nil ask defers all of them.
type Request ¶
type Request struct {
Instruction string
Model model.Selection
ModelSource model.Source
SupportedEffort func(providerID, modelID string) []string
SystemPrompt string
Client provider.Client
Tools []provider.ToolDefinition
Dispatcher turn.ToolDispatcher
StepCap int
// Journal is opened before the request is built, because a run that dies mid-dispatch must
// already have its snapshot on disk.
Journal *Journal
// Activity and InvocationID are optional and belong together: the caller that minted the
// id owns the sink, and validate requires neither.
Activity ActivitySink
InvocationID string
}
Request carries snapshots the caller takes at delegation time. Dispatcher is the authorization seam: the caller builds it against the child's mode context, and this package never assembles authority of its own.
type Result ¶
type Result struct {
Reply string
ReplyTruncated bool
// Requests started, not completed rounds: a request whose stream fails after it
// opened is still counted.
ProviderRequests int
ToolNames []string
// WriteTargets are the files the child's successful edit and create calls wrote, in call
// order. It is not a complete record of what the child changed on disk — a create may
// also have made parent directories, and a call that changed a file before reporting a
// failure is absent — so it cannot stand in for a rollback snapshot.
WriteTargets []string
DeleteTargets []string
MoveTargets []turn.MoveTarget
Reason Reason
RestoredPaths []string
// Unrecovered is a third outcome alongside a clean rollback and a clean commit: the named paths
// were left as they are and RetainedJournal survives for recovery or the user to resolve.
Unrecovered []string
RetainedJournal string
}
type RetainedJournal ¶
type Unresolved ¶
type Unresolved struct {
Path string
}