mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
docs(durable): specify ownership and subagents (Package 18)
This commit is contained in:
parent
fddc968b96
commit
9f1013506f
2 changed files with 55 additions and 22 deletions
|
|
@ -11,7 +11,7 @@ facades, membranes, document routing, view projection, events, or clone chains.
|
|||
|
||||
- Obsolete `pico` and `pico4` prototypes were removed.
|
||||
- `pico3` remains.
|
||||
- Packages 1–17 are implemented in `packages/durable`; Package 10 was already satisfied by Chord's canonical structural diff implementation.
|
||||
- Packages 1–18 are implemented in `packages/durable`; Package 10 was already satisfied by Chord's canonical structural diff implementation.
|
||||
|
||||
## 1. Records, cursors, and memory tables
|
||||
|
||||
|
|
@ -583,6 +583,16 @@ waits. Finish invocation-bound owned APIs used by tools and the foreground and
|
|||
background subagent provisioning patterns, including atomic task/conversation/
|
||||
registry creation and request-ID-safe submission recovery.
|
||||
|
||||
An abort cascade withdraws queued inputs in every owned conversation it reaches
|
||||
(spec §5.4). There is no full-teardown primitive: `close()` stops everything
|
||||
without outcomes, and a host cancels everything by aborting what `inspect()`
|
||||
lists. No built-in subagent tool or supervisor task: two concise, product-style
|
||||
examples, `test/examples/22-subagent-foreground.ts` (child owned by the tool task,
|
||||
reported through `api.details({ conversationId })`, the UI attaching to the
|
||||
child's events) and `test/examples/23-subagent-background.ts` (supervisor task,
|
||||
`app.subagents` name→conversation document, request-ID-safe submission), show
|
||||
the patterns.
|
||||
|
||||
Test deep ownership trees, owner edges after terminal settlement, nested
|
||||
background boundaries, conversation abort/join with surviving passive writes and
|
||||
background tasks, cancellation of waiters without cancellation of work, and
|
||||
|
|
|
|||
|
|
@ -684,10 +684,9 @@ a user message when given, and then resolves; while busy, placement follows
|
|||
section 6 and may occur later. Observe its placement through the conversation
|
||||
watch. An idle wait does not guarantee placement of queued passive writes.
|
||||
|
||||
`abortTask()` commits `abortRequested` and the durable foreground-subtree
|
||||
cascade, then signals and joins the active run; the scheduler then starts the
|
||||
abort invocation. Marking without signalling is internal: the cascade marks
|
||||
owned tasks in the same commit. `Conversation.abort()` withdraws queued
|
||||
`abortTask()` commits `abortRequested`, then signals and joins the active run;
|
||||
the scheduler then starts the abort invocation and marks the foreground-owned
|
||||
subtree in its next reconcile commit (section 5.4). `Conversation.abort()` withdraws queued
|
||||
input submissions, marks non-background tasks selected by ordinary ownership
|
||||
traversal, signals them, and resolves only after that scope is ordinarily idle.
|
||||
Passive writes and background subtrees survive. Conversation idle means no live
|
||||
|
|
@ -1685,9 +1684,10 @@ owner edges after the owner becomes terminal, but a background owner is a
|
|||
boundary: ordinary traversal skips that task and its complete owned subtree.
|
||||
Direct conversation operations start inside that conversation regardless of its
|
||||
owner. Directly aborting a live background task includes that task and follows
|
||||
its ordinary owned subtree; nested background owners remain boundaries. Full
|
||||
teardown crosses every boundary, marks every live task, and must seal new
|
||||
admission while it gathers the complete indexed ownership subtree.
|
||||
its ordinary owned subtree; nested background owners remain boundaries.
|
||||
`close()` stops every invocation, foreground and background, without writing an
|
||||
outcome, so the work resumes on reopen. A host that must durably cancel
|
||||
everything aborts each live task that `inspect()` lists.
|
||||
|
||||
`Conversation.abort()` withdraws queued inputs and marks live non-background
|
||||
tasks selected by ordinary traversal. `Conversation.waitForIdle()` waits until
|
||||
|
|
@ -1697,8 +1697,20 @@ counting tasks, so ordinary work below a background owner does not block it.
|
|||
Explicit `waitForTask()` waits for its referenced task regardless of the task's
|
||||
background flag.
|
||||
|
||||
An abort mark atomically and idempotently cascades to foreground-owned work,
|
||||
including conversations, tasks, and submissions staged in the same transaction.
|
||||
An abort mark cascades idempotently to foreground-owned work. The durable
|
||||
cancellation intent is the owner's own record: its abort mark, or a terminal
|
||||
outcome other than `completed`. The scheduler derives the rest in a later
|
||||
reconcile commit, and again at open, so a crash in between loses nothing; work
|
||||
already queued on the Session line may make one more commit first. Every live
|
||||
non-background task whose owner chain
|
||||
reaches a cancelled owner, without first crossing a background owner that is not
|
||||
itself cancelled, gets an abort mark, including work created there after the
|
||||
cascade. Deriving the marks in their own commit
|
||||
keeps them out of the owner's commit, which may already have written the tasks
|
||||
involved. Every conversation the cascade reaches is treated like
|
||||
`Conversation.abort()`: its queued input submissions become `unanswered` with
|
||||
reason `aborted` and leave its inbox, while queued writes stay. The cascade starts
|
||||
below the cancelled task, so that task's own conversation keeps its queue.
|
||||
Terminal outcomes `failed`, `faulted`, `orphaned`, and `aborted` record the same
|
||||
durable cancellation intent; `completed` does not. Conversation records and
|
||||
owner edges are never retired with the task. Active invocations are signalled
|
||||
|
|
@ -2421,10 +2433,15 @@ the same way; the admitted submission remains durable after those methods reject
|
|||
Invocation-owned document watches stop when the invocation ends.
|
||||
|
||||
A foreground subagent conversation is explicitly owned by its tool task. One
|
||||
transaction creates or forks the child and records its durable registry mapping;
|
||||
after settlement the tool reacquires the child, submits with the registered
|
||||
request ID, and waits for that submission's result. Aborting or abnormally
|
||||
terminalizing the tool task cascades through the owned scope.
|
||||
transaction creates or forks the child; the owner edge is its durable record, so
|
||||
a replay-safe rerun finds the child with `scanConversations({ ownerTaskId })`
|
||||
before creating one. After settlement the tool reacquires the child, submits
|
||||
with a request ID derived from its task ID, which a rerun keeps, and waits for
|
||||
that submission's result. Provider call IDs are not unique across a Session and
|
||||
must not serve as request IDs. Aborting or abnormally
|
||||
terminalizing the tool task cascades through the owned scope. The tool reports
|
||||
the child in its running details, for example `api.details({ conversationId })`,
|
||||
so a UI that sees the call can attach to the child's view or events.
|
||||
|
||||
A background subagent is provisioned in one transaction. After deduplicating by
|
||||
its durable registry key, the tool stages a background supervisor task `B`, a
|
||||
|
|
@ -2433,7 +2450,8 @@ semantic name to `C` plus its stable request ID. `B` may be staged earlier in th
|
|||
same transaction and used immediately as `C`'s owner. Its durable input contains
|
||||
the exact submission draft and the registry location/key; it need not contain
|
||||
`C`'s not-yet-created ID. Once scheduled, `B` resolves `C` from the mapping,
|
||||
verifies the immutable owner edge, and performs normal `Conversation.submit()`.
|
||||
which was committed together with `C` and its owner edge, and performs normal
|
||||
`Conversation.submit()`.
|
||||
A crash before admission makes `B` retry; a crash after admission returns the
|
||||
existing request-ID-deduplicated `Submission`. The initiating tool may race the
|
||||
same submit for lower latency and wait only for the durable admission receipt.
|
||||
|
|
@ -2443,6 +2461,8 @@ its owned scope.
|
|||
|
||||
Applications may maintain a conversation document mapping semantic subagent
|
||||
names to durable conversation IDs and application-minted submission request IDs.
|
||||
A UI lists background subagents from that document; a child is active while its
|
||||
`pi.live.run` is set.
|
||||
Such a live registry uses `fork: "initial"` so children do not inherit the
|
||||
parent's agent list and its update is not a selected fork source. Conversation
|
||||
creation/forking, supervisor creation when applicable, and the registry mapping
|
||||
|
|
@ -2455,8 +2475,7 @@ request to submit; a crash after admission retries the same request and receives
|
|||
the existing `Submission`. A read-only lookup by conversation/request ID can
|
||||
report absence or return the durable queued/placed/done/unanswered receipt.
|
||||
Submission admission therefore needs no private transaction shortcut and does
|
||||
not move onto `Tx`. Kernel ownership indexes independently drive abort and idle
|
||||
traversal.
|
||||
not move onto `Tx`. Owner edges alone drive abort and idle traversal.
|
||||
|
||||
A tool result may request `addTools`, `terminate`, or `handoff`. Post-tools
|
||||
appends added tool names to the configured loadout; they take effect at the next
|
||||
|
|
@ -2899,14 +2918,18 @@ because the terminal record keeps it; the call is read from the assistant entry.
|
|||
registered policy are `safe`, it executes again with the stored arguments,
|
||||
without `beforeTool`, and settles like `call`. Otherwise it commits an
|
||||
`interrupted` error result from the slot's durable partial output, details, and
|
||||
diagnostics.
|
||||
diagnostics, and ends `failed` with `{ entryId }`.
|
||||
- The result commit bounds the content, appends the diagnostics block (section
|
||||
7.3), appends one `pi.tool-result` entry with `model: [{ role: "toolResult",
|
||||
toolCallId, toolName, content, details, isError, timestamp }]` and
|
||||
`data: { diagnostics }`, marks the slot `done`, and
|
||||
completes with `{ entryId, control }`. An `isError` result still completes.
|
||||
- A throw from `execute()` becomes a `tool_error` result; once the invocation is
|
||||
signalled it propagates instead.
|
||||
- A throw from `execute()` becomes a `tool_error` result and the task ends
|
||||
`failed` with `{ entryId }`; once the invocation is signalled it propagates
|
||||
instead. Ending `failed`, like `aborted`, records cancellation intent (section
|
||||
5.4), so conversations the call owns, which nothing supervises any more, are
|
||||
aborted. A returned `isError` result still completes, and its call's owned work
|
||||
survives. Either way the run continues with the result entry.
|
||||
- The abort handler commits an `aborted` error result from the slot's durable
|
||||
partial output, details, and diagnostics and ends `aborted` with `{ entryId }`.
|
||||
|
||||
|
|
@ -3596,8 +3619,8 @@ These are contracts, not invitations to add defensive machinery:
|
|||
already counts it, and subtree sums would count it twice.
|
||||
- **Aborting one run task:** `abortTask()` on a run's post-tools task ends the
|
||||
run but leaves its tool tasks running; their results land in a conversation
|
||||
that may be idle or running another run. Abort the conversation instead
|
||||
(Package 18); until then, abort the round's tool tasks together with post-tools.
|
||||
that may be idle or running another run. Use `Conversation.abort()`, which
|
||||
aborts every task of the conversation's run, tools included.
|
||||
- **Hook memo names:** hooks share the asking task's memo namespace with the
|
||||
task and other hooks. Prefix memo names.
|
||||
- **Long transactions:** an async commit callback holds the Session mutation
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue