Merge remote-tracking branch 'origin/master' into worktree/web-multimodal-image-input
# Conflicts: # docs/architecture.i18n.yaml # docs/architecture.md # docs/architecture.zh.md # docs/config-catalog.md # docs/core-data-structures/core.i18n.yaml # docs/core-data-structures/llm-streaming.i18n.yaml # docs/module-graph.md # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl # packages/README.i18n.yaml # packages/client/connection/src/client/fixture.ts # packages/client/connection/src/index.ts # packages/client/runtime/README.i18n.yaml # packages/client/runtime/README.md # packages/client/runtime/README.zh.md # packages/client/runtime/src/client/sessions/conversation.ts # packages/client/ui-conversation/README.i18n.yaml # packages/client/ui-conversation/src/client/apply.ts # packages/client/ui-conversation/src/client/chat/ChatView.tsx # packages/client/ui-conversation/src/client/chat/MessageItem.tsx # packages/client/ui-conversation/src/client/contract/slots.ts # packages/client/ui-trajectory/tests/views.spec.tsx # packages/compact/compact-basic/README.i18n.yaml # packages/cordis/tool-cordis/src/api-catalog.ts # packages/host/apiproxy/src/api-proxy.ts # packages/host/apiproxy/src/api/index.ts # packages/host/apiproxy/src/api/sessions.ts # packages/host/apiproxy/src/index.ts # packages/host/apiproxy/tests/fetch-carrier.spec.ts # packages/llm/llm-deepseek/src/adapter.ts # packages/llm/llm-deepseek/tests/adapter.spec.ts # packages/llm/llm-deepseek/tests/serialize.spec.ts # packages/llm/llm-pi-ai/README.i18n.yaml # packages/llm/llm-pi-ai/src/adapter.ts # packages/llm/llm-pi-ai/src/index.ts # packages/llm/llm-pi-ai/tests/adapter.spec.ts # packages/llm/llm/src/types.ts # packages/ui/tui/README.i18n.yaml # packages/ui/tui/src/index.ts # packages/ui/tui/tests/tui.spec.ts
This commit is contained in:
1499 files changed
+48621
-21956
No files matched your search
@@ -32,6 +32,7 @@ harness 是一个微内核:一个极小的核心加上众多插件。大多数
|
||||
| [approval.md](approval.md) | 一次性用户审批 seam:`ApprovalRequest`、`ApprovalOutcome`、逐会话策略、审计与 answerer 契约 |
|
||||
| [attachment.md](attachment.md) | 持久图像标识与元数据、校验输入、经校验读取,以及 `AttachmentStore` seam |
|
||||
| [bash.md](bash.md) | bash 执行器 seam:`BashExecRequest`/`Spec`、`BashRunResult`、后台 `BashProcess` 句柄 |
|
||||
| [subprocess.md](subprocess.md) | 子进程 seam:完全显式的 `SubprocessSpawnSpec`、基于偏移的输出读取器、不含分类的 `SubprocessOutcome`,以及受管 `DSH_*` 环境词汇 |
|
||||
| [pty.md](pty.md) | 持久化终端 ID、后端/会话契约、发送就绪状态、有界读取与 owner 可见快照 |
|
||||
| [sandbox.md](sandbox.md) | 每会话策略解析与进程约束 seam:文件效果模式、执行/提供方策略、`ConfinedArgv`、强制执行与故障关闭错误 |
|
||||
| [code-runtime.md](code-runtime.md) | 代码执行 seam:`CodeRunRequest`/`Result`、绑定命名空间、捕获日志、`CodeRunFailure` 分类体系 |
|
||||
@@ -211,7 +212,7 @@ interface LlmModelInfo {
|
||||
}
|
||||
```
|
||||
|
||||
对正确性敏感的模型容量与参考目录分开查询,并归服务该确切路由的适配器所有。
|
||||
对正确性敏感的元数据与参考目录分开解析,并归服务该确切路由的适配器所有。上下文容量和推理选项共用同一个确切模型结果,消费方因而无需重复执行权威模型解析。
|
||||
|
||||
```ts type-equiv
|
||||
/** Provider-owned context capacity for one exact provider/model route. */
|
||||
@@ -221,17 +222,60 @@ interface LlmModelContext {
|
||||
}
|
||||
```
|
||||
|
||||
推理强度是另一项针对确切路由的能力。核心为标识符添加品牌类型,但不枚举其值;有序集合、展示名称和可选的部署默认值均由各适配器持有。
|
||||
|
||||
```ts type-equiv
|
||||
/** Adapter-owned identifier for one model's selectable reasoning effort. */
|
||||
type ReasoningEffortId = Branded<'ReasoningEffortId'>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Display metadata for one adapter-owned reasoning effort. */
|
||||
interface LlmReasoningEffortInfo {
|
||||
/** Opaque stable value accepted by {@link GenerateOptions.reasoningEffort}. */
|
||||
id: ReasoningEffortId
|
||||
/** Human-readable effort name for selectors and diagnostics. */
|
||||
name: string
|
||||
/** Optional user-facing distinction from otherwise similar efforts. */
|
||||
description?: string
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Selectable reasoning efforts for one exact provider/model route. */
|
||||
interface LlmModelReasoningInfo {
|
||||
/** Supported efforts in adapter-preferred display order. */
|
||||
efforts: readonly LlmReasoningEffortInfo[]
|
||||
/**
|
||||
* Adapter-configured default materialized into requests when callers omit
|
||||
* an effort. Absence preserves the provider's own default.
|
||||
*/
|
||||
defaultEffort?: ReasoningEffortId
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Exact-route model metadata resolved by its owning adapter. */
|
||||
interface LlmResolvedModelInfo extends LlmModelInfo {
|
||||
/** Provider-owned context capacity when known. */
|
||||
context?: LlmModelContext
|
||||
/** Adapter-owned selectable reasoning levels when exposed. */
|
||||
reasoning?: LlmModelReasoningInfo
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** A single model request, fully assembled. */
|
||||
interface GenerateOptions {
|
||||
/** Registered provider route selecting the adapter instance. */
|
||||
provider: string
|
||||
model: string
|
||||
/** Adapter-owned reasoning effort selected for this exact model. */
|
||||
reasoningEffort?: ReasoningEffortId
|
||||
/**
|
||||
* Ordered conversation messages, exactly as the provider sees them (after
|
||||
* the `system` slot). A loop-built request assembles them as
|
||||
* `EpochHeader.messagePrefix` + the derived history (dsh-agent-loop); a
|
||||
* hand-built one-shot passes any list.
|
||||
* the derived history (dsh-agent-loop); a hand-built one-shot passes any list.
|
||||
*/
|
||||
messages: Message[]
|
||||
/** System prompt text (adapters map to the provider's system slot). */
|
||||
@@ -301,23 +345,25 @@ interface ToolSchema {
|
||||
|
||||
### 请求信封:`LlmCallConfig` 与记录的 header
|
||||
|
||||
循环从已记录状态构建每个请求。`EpochHeader` 通过完整的 `request/header` 快照记录调用配置、渲染后的提示词、权威返回工具顺序(由 `toolOrder` 配置;未配置时按字典序)以及会话前缀。结合派生历史,请求便可由会话日志重建。见 [session.md](session.md#the-request-header-event-requestheader) 与[可重建性 Agent Note(agent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md)。
|
||||
循环从已记录状态构建每个请求。`EpochHeader` 通过完整的 `request/header` 快照记录调用配置、渲染后的提示词以及权威返回工具顺序(由 `toolOrder` 配置;未配置时按字典序)。结合派生历史,请求便可由会话日志重建。见 [session.md](session.md#the-request-header-event-requestheader) 与[可重建性 Agent Note(agent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md)。
|
||||
|
||||
`agent/request` 接收冻结的调用配置种子,并可返回替代值以切换提供方、模型或采样参数。`agent/session-prefix` 为每个循环实例组合一次仅用于请求的 prefix 消息,header 记录实际使用的确切结果。到达 `llm/stream` 的请求会被深度冻结,因此变更会抛异常;请求还携带进程本地循环标识,使观察者不会把单独记录的冻结辅助调用误认成对话请求。
|
||||
`agent/request` 接收冻结的调用配置种子,并可返回替代值以切换提供方、模型、推理强度或采样参数。waterfall 结束后,循环会在轮次信号控制下完成确切模型的能力准备,拒绝显式指定但不受支持的推理强度 ID(不自动调整),填入适配器配置的默认值,并记录最终生效值。准备完成的调用直至分派完成始终持有同一项适配器注册。到达 `llm/stream` 的请求会被深度冻结,因此变更会抛异常;请求还携带进程本地循环标识,使观察者不会把单独记录的冻结辅助调用误认成对话请求。
|
||||
|
||||
在协议格式上,循环构建的请求按此顺序读取:`system` 槽位(渲染后的提示词组装)→ `messagePrefix`(冻结的会话前缀)→ 派生历史——边界快照,其尾部在轮次首步是最新的 `user/message`,在后续步骤是上一步的工具结果。前缀从不进入派生历史;它的持久记录是 header 事件,开发不变式针对每个循环构建的请求精确重算此等式。
|
||||
在协议格式上,循环构建的请求先读取 `system` 槽位(渲染后的提示词组装),再读取派生历史——边界快照,其尾部在轮次首步是最新的 `user/message`,在后续步骤是上一步的工具结果。开发不变式针对每个循环构建的请求精确重算此等式。
|
||||
|
||||
FIXME(call-config-shape):重新审视此类型的精确定义——出于缓存目的,哪些字段确实属于 epoch 层级(`model` 肯定属于;采样标量目前出于谨慎放在这里),以及适配器需要时,提供方特有的额外项(推理选项、额外 body 参数)应归属何处。
|
||||
FIXME(call-config-shape):重新审视其余哪些字段出于缓存目的确实属于 epoch 层级(`model` 和模型持有的推理强度已明确属于;采样标量目前出于谨慎保留在此)。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Provider + model + sampling scalars of one conversation's requests. Every field maps
|
||||
* 1:1 onto the same-named `GenerateOptions` field; the loop builds requests
|
||||
* from the logged header rather than accepting these per call.
|
||||
* Provider, model, reasoning effort, and sampling scalars of one conversation's
|
||||
* requests. Every field maps 1:1 onto the same-named `GenerateOptions` field;
|
||||
* the loop builds requests from the logged header rather than accepting these
|
||||
* per call.
|
||||
*/
|
||||
interface LlmCallConfig {
|
||||
provider: string
|
||||
model: string
|
||||
reasoningEffort?: ReasoningEffortId
|
||||
temperature?: number
|
||||
maxTokens?: number
|
||||
stop?: string[]
|
||||
@@ -367,7 +413,7 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
}[T]
|
||||
```
|
||||
|
||||
十三种事件变体(`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)** 中。
|
||||
十二种事件变体(`turn/start`、`turn/end`、`step/start`、`step/end`、`user/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)** 中。
|
||||
|
||||
<a id="the-agent-handle"></a>
|
||||
|
||||
@@ -379,59 +425,51 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* 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.
|
||||
* Which inbox queue a {@link Agent.send} item joins:
|
||||
* - `next-turn` — the item becomes its own turn, claimed at a turn boundary.
|
||||
* - `next-step` — during prompt admission or an open turn, the item stages for
|
||||
* the next safe step boundary; otherwise it is promoted per its `wakeup`
|
||||
* flag.
|
||||
*/
|
||||
type SendTarget = 'next-turn' | 'next-step'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Resolved inbox placement reported when an accepted message is enqueued. */
|
||||
type InboxPlacement = 'queued' | 'steering'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Options for the unified {@link Agent.send} primitive over the
|
||||
* (`target` × `wakeup`) matrix. Named presets: {@link Agent.followup}
|
||||
* (`next-turn`/wakeup), {@link Agent.steer} (`next-step`/wakeup), and
|
||||
* {@link Agent.inject} (`next-step`/no-wakeup).
|
||||
*
|
||||
* The object is complete so routing policy is explicit.
|
||||
*/
|
||||
interface SendOptions {
|
||||
source?: MessageSource
|
||||
/** Queue the item joins. */
|
||||
target: SendTarget
|
||||
/**
|
||||
* Model-facing contexts captured with this inbox item. A queued prompt exposes
|
||||
* them through the default `agent/prompt-submit` allow decision, while steering
|
||||
* records them directly at its next checkpoint.
|
||||
* Whether this item makes the model run: wake a parked driver (`next-turn`)
|
||||
* or force a continuation step (`next-step` while running). A `false`
|
||||
* `next-turn` item queues without waking; a `false`
|
||||
* `next-step` item attaches durable context without forcing another step
|
||||
* (the injection preset).
|
||||
*/
|
||||
contexts?: HookContext[]
|
||||
/** Opaque JSON state retained on the durable message but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
wakeup: boolean
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Options specific to durable synthetic context injection. */
|
||||
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
|
||||
}
|
||||
```
|
||||
固定预设的别名方法自带 `target` 与 `wakeup`;其 `UserMessageData` 输入同时携带内容与 provenance。
|
||||
|
||||
高级接收形式会显式给出所有默认值,并禁止为注入附加上下文:
|
||||
`send` 返回被接收消息的不透明 `AgentMessageId`,该 id 在这条消息的各个 `agent/inbox/*` 事件中保持稳定:
|
||||
|
||||
```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.
|
||||
* Opaque id assigned to one accepted {@link Agent.send} message; returned by
|
||||
* `send` and carried on its `agent/inbox/*` events for correlation.
|
||||
*/
|
||||
type AgentMessageId = Branded<'AgentMessageId'>
|
||||
```
|
||||
@@ -440,26 +478,14 @@ type AgentMessageId = Branded<'AgentMessageId'>
|
||||
|
||||
```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.
|
||||
* One accepted {@link Agent.send} message, carried by the `agent/inbox/*` live
|
||||
* events. `id` is the value `send` returned to the caller, stable across this
|
||||
* message's enqueue, dequeue, and discard events. The agent snapshots and
|
||||
* freezes the accepted content and source before enqueue observers receive it.
|
||||
*/
|
||||
interface AgentMessage {
|
||||
/** The id returned by the accepting helper or {@link Agent.send}. */
|
||||
interface AgentMessage extends UserMessageData {
|
||||
/** The id `send` returned for this message. */
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
@@ -482,10 +508,10 @@ type AgentCancelCause =
|
||||
| { readonly kind: 'parent' }
|
||||
```
|
||||
|
||||
结构化 `Agent` 接口公开四个按意图命名的辅助方法,以及接受完全解析输入的方法。具体驱动器只需实现一次这套路由矩阵,每个辅助方法提供其固定路由与默认值。
|
||||
`Agent` 是覆盖公开活跃 agent 契约的接口。具体驱动器拥有 `followup`/`steer`/`inject` 别名方法,并将它们经由 `send` 的(`target` × `wakeup`)矩阵路由。
|
||||
|
||||
```ts type-equiv
|
||||
/** Public agent handle; its concrete implementation is internal to `@deepseek-ai/dsh-agent-loop`. */
|
||||
/** Public live-agent handle with aliases over the unified delivery primitive. */
|
||||
interface Agent {
|
||||
/** The single identity shared with {@link session}. */
|
||||
readonly id: SessionId
|
||||
@@ -495,90 +521,91 @@ interface Agent {
|
||||
readonly session: Session
|
||||
/** The current lifecycle state, mirrored on every `agent/status` transition. */
|
||||
readonly status: AgentStatus
|
||||
/**
|
||||
* Whether a `next-step` send currently stages for prompt admission or the
|
||||
* open turn. Unlike {@link status}, this excludes admission exit and turn
|
||||
* settlement, when a waking `next-step` send becomes a queued follow-up.
|
||||
*/
|
||||
readonly acceptsNextStep: boolean
|
||||
/** Agent-scoped context; its contributions are agent-local, unwind on disposal, and reject registration afterward. */
|
||||
readonly ctx: Context
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The unified delivery primitive over the (`target` × `wakeup`) matrix.
|
||||
* It routes the caller's typed content and source as follows:
|
||||
*
|
||||
* - `next-turn` queues an item that becomes the sole ordinary message of its
|
||||
* own FIFO-ordered turn; `wakeup:true` wakes a
|
||||
* parked driver, while `wakeup:false` queues without waking.
|
||||
* - `next-step` with `wakeup:true` stages steering during prompt admission
|
||||
* or an open turn; outside that window it falls back to a woken
|
||||
* `next-turn`.
|
||||
* - `next-step` with `wakeup:false` injects durable model-facing context
|
||||
* without running the model: admission or an open turn stages it for the
|
||||
* next safe log position, while an injection outside that window appends
|
||||
* immediately without opening a turn. If admission closes without a turn,
|
||||
* a context-only boundary appends immediately; context staged beside
|
||||
* steering remains pending with it.
|
||||
* The agent snapshots and freezes `input` before publishing or queueing it.
|
||||
* @param input - model-facing content and its producer provenance.
|
||||
* @param options - target queue and wakeup decision.
|
||||
* @returns the accepted message's {@link AgentMessageId}, stable across its `agent/inbox/*` events.
|
||||
*/
|
||||
followup(content: ContentBlock[], options?: SendOptions): AgentMessageId
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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`. 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): AgentMessageId
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
send(input: ResolvedAgentInput): AgentMessageId
|
||||
send(input: UserMessageData, options: SendOptions): 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.
|
||||
* `whenIdle()` resolves after cancellation reaches quiescence. Idle
|
||||
* cancellation is a no-op and does not arm later work.
|
||||
* @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
|
||||
cancel(cause: AgentCancelCause, options?: CancelOptions): void
|
||||
|
||||
/** Resolve at idle quiescence; disposal waits for driver exit rather than only the status transition. */
|
||||
whenIdle(): Promise<void>
|
||||
|
||||
/**
|
||||
* Queue an ordinary follow-up turn and wake the driver — the
|
||||
* `next-turn`/wakeup preset of {@link send}. The item becomes the sole
|
||||
* ordinary message of its own turn.
|
||||
* @param input - prompt content and its producer provenance.
|
||||
* @returns the accepted message's {@link AgentMessageId}.
|
||||
*/
|
||||
followup(input: UserMessageData): AgentMessageId
|
||||
|
||||
/**
|
||||
* Submit steering during prompt admission or an open turn — the
|
||||
* `next-step`/wakeup preset of {@link send}. It stages for the next steering
|
||||
* checkpoint before a request or stop decision. If the activity fails before
|
||||
* that boundary, the remainder stays staged without waking the agent; retry
|
||||
* or a later prompt takes it. Outside that window steering falls back to a
|
||||
* woken follow-up turn, while cancellation or disposal may discard pending
|
||||
* steering.
|
||||
* @param input - steering content and its producer provenance.
|
||||
* @returns the accepted message's {@link AgentMessageId}.
|
||||
*/
|
||||
steer(input: UserMessageData): AgentMessageId
|
||||
|
||||
/**
|
||||
* Append model-facing context without running the model — the
|
||||
* `next-step`/no-wakeup preset of {@link send}. Admission or an open turn
|
||||
* stages it at the next safe log position; outside that window it appends
|
||||
* immediately without opening a turn. If admission closes without a turn,
|
||||
* a context-only boundary appends immediately; context staged beside
|
||||
* steering remains pending with it.
|
||||
* @param input - injected context and its producer provenance.
|
||||
* @returns the accepted message's {@link AgentMessageId}.
|
||||
*/
|
||||
inject(input: UserMessageData): AgentMessageId
|
||||
}
|
||||
```
|
||||
|
||||
`AgentStatus` 为 `'idle' | 'running' | 'disposed'`,`SessionId` 是品牌类型。`running` 描述整个驱动器的排空区间,可能跨越轮次关闭、其持久化检查点以及连续的排队轮次;它不能证明某个轮次仍然打开。`AgentOptions` 可合并扩展:core 声明 `provider?` 与 `model?`(在 `agent/request` 后,分发要求两者都存在)。Persona 归 `dsh-system-prompt` 所有:agent 作用域的 `deployment:persona` 可以遮蔽全局默认值。
|
||||
`AgentStatus` 为 `'idle' | 'running'`,`SessionId` 是品牌类型。dispose(资源释放)会把 agent 从注册表移除并发出 `agent/disposed`;它不是一个终态 status 值。`running` 描述整个驱动器的排空区间,可能跨越连续的排队轮次;它不能证明某个轮次仍然打开。对于需要在把输入作为 steering 加入当前提示词准入/轮次,还是提交为一个新的待准入提示词之间做选择的调用方,`acceptsNextStep` 才是更窄且准确的路由判断条件。`AgentOptions` 可合并扩展:core 声明 `provider?` 与 `model?`(在 `agent/request` 后,分发要求两者都存在)。Persona 归 `dsh-system-prompt` 所有:agent 作用域的 `deployment:persona` 可以遮蔽全局默认值。
|
||||
|
||||
cause 是由 TypeScript 强制约束的同进程输入。活跃的 `TurnCancellation` 持有者会把其判别字段复制到仅运行时的 `AbortSignal.reason`,并在发布 `turn/end` 前退役;冻结后的 `AbortSignal.reason` 仍可读取。`agentInterruptReasonOf(signal)` 无需查询环境中的 initiator 状态,即可识别 `user`、`parent` 与仅用于生命周期的 `disposed`。持久 `turn/end` 保留粗粒度 `{ kind: 'aborted' }` 结果;若需记录请求 provenance,应使用单独的持久事件,而不是让终态结果承担额外含义。
|
||||
cause 是由 TypeScript 强制约束的同进程输入。活跃的 `TurnCancellation` 持有者会把其判别字段复制到仅运行时的 `AbortSignal.reason`,并在发布 `turn/end` 前退役;冻结后的 `AbortSignal.reason` 仍可读取。只有 loop 会在结算时从自己机器私有的 signal 上读回 cause(`user`、`parent` 或仅用于生命周期的 `disposed`)——不存在公开的读取器,signal 也不授予协作监听器任何分类权限。持久 `turn/end` 保留粗粒度 `{ kind: 'aborted' }` 结果;若需记录请求 provenance,应使用单独的持久事件,而不是让终态结果承担额外含义。
|
||||
|
||||
[事件分类](../architecture.md#event)拥有 `agent/*` 生命周期、检查点与 waterfall(瀑布式事件)契约。轮次和步骤边界是持久会话事件,而不是 agent emit。
|
||||
|
||||
@@ -588,78 +615,37 @@ 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` 时,上下文会成为一条注入的 `user/message`(来源类别为插件或 goal);`prompt-prefix` 放置方式可用于提示词和 steering 收件箱附件,会在同一条消息中把上下文置于最终生效的请求之前。两种决策都携带 `additionalContexts[]`,使每一项保留各自的 provenance、元数据与放置方式。Continuation reason 则是 steering 消息,并有意使用更窄的 content/source 形状。
|
||||
提示词决策与工具后决策使用与持久 user-role 输入相同的 `UserMessageData` content/source 形状。每个 `additionalContexts` 条目都会成为一条独立的 `user/message`,保留各自的 provenance。钩子桥接层把其原生决策字段映射到这些类型化结果上。
|
||||
|
||||
源码:[`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/** Model-facing context injected by a listener or atomically attached to one inbox message. */
|
||||
interface HookContext {
|
||||
content: ContentBlock[]
|
||||
source: MessageSource
|
||||
/**
|
||||
* 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'
|
||||
/** Opaque JSON state retained in the session event but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
```
|
||||
|
||||
`agent/prompt-submit` 返回 `PromptDecision`(允许该轮次已领取的排队消息——可选地改写其 `content` 或附加 `additionalContexts`——或者记录 `prompt/blocked` 并以 `rejected` 结束这个零步骤轮次):
|
||||
`agent/prompt-submit` 在轮次打开前返回 `PromptDecision`。allow 可以改写已领取的提示词或附加 `additionalContexts`;block 拒绝准入且不产生任何轮次事件:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Prompt interception result. `allow.content` replaces the prompt. Each
|
||||
* `additionalContexts` entry follows its declared placement: separate context
|
||||
* message by default, or a prefix inside the prompt's user-role message.
|
||||
* `block` records a durable `prompt/blocked` and ends the claimed prompt's
|
||||
* zero-step turn as rejected. An `allow` returned by a listener is
|
||||
* authoritative: a listener wrapping `next()` preserves downstream `content`
|
||||
* and `additionalContexts` unless it intentionally replaces them.
|
||||
* Prompt interception result. `allow.content` replaces the prompt, while
|
||||
* `additionalContexts` appends model-facing context before the turn starts.
|
||||
* An `allow` returned by a listener is authoritative: a listener wrapping
|
||||
* `next()` preserves both fields unless it intentionally replaces them.
|
||||
*/
|
||||
type PromptDecision =
|
||||
| { kind: 'allow'; content?: ContentBlock[]; additionalContexts?: HookContext[] }
|
||||
| { kind: 'allow'; content?: ContentBlock[]; additionalContexts?: UserMessageData[] }
|
||||
| { kind: 'block'; reason: string }
|
||||
```
|
||||
|
||||
`agent/turn-continuation` 返回 `ContinuationDecision`(步骤有工具调用或注入了 steering 时,循环默认为 `continue`,否则为 `stop`;`continue` 的 `reason` 会记录为同一轮次中下一个步骤的 steering,因此不携带上下文元数据——即类型化 `/goal` 模式):
|
||||
`agent/request-error` 在失败的模型步骤关闭之后、其轮次关闭之前运行。listener 可以在失败轮次的 signal 仍然存活时修复持久状态或 await 策略工作。处理该错误的 listener 返回 `{ kind: 'retry' }` 且不调用 `next()`;默认的 `undefined` 会让失败保持终态。
|
||||
|
||||
```ts type-equiv
|
||||
/** Turn continuation override; a continue reason is recorded as next-step steering in the same turn. */
|
||||
type ContinuationDecision =
|
||||
| { action: 'stop' }
|
||||
| { action: 'continue'; reason?: { content: ContentBlock[]; source: MessageSource } }
|
||||
/** Action returned by a listener that owns model-request recovery. */
|
||||
type RequestErrorAction = { kind: 'retry' } | undefined
|
||||
```
|
||||
|
||||
`agent/request-error` 接收确切的原始 `RequestError`、其不可变 `LlmFailure`、在连续序列中已批准另一次请求的不可变失败列表、轮次信号以及 `next()`。恢复插件按 `failure.code` 路由,而不是按活跃错误的消息路由;每项策略只统计自身的 code,一次成功请求会清空历史:
|
||||
|
||||
```ts type-equiv
|
||||
/** Model-request failure with an optional machine-routable provider code. */
|
||||
type RequestError = Error & { code?: string }
|
||||
```
|
||||
|
||||
它返回 `RequestErrorDecision`;`retry` 在恢复 listener 的持久变更之后打开一个带新编号的步骤,而 `fail` 在 `turn/end` 上保留结构化失败:
|
||||
|
||||
```ts type-equiv
|
||||
/** Failed-request recovery decision; `retry` opens another numbered step while listeners delegate by calling `next()`. */
|
||||
type RequestErrorDecision = { action: 'fail' } | { action: 'retry' }
|
||||
```
|
||||
|
||||
`agent/post-step` 会在 assistant 输出、真实或合成的工具结果、缓冲上下文与 steering 持久化之后、`step/end` 之前被 await。被取消的工具批次在排空后携带 aborted signal 到达这里;其签名为 `(agent, turn, step, signal)`,可回放事实保留在会话日志中,而不是瞬态 payload 中。
|
||||
|
||||
`agent/turn-stop` 返回仅停止的 `ContinuationStop` 子集或 `undefined`。循环在折叠普通决策、其 reason 和待处理 steering 之后调用此串行检查点;stop 是终态,会丢弃待处理的 steering。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* The terminal subset of {@link ContinuationDecision}. A listener on
|
||||
* `agent/turn-stop` returns this to make the already-composed continuation
|
||||
* outcome terminal; `undefined` abstains.
|
||||
*/
|
||||
type ContinuationStop = Extract<ContinuationDecision, { action: 'stop' }>
|
||||
```
|
||||
`agent/step` 是请求推导前唯一的串行边界。`agent/turn-stopping` 在轮次没有工具或 steering(中途引导)后续时运行,先于最后一次 steering 排空。
|
||||
|
||||
`agent/session-start` 携带 `SessionStartSource`(会话生命周期为何开始;桥接层据此匹配其 SessionStart):
|
||||
|
||||
@@ -668,8 +654,6 @@ type ContinuationStop = Extract<ContinuationDecision, { action: 'stop' }>
|
||||
type SessionStartSource = 'startup' | 'resume' | 'clear' | 'compact'
|
||||
```
|
||||
|
||||
`agent/session-prefix` 在每个循环实例中组合一次 `Message[]`。深度冻结的结果被记录在请求 header 中,并前置于每次派生历史,使其成为会话稳定开场白的归属。恢复的实例会重新组合;会话中途的变更使用仅追加的上下文通道。该 waterfall 直接返回内容,因为它是贡献而非决策。
|
||||
|
||||
## `ToolDefinition`
|
||||
|
||||
唯一属于核心的流水线编写类型:每个已注册工具*是什么*——一个面向模型的 `ToolSchema` 加上一个 `execute` 函数,以及可选的最终内容回调与 UI 回调。工具作者很少手动构造它(`defineTool` DSL 会用类型化参数构建),但它是注册表持有、循环分发所经过的契约。
|
||||
|
||||
Reference in New Issue
Block a user