docs: align prose with runtime contracts

This commit is contained in:
Tianyi Cui
2026-07-14 16:21:41 +08:00
parent 26b4b036fc
commit f0fc20ca42
84 changed files with 162 additions and 146 deletions

View File

@@ -77,7 +77,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
},
{
key: 'approval',
summary: 'Approval request and policy service.',
summary: 'Approval service that applies session policy before answerers and logs every ask/outcome pair to the requesting session.',
methods: [
'async request(req: ApprovalRequest): Promise<ApprovalOutcome>',
],
@@ -265,7 +265,7 @@ export const EVENT_API: readonly EventApiEntry[] = [
name: 'agent/disposed',
mode: 'emit',
signature: '\'agent/disposed\'(this: Scoped<Agent>, agent: Agent): void',
summary: 'An agent left the registry.',
summary: 'An agent left the registry; AgentLoop emits this after driver quiescence but before session detachment and scoped-registration unwind.',
},
{
name: 'agent/error',
@@ -277,7 +277,7 @@ export const EVENT_API: readonly EventApiEntry[] = [
name: 'agent/pre-step',
mode: 'serial',
signature: '\'agent/pre-step\'(this: Scoped<Agent>, agent: Agent, turn: number, step: number, fullSystemPrompt: string, sessionPrefix: readonly Message[], signal: AbortSignal): Promise<void> | void',
summary: 'Awaited serial checkpoint after prompt assembly and before `step/start`.',
summary: 'Awaited serial checkpoint for session-surface mutation after prompt assembly and before `step/start`; appends land outside the pending step.',
},
{
name: 'agent/prompt-submit',
@@ -331,7 +331,7 @@ export const EVENT_API: readonly EventApiEntry[] = [
name: 'agent/turn-stop',
mode: 'serial',
signature: '\'agent/turn-stop\'(this: Scoped<Agent>, agent: Agent, turn: number): ContinuationStop | undefined',
summary: 'Monotonic terminal-stop checkpoint after continuation and steering are folded.',
summary: 'Monotonic terminal-stop checkpoint after continuation and steering are folded; a stop remains authoritative through turn close and flush: steering queued in that window is discarded, while ordinary sends survive.',
},
{
name: 'approval/request',

View File

@@ -934,11 +934,9 @@ describe('agent scope lifecycle', () => {
order.push(`session-still-stored=${ctx.sessions.get(SessionId('o1-s')) !== undefined}`)
})
// Open a turn so the drain has real work: the loop must finish it BEFORE
// the registry entry goes away (the agent/disposed contract: "its fiber
// and any in-flight turn have been torn down"). Wait for the turn to be
// OPEN in the log — a dispose landing in the pre-step window would drop
// the queued prompt without ever opening a turn.
// Open a turn so disposal must drain real work before registry removal.
// Waiting for turn/start avoids pre-step disposal dropping the queued prompt
// before a turn opens.
const turnOpen = new Promise<void>((resolve) => {
const off = ctx.on('session/event', (_s, event) => {
if (event.type === 'turn/start') { off(); resolve() }

View File

@@ -170,6 +170,7 @@ export class AgentRegistry extends Service {
/**
* Create and publish an owned agent and session through the active factory.
* Rejects if no factory is registered or creation, setup, or publication fails.
* @param options - agent id, session id/seed/metadata, and agent options.
* @returns the handle after setup, rollback-covered publication, and loop start complete.
*/

View File

@@ -138,8 +138,9 @@ declare module 'cordis' {
*/
'agent/created'(this: Scoped<Agent>, agent: Agent): void
/**
* An agent left the registry. AgentLoop emits this after driver quiescence;
* custom registry users own their driver-ordering contract.
* An agent left the registry; AgentLoop emits this after driver quiescence
* but before session detachment and scoped-registration unwind. Custom
* registry users own their driver-ordering contract.
* @param agent - the exact agent removed from the registry.
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
* @mode emit
@@ -182,11 +183,12 @@ declare module 'cordis' {
// ---- step/request extension seams (serial + waterfall) ----
/**
* Awaited serial checkpoint after prompt assembly and before `step/start`.
* Listeners may mutate the session surface outside the pending step; the loop
* derives history once afterward, so compaction records and replacements are
* included without rewriting an assembled request. The prompt and prefix are
* the exact pressure inputs for that request, and `signal` cancels listener work.
* Awaited serial checkpoint for session-surface mutation after prompt
* assembly and before `step/start`; appends land outside the pending step.
* The loop derives history once afterward, so compaction records and
* replacements are included without rewriting an assembled request. The
* prompt and prefix are the exact pressure inputs for that request, and
* `signal` cancels listener work.
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
* @param agent - the agent opening the step.
* @param turn - the open turn number.
@@ -258,7 +260,8 @@ declare module 'cordis' {
'agent/turn-continuation'(this: Scoped<Agent>, agent: Agent, turn: number, defaultDecision: ContinuationDecision, next: () => Promise<ContinuationDecision>): Promise<ContinuationDecision>
/**
* Monotonic terminal-stop checkpoint after continuation and steering are
* folded. A stop discards pending steering.
* folded; a stop remains authoritative through turn close and flush:
* steering queued in that window is discarded, while ordinary sends survive.
* @param agent - the agent whose composed continuation outcome may be stopped.
* @param turn - the turn at its terminal-stop checkpoint.
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.

View File

@@ -23,8 +23,8 @@ The JSONL durable session-persistence backend — a concrete `SessionPersistence
- **Lazy materialization.** `create(meta)` writes nothing; on the first `append`, the backend writes and `fsync`s a temporary file, publishes it without overwrite via a hard link, then `fsync`s the directory. A created-but-never-appended session leaves nothing on disk and is absent from `list`.
- **Append-only.** Committed events (at or below a flushed `turn/end`) are never rewritten. Subsequent appends are line appends at EOF + `fsync`.
- **Crash recovery — close, don't truncate.** `load` preserves valid events from an interrupted final turn, appends the synthetic tool, step, and turn closers required by the shared [persistence contract](../../../docs/rfc/implemented/architecture/2026-06-14-session-persistence.md), and removes only an incomplete final line.
- **Contiguous-seq.** `load` rejects a mid-log parse error or `seq` gap (unloadable); `append` rejects a batch whose first `seq` does not continue the stored log, and rejects non-JSON-serializable `event.data` naming the offending event type.
- **Crash recovery — preserve valid tail work.** `load` keeps the contiguous valid prefix of an interrupted final turn. It truncates from the first unparsable or sequence-gapped uncommitted record, then appends the synthetic tool, step, and turn closers required by the shared [persistence contract](../../../docs/rfc/implemented/architecture/2026-06-14-session-persistence.md); the same defect at or before the last committed `turn/end` rejects.
- **Contiguous-seq.** `append` rejects a batch whose first `seq` does not continue the stored log, and rejects non-JSON-serializable `event.data` naming the offending event type.
## Write path

View File

@@ -34,7 +34,7 @@ function permissionOption(currentValue: string): object {
type: 'select',
currentValue,
options: [
{ value: 'workspace-write', name: 'workspace-write', description: 'Write inside the workspace; wider retries require approval.' },
{ value: 'workspace-write', name: 'workspace-write', description: 'Write inside the workspace and permitted temporary directories; wider retries require approval.' },
{ value: 'danger-full-access', name: 'danger-full-access', description: 'Full file access without approval prompts.' },
],
}

View File

@@ -102,7 +102,7 @@ export class PermissionService extends Service {
})).default({
'workspace-write': {
sandbox: 'workspace-write', approval: 'ask',
name: 'workspace-write', description: 'Write inside the workspace; wider retries require approval.',
name: 'workspace-write', description: 'Write inside the workspace and permitted temporary directories; wider retries require approval.',
},
'danger-full-access': {
sandbox: 'danger-full-access', approval: 'never',

View File

@@ -222,9 +222,9 @@ export interface Config {
}
/**
* Approval request and policy service. It logs each ask/outcome pair, applies
* session policy before answerers, and exposes deterministic policy changes to
* the model through prompt and pre-step notices.
* Approval service that applies session policy before answerers and logs every
* ask/outcome pair to the requesting session. It exposes deterministic policy
* changes to the model through prompt and pre-step notices.
*/
export class ApprovalService extends Service {
static Config: z<Config> = z.object({