docs: tighten agent execution contracts

This commit is contained in:
Tianyi Cui
2026-07-19 11:58:55 +08:00
parent cfe9b9617d
commit 9912918a48
12 changed files with 41 additions and 87 deletions

View File

@@ -14,6 +14,6 @@ The session log, system-prompt assembly, tool registry, agent vocabulary, and co
`scope/` is the one non-service package here: a dependency-free library (`createScope`/`scopeOf`/`scopeTarget`) the registries and the loop build per-agent scoping on — it sits below `session/` and `system-prompt/` in the module graph precisely so they can consume it without a cycle.
`agent-execution` is mandatory control infrastructure shared by concrete loops and deep process-local consumers. `agent-loop` is the one concrete implementation of the `agent` seam and lives here because it is the harness's default product loop; other plugins depend on the `agent` vocabulary and execution service, never on `agent-loop` directly, so the loop stays swappable.
`agent-execution` is mandatory control infrastructure for concrete loops and deep process-local consumers. `agent-loop` is the one concrete implementation of the `agent` seam and lives here because it is the harness's default product loop. Extension plugins depend on `agent` and, when they need ambient identity, `agent-execution`; they never depend on `agent-loop` directly, so the loop stays swappable.
The default composition that wires this spine into a runnable agent lives in [`examples/agent-spine-demo`](../examples/agent-spine-demo/README.md): one bundle plugin that loads the control spine plus selected default capabilities (`timer` + `llm` + sessions + system-prompt + tools + agents + agent-execution + invariants + the local [skill family](../skill/README.md) + `tool-bash` + workspace-context + `agent-loop`) and forwards `agent-loop`'s `agents` list as its own config. It sits in `examples/` — ready-to-run demo/reference bundles — not in `core/`: `core/` ships the swappable spine pieces, while a demo bundle picks one concrete composition of them and adds a front door.

View File

@@ -8,13 +8,13 @@ Process-local ambient Agent identity for asynchronous work initiated by a concre
- `require()` returns the inherited execution or throws `no agent execution context is active`.
- `run(execution, operation)` returns the exact synchronous value or Promise from `operation`. Passing `undefined` establishes a real boundary that hides an inherited Agent.
The store contains only `{ readonly agent: Agent }`. A Session is available through `agent.session`; turn, step, signal, cwd, sandbox, authorization, and other capability state remain with their explicit owners. Ambient presence identifies the initiator but does not prove that the Agent is live or that an operation is authorized.
The store contains only `{ readonly agent: Agent }`. The `Session` remains available through `agent.session`; turn, step, `signal`, `cwd`, sandbox, authorization, and other capability state remain with their explicit owners. Ambient presence identifies the initiator but does not prove that the Agent is live or that an operation is authorized.
## Lifetime and detached work
Provider teardown rejects new `run()` boundaries, removes the service so injected dependents drain, waits for returned Promise boundaries, then disables its `AsyncLocalStorage`. In-flight code retaining the service can call `current()` and `require()` while it drains; after disposal, all three methods throw `agent execution service is disposed`.
Async resources created inside `run()` inherit its Agent even when the operation does not await them. Agent-owned foreground work may inherit the boundary but keeps using the explicit cancellation and disposal contract of its execution seam. Unrelated timers, queues, and deployment infrastructure start under `run(undefined, operation)` and own an explicit stop. Queue, worker, process, and wire boundaries serialize any identity they need instead of relying on ALS propagation.
Async resources created inside `run()` inherit its Agent even if `operation` returns before they settle, but provider teardown waits only for the Promise returned by `operation`. The owning seam must stop unreturned work explicitly. Unrelated timers, queues, and deployment infrastructure start under `run(undefined, operation)` and own an explicit stop; queue, worker, process, and wire boundaries serialize any identity they need instead of relying on ALS propagation.
## Known Limitations and Deferred Work

View File

@@ -1,6 +1,6 @@
{
"name": "@deepseek-ai/dsh-agent-execution",
"description": "Agent-scoped asynchronous execution context for the DeepSeek Harness",
"description": "Process-local ambient Agent context for asynchronous driver work",
"version": "0.0.1",
"private": true,
"type": "module",

View File

@@ -6,7 +6,7 @@
import type { Agent } from '@deepseek-ai/dsh-agent'
/** The exact live Agent associated with one asynchronous execution chain. */
/** The exact Agent associated with one asynchronous execution chain. */
export interface AgentExecution {
readonly agent: Agent
}

View File

@@ -50,9 +50,7 @@ The concrete `Agent` class, its `Inbox`, `runLoop`, and instance-bound publicati
### Loop lifecycle (`loop.ts`)
The driver owns one agent for its lifetime and runs inside `ctx.agentExecution.run({ agent }, ...)`, so process-local asynchronous continuations can recover the initiating Agent. Creation, persistence load, and unpublished setup stay outside the child boundary; explicit Agent fields remain authoritative at service, worker, process, persistence, and wire boundaries. The [execution-context package](../agent-execution/README.md) owns propagation and detached-work rules.
The loop records turn, step, request, stream, and tool boundaries in the session log; live extension events coordinate policy around those durable facts. The [architecture turn flow](../../../docs/architecture.md#turn-flow) and generated [event catalog](../../../docs/cordis-catalog/events.md) are the authoritative sequence and signatures.
The driver owns one agent for its lifetime and runs inside `ctx.agentExecution.run({ agent }, ...)`, so process-local asynchronous continuations can recover the initiating Agent. Creation, persistence load, and unpublished setup stay outside the driver boundary; explicit Agent fields remain authoritative at service, worker, process, persistence, and wire boundaries. The [execution-context package](../agent-execution/README.md) owns propagation, teardown, and detached-work rules.
Every provider call that reaches a successful finish appends exactly one `assistant/message` completion anchor, including content-less calls and `max-tokens` finishes. A successful `agent/step-result` stores its transformed content; a rejected result records empty content before the original failure continues. The anchor retains exact chunk provenance (`[]` for a stream with no chunks) and usage when available, while empty content stays out of derived message history.