Merge remote-tracking branch 'origin/master' into worktree/pr343-retarget-latest-master
# Conflicts: # docs/cookbook/extension-cookbook.i18n.yaml # docs/cookbook/extension-cookbook.zh.md
This commit is contained in:
@@ -333,7 +333,7 @@ interface LlmCallConfig {
|
||||
*
|
||||
* The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
|
||||
* they only exist on {@link SurfaceEventType} variants (`user/message`,
|
||||
* `assistant/message`, `tool/result`, `context/message`, `steering/message`).
|
||||
* `assistant/message`, `tool/result`, `steering/message`).
|
||||
* Non-surface events (boundary markers, chunks, usage, errors) never carry
|
||||
* surface metadata — the compiler enforces this at `Session.append()`
|
||||
* call sites.
|
||||
@@ -361,7 +361,7 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
}[T]
|
||||
```
|
||||
|
||||
十四种事件变体(`turn/start`、`turn/end`、`step/start`、`step/end`、`user/message`、`prompt/blocked`、`context/message`、`assistant/chunk`、`assistant/message`、`tool/call`、`tool/result`、`steering/message`、`todo/write`、`request/header`)、`deriveMessages()` 投影规则、`TurnTrigger`/`TurnEndReason` 原因以及轮次封闭不变量都在 **[session.md](session.md)** 中。日志如何持久化——`SessionPersistence` seam、JSONL/SQLite 后端、`session/flush` 检查点、崩溃恢复与 `SessionHeader`——则在 **[persistence.md](persistence.md)** 中。
|
||||
十三种事件变体(`turn/start`、`turn/end`、`step/start`、`step/end`、`user/message`、`prompt/blocked`、`assistant/chunk`、`assistant/message`、`tool/call`、`tool/result`、`steering/message`、`todo/write`、`request/header`)、`deriveMessages()` 投影规则、`TurnTrigger`/`TurnEndReason` 原因以及轮次封闭不变量都在 **[session.md](session.md)** 中。日志如何持久化——`SessionPersistence` seam、JSONL/SQLite 后端、`session/flush` 检查点、崩溃恢复与 `SessionHeader`——则在 **[persistence.md](persistence.md)** 中。
|
||||
|
||||
<a id="the-agent-handle"></a>
|
||||
|
||||
@@ -373,8 +373,9 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Message options. An omitted source attests direct human input as `{ kind: 'user' }`
|
||||
* and may authorize policy consumers, so non-human producers must label their content.
|
||||
* Options for {@link Agent.followup}, {@link Agent.queue}, and {@link Agent.steer}.
|
||||
* An omitted source attests direct human input as `{ kind: 'user' }` and may
|
||||
* authorize policy consumers, so non-human producers must label their content.
|
||||
*/
|
||||
interface SendOptions {
|
||||
source?: MessageSource
|
||||
@@ -384,19 +385,90 @@ interface SendOptions {
|
||||
* records them directly at its next checkpoint.
|
||||
*/
|
||||
contexts?: HookContext[]
|
||||
/** Opaque JSON state retained on the durable message but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
```
|
||||
|
||||
`InjectOptions` 接受普通消息归属信息和对模型隐藏的持久 JSON 元数据。附加上下文只属于排队输入或 steering(中途引导)输入,因此合成注入不接受这类上下文:
|
||||
|
||||
```ts type-equiv
|
||||
/** Options specific to durable synthetic context injection. */
|
||||
interface InjectOptions extends Omit<SendOptions, 'contexts'> {
|
||||
/** Opaque JSON state retained in the session event but hidden from the model. */
|
||||
interface InjectOptions {
|
||||
/** Defaults to `{ kind: 'plugin', plugin: '' }`; non-human producers should identify themselves. */
|
||||
source?: MessageSource
|
||||
/** Opaque JSON state retained on the durable message but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
```
|
||||
|
||||
高级接收形式会显式给出所有默认值,并禁止为注入附加上下文:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Fully specified input for {@link Agent.send}. Unlike the intent-named
|
||||
* helpers, this form applies no defaults: callers provide content, source,
|
||||
* contexts, metadata (including explicit `undefined`), target, and wakeup.
|
||||
* The union excludes attached contexts from non-waking next-step injection.
|
||||
*/
|
||||
type ResolvedAgentInput = {
|
||||
content: ContentBlock[]
|
||||
source: MessageSource
|
||||
meta: JsonValue | undefined
|
||||
} & (
|
||||
| { target: 'next-turn'; wakeup: boolean; contexts: HookContext[] }
|
||||
| { target: 'next-step'; wakeup: true; contexts: HookContext[] }
|
||||
| { target: 'next-step'; wakeup: false; contexts: [] }
|
||||
)
|
||||
```
|
||||
|
||||
FIFO 投递方法返回不透明的 `AgentMessageId`,该 id 在同一条消息的各个 `agent/inbox/*` 事件中保持稳定。注入也返回 id,但会绕过这些事件:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Opaque id assigned to one accepted agent input. FIFO inputs carry the same id
|
||||
* on their `agent/inbox/*` events; injection bypasses those events.
|
||||
*/
|
||||
type AgentMessageId = Branded<'AgentMessageId'>
|
||||
```
|
||||
|
||||
`agent/inbox/*` 实时事件承载一条已接收的消息;注入绕过两个 FIFO,从不出现在这些事件中:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* One accepted FIFO message, carried by the `agent/inbox/*` live events. `id`
|
||||
* is the value returned by the accepting helper or {@link Agent.send},
|
||||
* stable across this message's enqueue, dequeue, and discard events. Source
|
||||
* defaults, when applicable, are already applied, so these are the exact values
|
||||
* the item was accepted with.
|
||||
* `steering` is true for an item drained between steps; otherwise it is claimed
|
||||
* at a turn boundary. `SendOptions.meta` is intentionally omitted: it is durable
|
||||
* model-hidden state that lands on the eventual `user/message`/
|
||||
* `steering/message`, not live-event routing data.
|
||||
*/
|
||||
interface AgentMessage {
|
||||
/** The id returned by the accepting helper or {@link Agent.send}. */
|
||||
id: AgentMessageId
|
||||
content: ContentBlock[]
|
||||
source: MessageSource
|
||||
contexts: HookContext[]
|
||||
/** Whether the item joined the steering FIFO rather than the queued FIFO. */
|
||||
steering: boolean
|
||||
/** Whether the item wakes the driver or requests another step. */
|
||||
wakeup: boolean
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Options for {@link Agent.cancel}. */
|
||||
interface CancelOptions {
|
||||
/**
|
||||
* Preserve queued and steering inbox items instead of discarding them. The
|
||||
* active turn is still aborted, but un-started and pending work survives for a
|
||||
* later turn and no `agent/inbox/discard` fires.
|
||||
*/
|
||||
keepInbox?: boolean
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Stable runtime cause accepted by {@link Agent.cancel}. */
|
||||
type AgentCancelCause =
|
||||
@@ -404,59 +476,97 @@ type AgentCancelCause =
|
||||
| { readonly kind: 'parent' }
|
||||
```
|
||||
|
||||
结构化 `Agent` 接口公开四个按意图命名的辅助方法,以及接受完全解析输入的方法。具体驱动器只需实现一次这套路由矩阵,每个辅助方法提供其固定路由与默认值。
|
||||
|
||||
```ts type-equiv
|
||||
/** Public agent handle; its concrete implementation is internal to `@deepseek-ai/dsh-agent-loop`. */
|
||||
interface Agent {
|
||||
/** The single identity shared with {@link session}. */
|
||||
readonly id: SessionId
|
||||
/** The provider route and model this agent's requests use. */
|
||||
readonly options: AgentOptions
|
||||
/** The live session this agent drives; its log is the durable source of truth. */
|
||||
readonly session: Session
|
||||
/** The current lifecycle state, mirrored on every `agent/status` transition. */
|
||||
readonly status: AgentStatus
|
||||
/** Agent-scoped context; its contributions are agent-local, unwind on disposal, and reject registration afterward. */
|
||||
readonly ctx: Context
|
||||
|
||||
/**
|
||||
* Queue one detached, frozen lossless-JSON item. If claimed, it is the sole
|
||||
* ordinary message in its FIFO-ordered turn; the next claimed item waits for
|
||||
* that turn's checkpoint.
|
||||
* Attached contexts share the same snapshot and ownership boundary. Invalid
|
||||
* input throws synchronously before notification or enqueue.
|
||||
* Queue an ordinary message as its own FIFO-ordered turn and wake the driver.
|
||||
* Content, resolved source, and attached contexts are detached, validated,
|
||||
* and frozen together; invalid input throws synchronously before notification
|
||||
* or enqueue.
|
||||
* @param content - the prompt content blocks.
|
||||
* @param options - source, attached contexts, and durable model-hidden meta.
|
||||
* @returns the accepted message's {@link AgentMessageId}, stable across its `agent/inbox/*` events.
|
||||
*/
|
||||
send(content: ContentBlock[], options?: SendOptions): void
|
||||
followup(content: ContentBlock[], options?: SendOptions): AgentMessageId
|
||||
|
||||
/**
|
||||
* Submit steering while the agent is `running`. An open turn records it at
|
||||
* the next steering checkpoint before a request or continuation decision;
|
||||
* policy may stop before another step. After turn close and its checkpoint,
|
||||
* any remainder is queued for a later turn; terminal `agent/turn-stop`,
|
||||
* cancellation, or disposal may discard it. Uses the same synchronous
|
||||
* snapshot-and-validation boundary as {@link send}; when idle, delegates to it.
|
||||
* Queue an ordinary message without waking an idle driver. The item retains
|
||||
* FIFO order and is claimed only after another input wakes the driver. A lone
|
||||
* queued item leaves `whenIdle()` resolved.
|
||||
* @param content - the prompt content blocks.
|
||||
* @param options - source, attached contexts, and durable model-hidden meta.
|
||||
* @returns the accepted message's {@link AgentMessageId}, stable across its `agent/inbox/*` events.
|
||||
*/
|
||||
steer(content: ContentBlock[], options?: SendOptions): void
|
||||
queue(content: ContentBlock[], options?: SendOptions): AgentMessageId
|
||||
|
||||
/**
|
||||
* Submit steering into the running turn and request another step. An open turn
|
||||
* records it at the next steering checkpoint before a request or continuation
|
||||
* decision; policy may stop before another step. After turn close and its
|
||||
* checkpoint, any remainder is queued for a later turn; terminal
|
||||
* `agent/turn-stop`, cancellation, or disposal may discard it. Idle steering
|
||||
* becomes a waking ordinary turn.
|
||||
* @param content - the steering content blocks.
|
||||
* @param options - source, attached contexts, and durable model-hidden meta.
|
||||
* @returns the accepted message's {@link AgentMessageId}, stable across its `agent/inbox/*` events.
|
||||
*/
|
||||
steer(content: ContentBlock[], options?: SendOptions): AgentMessageId
|
||||
|
||||
/**
|
||||
* Append detached model-facing context without running the model. An open-turn
|
||||
* injection joins at the current log position unless the current tool batch is
|
||||
* executing; then it waits FIFO until that batch settles and drains before turn
|
||||
* close even when interrupted. Idle injection uses a one-shot turn and durability
|
||||
* checkpoint. Disposal awaits idle checkpoints; flush failures report through `agent/error`.
|
||||
* executing; then it waits FIFO until that batch settles and drains before
|
||||
* turn close even when interrupted. Idle injection uses a one-shot turn and
|
||||
* durability checkpoint. Disposal awaits idle checkpoints; flush failures
|
||||
* report through `agent/error`. An omitted source defaults to
|
||||
* `{ kind: 'plugin', plugin: '' }`.
|
||||
* @param content - the injected context content blocks.
|
||||
* @param options - source and durable model-hidden meta.
|
||||
* @returns the accepted injection's {@link AgentMessageId}; injection emits no `agent/inbox/*` events.
|
||||
*/
|
||||
inject(content: ContentBlock[], options?: InjectOptions): void
|
||||
inject(content: ContentBlock[], options?: InjectOptions): AgentMessageId
|
||||
|
||||
/**
|
||||
* Clear all queued and steering work, including items waiting to start, and
|
||||
* abort the active turn. An effective call first emits
|
||||
* `agent/cancel-requested` with the resolved typed cause. The first cause wins
|
||||
* for the active turn, and `whenIdle()` resolves after cancellation reaches
|
||||
* quiescence. Omission means `{ kind: 'user' }`. Idle cancellation is a no-op
|
||||
* and does not arm later work. The active turn snapshots and freezes the cause.
|
||||
* @param cause - the stable caller intent carried by the current turn signal.
|
||||
* Accept one fully specified input through the same snapshot and routing path
|
||||
* as the four intent-named helpers. `next-turn` targets the ordinary FIFO;
|
||||
* `next-step`/wakeup targets steering (falling back to an ordinary waking turn
|
||||
* while idle); and `next-step` without wakeup injects durable context without
|
||||
* running the model. Every field is mandatory and no source or routing default
|
||||
* is applied. Invalid input throws synchronously before notification, enqueue,
|
||||
* or append.
|
||||
* @param input - the resolved content, attribution, context, metadata, and routing facts.
|
||||
* @returns the accepted input's {@link AgentMessageId}, carried by FIFO lifecycle events when applicable.
|
||||
*/
|
||||
cancel(cause?: AgentCancelCause): void
|
||||
send(input: ResolvedAgentInput): AgentMessageId
|
||||
|
||||
/**
|
||||
* Clear queued and steering work — unless `keepInbox` — and abort the active
|
||||
* turn. An effective call first emits `agent/cancel-requested` with the
|
||||
* resolved typed cause. The first cause wins for the active turn, and
|
||||
* `whenIdle()` resolves after cancellation reaches quiescence. Omitted cause
|
||||
* means `{ kind: 'user' }`. Idle cancellation is a no-op and does not arm
|
||||
* later work. The active turn snapshots and freezes the cause.
|
||||
* @param cause - the stable caller intent carried by the current turn signal.
|
||||
* @param options - cancellation options; `keepInbox` preserves pending work.
|
||||
*/
|
||||
cancel(cause?: AgentCancelCause, options?: CancelOptions): void
|
||||
|
||||
/** Resolve at idle quiescence; disposal waits for driver exit rather than only the status transition. */
|
||||
whenIdle(): Promise<void>
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
@@ -472,7 +582,7 @@ cause 是由 TypeScript 强制约束的同进程输入。活跃的 `TurnCancella
|
||||
|
||||
## 拦截决策
|
||||
|
||||
每个 `agent/*` 拦截 waterfall 都返回一个小型、特定于 seam 的类型化联合——统一的 Decision 惯用形状([tools.md](tools.md) 中工具 seam 的 `PreToolDecision`/`PostToolDecision` 也采用相同形状)。CC/Codex 钩子桥接层把其 `permissionDecision`/`decision`/`continue`/`additionalContext` 字段映射到这些联合上;原生插件则直接返回它们。提示词决策与工具后决策共享一种面向模型的上下文形状 `HookContext`,它必须携带 `source`(缺少 source 会默认成 `{kind:'user'}`,从而把插件上下文错标为用户提示词)。其中的 `content` 作为 user-role 输入逐字到达模型,而 JSON `meta` 持久保存插件状态但不向模型暴露。未指定放置方式或指定为 `separate` 时,上下文会成为 `context/message`;`prompt-prefix` 放置方式可用于提示词和 steering 收件箱附件,会在同一条消息中把上下文置于最终生效的请求之前。两种决策都携带 `additionalContexts[]`,使每一项保留各自的 provenance、元数据与放置方式。Continuation reason 则是 steering 消息,并有意使用更窄的 content/source 形状。
|
||||
每个 `agent/*` 拦截 waterfall 都返回一个小型、特定于 seam 的类型化联合——统一的 Decision 惯用形状([tools.md](tools.md) 中工具 seam 的 `PreToolDecision`/`PostToolDecision` 也采用相同形状)。CC/Codex 钩子桥接层把其 `permissionDecision`/`decision`/`continue`/`additionalContext` 字段映射到这些联合上;原生插件则直接返回它们。提示词决策与工具后决策共享一种面向模型的上下文形状 `HookContext`,它必须携带 `source`(缺少 source 会默认成 `{kind:'user'}`,从而把插件上下文错标为用户提示词)。其中的 `content` 作为 user-role 输入逐字到达模型,而 JSON `meta` 持久保存插件状态但不向模型暴露。未指定放置方式或指定为 `separate` 时,上下文会成为一条注入的 `user/message`(来源类别为插件或 goal);`prompt-prefix` 放置方式可用于提示词和 steering 收件箱附件,会在同一条消息中把上下文置于最终生效的请求之前。两种决策都携带 `additionalContexts[]`,使每一项保留各自的 provenance、元数据与放置方式。Continuation reason 则是 steering 消息,并有意使用更窄的 content/source 形状。
|
||||
|
||||
源码:[`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
@@ -482,8 +592,8 @@ interface HookContext {
|
||||
content: ContentBlock[]
|
||||
source: MessageSource
|
||||
/**
|
||||
* Model placement. Absent or `separate` records an independent
|
||||
* `context/message`; `prompt-prefix` prepends this context and a stable
|
||||
* Model placement. Absent or `separate` records an independent injected
|
||||
* `user/message`; `prompt-prefix` prepends this context and a stable
|
||||
* request delimiter to the same user-role message as its attached prompt.
|
||||
*/
|
||||
placement?: 'separate' | 'prompt-prefix'
|
||||
|
||||
Reference in New Issue
Block a user