docs(i18n): address core-data review feedback
This commit is contained in:
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
approval.md: c9b411e38508fc7e8ac2e2dd923d9c8cc2f475a3
|
||||
approval.zh.md: ee593298fece8b2782228f2e93c5509871e15166
|
||||
approval.zh.md: 54985cd32f6b16ef90a1ddd1f6da4b87a261788d
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
## 标识与结果
|
||||
|
||||
每个请求都会获得一个全新的 `ApprovalRequestId`。该品牌类型将 `approval/asked` 与 `approval/decided` 审计事件配对,同时不会让审批 id 与工具调用 id 或 agent/session id 互换。
|
||||
每个请求都会获得一个全新的 `ApprovalRequestId`。该品牌类型将 `approval/asked` 与 `approval/decided` 审计事件配对,同时不会让审批 id 与工具调用 id 或 agent/会话 id 互换。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
bash.md: 35cf2061588907dde41123efb01e453eb9cc929d
|
||||
bash.zh.md: 56e5ae7b6231575a6b591093724d5560ade27056
|
||||
bash.zh.md: 2b388bd51047219ed46faa07313bb84da1dbb080
|
||||
@@ -161,9 +161,9 @@ interface CollectedOutput {
|
||||
|
||||
## 文件沙箱:`BashSandboxInfo`
|
||||
|
||||
消费 sandbox 的执行器通过 `BashExecutor.sandboxMode` 暴露其已配置的模式回退值。工具层请求 [`@deepseek-ai/dsh-sandbox-policy`](../../packages/sandbox/sandbox-policy/README.md),把每个调用会话的持久 `sandbox/mode` 覆盖值与不可变 cwd 解析为 `BashExecRequest.sandboxPolicy`;经用户批准、严格更宽松的调用只替换模式。模式/root/enforcement 词汇归 [`@deepseek-ai/dsh-sandbox` seam](sandbox.md) 所有;模式仅管辖文件效果。
|
||||
使用沙箱的执行器通过 `BashExecutor.sandboxMode` 暴露其已配置的模式回退值。工具层请求 [`@deepseek-ai/dsh-sandbox-policy`](../../packages/sandbox/sandbox-policy/README.md),把每个调用会话的持久 `sandbox/mode` 覆盖值与不可变 cwd 解析为 `BashExecRequest.sandboxPolicy`;经用户批准、严格更宽松的调用只替换模式。模式/root/enforcement 词汇归 [`@deepseek-ai/dsh-sandbox` 沙箱 seam](sandbox.md) 所有;模式仅管辖文件效果。
|
||||
|
||||
sandbox 化运行会报告其模式、保守的拒绝分类与强制执行完整度。`runnerFailed` 标记命令运行前 sandbox runner 已失败;前台执行会抛出 `SANDBOX_UNAVAILABLE`,而已结束的后台进程只能通过其事实通道报告。
|
||||
沙箱化运行会报告其模式、保守的拒绝分类与强制执行完整度。`runnerFailed` 标记命令运行前沙箱 runner 已失败;前台执行会抛出 `SANDBOX_UNAVAILABLE`,而已结束的后台进程只能通过其事实通道报告。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -183,11 +183,11 @@ interface BashSandboxInfo {
|
||||
}
|
||||
```
|
||||
|
||||
最后一项补全了这套词汇:当受限模式没有可用后端时,`ctx.sandbox` 提供方会抛出、执行器会传播由 [sandbox seam](sandbox.md) 所有的 `SANDBOX_UNAVAILABLE` 错误码。选定的 runner 拒绝其 profile 时会触达同一个故障关闭的前台错误;已结束的后台任务则记录 `runnerFailed`。模型会在结果中收到拒绝/runner 事实,仅当拒绝标记指出生效模式时才得知该模式,并可通过 `sandbox_permissions` 加 `justification` 请求一次性、严格更宽松的重试;执行任何操作前,`ctx.approval` 必须批准该次确切调用。完整的策略与切换设计见 [sandbox Agent Note](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)。
|
||||
最后一项补全了这套词汇:当受限模式没有可用后端时,`ctx.sandbox` 提供方会抛出、执行器会传播由[沙箱 seam](sandbox.md)所有的 `SANDBOX_UNAVAILABLE` 错误码。选定的 runner 拒绝其 profile 时会触达同一个故障关闭的前台错误;已结束的后台任务则记录 `runnerFailed`。模型会在结果中收到拒绝/runner 事实,仅当拒绝标记指出生效模式时才得知该模式,并可通过 `sandbox_permissions` 加 `justification` 请求一次性、严格更宽松的重试;执行任何操作前,`ctx.approval` 必须批准该次确切调用。完整的策略与切换设计见[沙箱 Agent Note](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)。
|
||||
|
||||
## 后台进程:`BashProcess`
|
||||
|
||||
`start()` 返回不含 id 或所有者的句柄。`dsh-tool-bash` 将它适配为 `ctx.tasks.start()` hooks;随后由通用运行时拥有任务标识与生命周期。`done` 在进程关闭时 resolve 且绝不 reject;进程结束后仍可读取,并且 sandbox 事实会在 `done` resolve 前写入。
|
||||
`start()` 返回不含 id 或所有者的句柄。`dsh-tool-bash` 将它适配为 `ctx.tasks.start()` 钩子;随后由通用运行时拥有任务标识与生命周期。`done` 在进程关闭时 resolve 且绝不 reject;进程结束后仍可读取,并且沙箱事实会在 `done` resolve 前写入。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
compaction.md: 71bbe7d9c17c6a3d35684a6c87d3072ab4f88df1
|
||||
compaction.zh.md: 173b7a7bb6e5e5a47922b0be8011e89a747c2771
|
||||
compaction.zh.md: 592e215e7efe69f0dd6099d3d510847cc732e641
|
||||
@@ -13,7 +13,7 @@
|
||||
| 事件 | 载荷 | 作用 |
|
||||
|---|---|---|
|
||||
| `compact/start` | `{ turn }` | 获取日志记录的锁 |
|
||||
| `compact/summary` | `{ summary, shadowedRange, shadowedSeqs, shadowedTokenCount, provider, model, maxTokens? }` | provenance:摘要 block、被遮蔽的 surface 边界对(`start`/`end` seq——位置跨度,而非数值区间)、按 surface 顺序排列的被遮蔽 seq、估算 token 数,以及摘要调用的 envelope(`provider`、`model`,若有生成上限则还包括该上限)——写入日志后,该一次性请求可由日志 + 代码重建(见可重建性 Agent Note) |
|
||||
| `compact/summary` | `{ summary, shadowedRange, shadowedSeqs, shadowedTokenCount, provider, model, maxTokens? }` | provenance:摘要块、被遮蔽的 surface 边界对(`start`/`end` seq——位置跨度,而非数值区间)、按 surface 顺序排列的被遮蔽 seq、估算 token 数,以及摘要调用的 envelope(`provider`、`model`,若有生成上限则还包括该上限)——写入日志后,该一次性请求可由日志 + 代码重建(见可重建性 Agent Note) |
|
||||
| `compact/end` | `{ turn, error? }` | 释放锁(摘要调用抛出异常时设置 `error`) |
|
||||
|
||||
锁括住**整个**操作:先追加 `compact/start`,然后执行摘要生成、写入 `compact/summary` 来源记录与 `user/message` 替换,最后才追加 `compact/end`。最后释放锁意味着操作中途崩溃会表现为可检测的遗留锁(有 `compact/start` 而无匹配的 `compact/end`),而非一个虚假声称压缩已完成的 `compact/end`。
|
||||
@@ -62,7 +62,7 @@ type CompactionTrigger = 'pressure' | 'context-overflow'
|
||||
|
||||
`CompactService` 暴露 `compactIfNeeded(agent, trigger, signal)` 以执行自动 `pressure` 或 `context-overflow` 策略;没有可安全执行的工作时返回 `null`。它还针对显式、两端均包含的 surface 范围暴露 `compactRegion(...)`。每个后端都使用包导出的 `COMPACT_CHECKPOINT_SOURCE` 标记其替换用的 `user/message`;消费方调用 `isCompactCheckpointSource()`,而不是把检查点识别逻辑耦合到某一个后端。实现必须把传入的 signal 转发给摘要流程。该 seam 不拥有计价 API:单例 [`ctx.tokenMeter`](token-meter.md) 直接拥有估算与回放,而 `dsh-compact-basic` 拥有保留策略、事件排序、按路由执行的摘要调用及其配置。
|
||||
|
||||
压力压缩在串行 `agent/post-step` 中运行:此时成功的 assistant 输出、工具结果、缓冲 context 和 steering 已持久化,但 `step/end` 尚未发生。一旦压力或规范化溢出满足条件,compact-basic 会在选择范围前调用可选的 [`ctx.toolResultPrune`](../../packages/compact/compact-tool-result-prune/README.md),再通过 `ctx.tokenMeter` 重新测量,并且可以在不生成摘要的情况下推进 surface。失败请求的恢复在失败 step 关闭后通过 `agent/request-error` 运行;仅当 surface replacement generation 前进时才批准一个带新编号的 step 重试,即便后续摘要工作在剪枝后抛异常亦如此;取消仍然优先。区域边界保持工具调用/结果配对,但不保持整个 turn,因此一个过大 turn 中较早关闭的 step 可以被压缩。`dsh-compact-basic` 拥有阈值、保留尾部策略、溢出上限与失败处理。
|
||||
压力压缩在串行 `agent/post-step` 中运行:此时成功的 assistant 输出、工具结果、缓冲上下文和 steering 已持久化,但 `step/end` 尚未发生。一旦压力或规范化溢出满足条件,compact-basic 会在选择范围前调用可选的 [`ctx.toolResultPrune`](../../packages/compact/compact-tool-result-prune/README.md),再通过 `ctx.tokenMeter` 重新测量,并且可以在不生成摘要的情况下推进 surface。失败请求的恢复在失败 step 关闭后通过 `agent/request-error` 运行;仅当 surface replacement generation 前进时才批准一个带新编号的 step 重试,即便后续摘要工作在剪枝后抛异常亦如此;取消仍然优先。区域边界保持工具调用/结果配对,但不保持整个 turn,因此一个过大 turn 中较早关闭的 step 可以被压缩。`dsh-compact-basic` 拥有阈值、保留尾部策略、溢出上限与失败处理。
|
||||
|
||||
该 seam 导出 `toolPairingBalancedBefore(session, seq)` 与 `toolPairingBalancedAfter(session, seq)`,用于这些边缘检查。两者都会验证当前 surface 成员关系,并拒绝缺失的 seq 与孤立结果;其缓存语义由[包契约](../../packages/compact/compact/README.md#tool-pairing-boundaries)规定。
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
core.md: 9c6f1d88c67f60ea3c42ba2fcec07cb20ede4214
|
||||
core.zh.md: 2fee488e609166328b743e8ca035a9b207706f37
|
||||
core.md: 7d0f9503dfd4c47f185d2bcef84528145d66fa70
|
||||
core.zh.md: 4bc31681d96483a300cc7a0ccfb5e489ba34f691
|
||||
@@ -165,6 +165,8 @@ Adapters emit a raw **chunk** protocol; the loop logs the chunks (replay fidelit
|
||||
|
||||
The full union, the adapter contract (usage-before-finish, raw-JSON tool arguments, the two sanctioned error paths), and `BlockAssembler` live on **[llm-streaming.md](llm-streaming.md)**.
|
||||
|
||||
<a id="the-model-request-and-result"></a>
|
||||
|
||||
## The model request
|
||||
|
||||
One model call is a fully-assembled `GenerateOptions`. The adapter answers with a raw `StreamChunk` stream; the consumer assembles it with `BlockAssembler` (see [llm-streaming.md](llm-streaming.md)).
|
||||
|
||||
@@ -26,9 +26,9 @@ harness 是一个微内核:一个极小的核心加上众多插件。大多数
|
||||
| [persistence.md](persistence.md) | 持久性 seam:`SessionPersistence`、JSONL + SQLite 后端、`session/flush`、崩溃恢复、`SessionHeader` |
|
||||
| [session-query.md](session-query.md) | 逻辑记录、有界精确事件读取与关系追踪 |
|
||||
| [session-title.md](session-title.md) | 持久标题快照、来源 provenance 与异步提供方契约 |
|
||||
| [system-prompt.md](system-prompt.md) | 逐次组装的上下文、工具提供方结果、prompt 段落与协作式组装 |
|
||||
| [system-prompt.md](system-prompt.md) | 逐次组装的上下文、工具提供方结果、提示词段落与协作式组装 |
|
||||
| [tools.md](tools.md) | `ToolDefinition` 完整字段、schema DSL、`ToolExecution`/`ToolResult`、工具展示 UI 类型,以及受保护的执行流水线 |
|
||||
| [user-interaction.md](user-interaction.md) | UI 支持的人工问答 seam:`AskUserQuestionRequest`、answer/options 词汇、provider API、错误分类体系 |
|
||||
| [user-interaction.md](user-interaction.md) | UI 支持的人工问答 seam:`AskUserQuestionRequest`、answer/options 词汇、提供方 API、错误分类体系 |
|
||||
| [approval.md](approval.md) | 一次性用户审批 seam:`ApprovalRequest`、`ApprovalOutcome`、逐会话策略、审计与 answerer 契约 |
|
||||
| [bash.md](bash.md) | bash 执行器 seam:`BashExecRequest`/`Spec`、`BashRunResult`、后台 `BashProcess` 句柄 |
|
||||
| [pty.md](pty.md) | 持久化终端 ID、后端/会话契约、发送就绪状态、有界读取与 owner 可见快照 |
|
||||
@@ -39,11 +39,11 @@ harness 是一个微内核:一个极小的核心加上众多插件。大多数
|
||||
| [skills.md](skills.md) | skill 服务:发现优先级、`SkillSummary`/`SkillDefinition`、会话前缀目录、面向模型的 `skill` 加载 |
|
||||
| [compaction.md](compaction.md) | 压缩(compaction)seam:`compact/*` 会话事件、`CompactionResult`、`CompactService` 接口 |
|
||||
| [subagent.md](subagent.md) | subagent seam:命名提供方注册表、`SubagentStartRequest`/`Result`/`Run`、启动时与运行时能力拆分 |
|
||||
| [web.md](web.md) | Web 访问 seam:`WebSearchRequest`/`Result`、`WebFetchRequest`/`Result`、`WebFetchBody`、provider 可用性、`WebError` |
|
||||
| [web.md](web.md) | Web 访问 seam:`WebSearchRequest`/`Result`、`WebFetchRequest`/`Result`、`WebFetchBody`、提供方可用性、`WebError` |
|
||||
| [spill.md](spill.md) | spill 存储 seam:`SaveTextSpill`、`SpillOwner`/`SpillSource`、`SpillRef`、品牌类型 `SpillLocator` |
|
||||
| [workflow.md](workflow.md) | 工作流 seam:`WorkflowStartRequest`、`WorkflowMeta`、`WorkflowRun`/`Result`、`workflow/*` 事件载荷、`WorkflowError` 致命性 |
|
||||
|
||||
> 这些页面上的类型声明及其 JSDoc 与源码等价,并由 `pnpm run verify-type-equiv` 检查漂移(见 [development.md](../development.md#documenting-types-verbatim-ts-type-equiv))。普通 block 保留完整声明;`public-api` block 保留去除实现体的公开 class 声明。Cordis 服务使用生成的[服务目录](../cordis-catalog/services.md)。
|
||||
> 这些页面上的类型声明及其 JSDoc 与源码等价,并由 `pnpm run verify-type-equiv` 检查漂移(见 [development.md](../development.md#documenting-types-verbatim-ts-type-equiv))。普通块保留完整声明;`public-api` 块保留去除实现体的公开 class 声明。Cordis 服务使用生成的[服务目录](../cordis-catalog/services.md)。
|
||||
|
||||
## `…Map → derived-union` 模式
|
||||
|
||||
@@ -92,7 +92,9 @@ declare module '@deepseek-ai/dsh-llm' {
|
||||
type Branded<B extends string> = string & { readonly [BRAND]: B }
|
||||
```
|
||||
|
||||
两个核心 ID 是 `CallId`(关联工具调用及其结果;dsh-llm)和 `SessionId`(活跃 agent 与持久 session 共享的标识;dsh-session)。能力包也会品牌化各自的 id,例如 [tasks.md](tasks.md) 中的 `TaskId`。
|
||||
两个核心 ID 是 `CallId`(关联工具调用及其结果;dsh-llm)和 `SessionId`(活跃 agent 与持久会话共享的标识;dsh-session)。能力包也会品牌化各自的 id,例如 [tasks.md](tasks.md) 中的 `TaskId`。
|
||||
|
||||
<a id="content-blocks-and-messages"></a>
|
||||
|
||||
## 内容块与消息
|
||||
|
||||
@@ -115,7 +117,7 @@ interface ContentBlockMap {
|
||||
|
||||
各块接口(完整字段见源码):`TextBlock`(`text`)、`ReasoningBlock`(thinking,区别于可见文本)、`ToolCallBlock`(`id: CallId`、`name`、原始 JSON `arguments`)、`ToolResultBlock`(`toolCallId`、嵌套 `content: ContentBlock[]`、`isError?`)。`ContentBlock = ContentBlockMap[ContentBlockType]`。核心集仅限于每条交付路径都尊重的块——多模态内容(图像、音频等)没有核心块类型;需要的功能通过可合并扩展的 map 添加,同时提供适配器/UI/压缩支持。
|
||||
|
||||
`Message` 由角色和 block 组成。由循环派生的 assistant 消息携带其持久 provider/model 标识,以及可选的适配器私有回放元数据:
|
||||
`Message` 由角色和块组成。由循环派生的 assistant 消息携带其持久提供方/模型标识,以及可选的适配器私有回放元数据:
|
||||
|
||||
```ts type-equiv
|
||||
/** Provider ownership and adapter-private replay data for an assistant message. */
|
||||
@@ -165,6 +167,8 @@ interface MessageSourceMap {
|
||||
|
||||
完整联合类型、适配器契约(usage-before-finish、原始 JSON 工具参数、两条认可的错误路径)和 `BlockAssembler` 在 **[llm-streaming.md](llm-streaming.md)** 中。
|
||||
|
||||
<a id="the-model-request-and-result"></a>
|
||||
|
||||
## 模型请求
|
||||
|
||||
一次模型调用是一个完全组装好的 `GenerateOptions`。适配器以原始 `StreamChunk` 流作答;消费方用 `BlockAssembler` 组装它(见 [llm-streaming.md](llm-streaming.md))。
|
||||
@@ -287,11 +291,11 @@ interface ToolSchema {
|
||||
|
||||
### 请求信封:`LlmCallConfig` 与记录的 header
|
||||
|
||||
循环从已记录状态构建每个请求。`EpochHeader` 通过完整的 `request/header` 快照记录调用配置、渲染后的 prompt、权威返回工具顺序(由 `toolOrder` 配置;未配置时按字典序)以及 session prefix。结合派生历史,请求便可由会话日志重建。见 [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` 的请求会被深度冻结,因此变更会抛异常;请求还携带进程本地循环标识,使观察者不会把单独记录的冻结辅助调用误认成对话请求。
|
||||
|
||||
在协议格式上,循环构建的请求按此顺序读取:`system` 槽位(渲染后的 prompt 组装)→ `messagePrefix`(冻结的会话前缀)→ 派生历史——边界快照,其尾部在轮次首步是最新的 `user/message`,在后续步骤是上一步的工具结果。前缀从不进入派生历史;它的持久记录是 header 事件,开发不变式针对每个循环构建的请求精确重算此等式。
|
||||
在协议格式上,循环构建的请求按此顺序读取:`system` 槽位(渲染后的提示词组装)→ `messagePrefix`(冻结的会话前缀)→ 派生历史——边界快照,其尾部在轮次首步是最新的 `user/message`,在后续步骤是上一步的工具结果。前缀从不进入派生历史;它的持久记录是 header 事件,开发不变式针对每个循环构建的请求精确重算此等式。
|
||||
|
||||
FIXME(call-config-shape):重新审视此类型的精确定义——出于缓存目的,哪些字段确实属于 epoch 层级(`model` 肯定属于;采样标量目前出于谨慎放在这里),以及适配器需要时,提供方特有的额外项(推理选项、额外 body 参数)应归属何处。
|
||||
|
||||
@@ -353,11 +357,11 @@ 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` 原因以及 turn enclosure 不变量都在 **[session.md](session.md)** 中。日志如何持久化——`SessionPersistence` seam、JSONL/SQLite 后端、`session/flush` checkpoint、崩溃恢复与 `SessionHeader`——则在 **[persistence.md](persistence.md)** 中。
|
||||
十四种事件变体(`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` 原因以及 turn enclosure 不变量都在 **[session.md](session.md)** 中。日志如何持久化——`SessionPersistence` seam、JSONL/SQLite 后端、`session/flush` 检查点、崩溃恢复与 `SessionHeader`——则在 **[persistence.md](persistence.md)** 中。
|
||||
|
||||
## Agent 句柄
|
||||
|
||||
`Agent` 是每个插件(UI、hook、orchestrator)面向编程的 surface。具体实现为 dsh-agent-loop 包内部细节;循环外没有任何组件依赖它。
|
||||
`Agent` 是每个插件(UI、钩子、orchestrator)面向编程的 surface。具体实现为 dsh-agent-loop 包内部细节;循环外没有任何组件依赖它。
|
||||
|
||||
源码:[`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
@@ -450,11 +454,11 @@ interface Agent {
|
||||
}
|
||||
```
|
||||
|
||||
`AgentStatus` 为 `'idle' | 'running' | 'disposed'`,`SessionId` 是品牌类型。`running` 描述整个驱动器的排空区间,可能跨越 turn 关闭、其持久化 checkpoint 以及连续的排队 turn;它不能证明某个 turn 仍然打开。`AgentOptions` 可合并扩展:core 声明 `provider?` 与 `model?`(在 `agent/request` 后,分发要求两者都存在)。Persona 归 `dsh-system-prompt` 所有:agent 作用域的 `deployment:persona` 可以遮蔽全局默认值。
|
||||
`AgentStatus` 为 `'idle' | 'running' | 'disposed'`,`SessionId` 是品牌类型。`running` 描述整个驱动器的排空区间,可能跨越 turn 关闭、其持久化检查点以及连续的排队 turn;它不能证明某个 turn 仍然打开。`AgentOptions` 可合并扩展:core 声明 `provider?` 与 `model?`(在 `agent/request` 后,分发要求两者都存在)。Persona 归 `dsh-system-prompt` 所有:agent 作用域的 `deployment:persona` 可以遮蔽全局默认值。
|
||||
|
||||
cause 是由 TypeScript 强制约束的同进程输入。活跃持有者会把其判别字段复制到仅运行时的 `AbortSignal.reason`;该值在发布 `turn/end` 前退役。`agentInterruptReasonOf(signal)` 无需查询环境中的 initiator 状态,即可识别 `user`、`parent` 与仅用于生命周期的 `disposed`。持久 `turn/end` 保留粗粒度 `{ kind: 'aborted' }` 结果;若需记录请求 provenance,应使用单独的持久事件,而不是让终态结果承担额外含义。
|
||||
|
||||
[事件分类](../architecture.md#event)拥有 `agent/*` 生命周期、checkpoint 与 waterfall 契约。Turn 和 step 边界是持久 session 事件,而不是 agent emit。
|
||||
[事件分类](../architecture.md#event)拥有 `agent/*` 生命周期、检查点与 waterfall 契约。Turn 和 step 边界是持久会话事件,而不是 agent emit。
|
||||
|
||||
## 发起 Agent
|
||||
|
||||
@@ -462,7 +466,7 @@ cause 是由 TypeScript 强制约束的同进程输入。活跃持有者会把
|
||||
|
||||
## 拦截决策
|
||||
|
||||
每个 `agent/*` 拦截 waterfall 都返回一个小型、特定于 seam 的类型化联合——统一的 Decision 惯用形状([tools.md](tools.md) 中工具 seam 的 `PreToolDecision`/`PostToolDecision` 也采用相同形状)。CC/Codex hook bridge 把其 `permissionDecision`/`decision`/`continue`/`additionalContext` 字段映射到这些联合上;原生插件则直接返回它们。Prompt 与工具后决策共享一种面向模型的 context 形状 `HookContext`,它必须携带 `source`(缺少 source 会默认成 `{kind:'user'}`,从而把插件 context 错标为用户 prompt)。其中的 `content` 作为 user-role 输入逐字到达模型,而 JSON `meta` 持久保存插件状态但不向模型暴露。未指定放置方式或指定为 `separate` 时,context 会成为 `context/message`;`prompt-prefix` 放置方式可用于 prompt 和 steering 收件箱附件,会在同一条消息中把 context 置于最终生效的请求之前。两种决策都携带 `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` 时,上下文会成为 `context/message`;`prompt-prefix` 放置方式可用于提示词和 steering 收件箱附件,会在同一条消息中把上下文置于最终生效的请求之前。两种决策都携带 `additionalContexts[]`,使每一项保留各自的 provenance、元数据与放置方式。Continuation reason 则是 steering 消息,并有意使用更窄的 content/source 形状。
|
||||
|
||||
源码:[`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
@@ -499,7 +503,7 @@ type PromptDecision =
|
||||
| { kind: 'block'; reason: string }
|
||||
```
|
||||
|
||||
`agent/turn-continuation` 返回 `ContinuationDecision`(step 有工具调用或注入了 steering 时,循环默认为 `continue`,否则为 `stop`;`continue` 的 `reason` 会记录为同一 turn 中下一 step 的 steering,因此不携带 context 元数据——即类型化 `/goal` 模式):
|
||||
`agent/turn-continuation` 返回 `ContinuationDecision`(step 有工具调用或注入了 steering 时,循环默认为 `continue`,否则为 `stop`;`continue` 的 `reason` 会记录为同一 turn 中下一 step 的 steering,因此不携带上下文元数据——即类型化 `/goal` 模式):
|
||||
|
||||
```ts type-equiv
|
||||
/** Turn continuation override; a continue reason is recorded as next-step steering in the same turn. */
|
||||
@@ -522,7 +526,7 @@ type RequestError = Error & { code?: string }
|
||||
type RequestErrorDecision = { action: 'fail' } | { action: 'retry' }
|
||||
```
|
||||
|
||||
`agent/post-step` 会在 assistant 输出、真实或合成的工具结果、缓冲 context 与 steering 持久化之后、`step/end` 之前被 await。被取消的工具批次在排空后携带 aborted signal 到达这里;其签名为 `(agent, turn, step, signal)`,可回放事实保留在 session 日志中,而不是瞬态 payload 中。
|
||||
`agent/post-step` 会在 assistant 输出、真实或合成的工具结果、缓冲上下文与 steering 持久化之后、`step/end` 之前被 await。被取消的工具批次在排空后携带 aborted signal 到达这里;其签名为 `(agent, turn, step, signal)`,可回放事实保留在会话日志中,而不是瞬态 payload 中。
|
||||
|
||||
`agent/turn-stop` 返回仅停止的 `ContinuationStop` 子集或 `undefined`。循环在折叠普通决策、其 reason 和待处理 steering 之后调用此串行检查点;stop 是终态,会丢弃待处理的 steering。
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
filesystem.md: 3c9b041da92e71cd20429e8b1549c0b8e6f2436d
|
||||
filesystem.zh.md: 11e386c98e671f4147419eec587399a5a3bb0d07
|
||||
filesystem.zh.md: 04d7c63da151f7198099486106ce2fb9d0dd1520
|
||||
@@ -177,13 +177,13 @@ interface FsEditOutcome {
|
||||
|
||||
## fs 策略事件(提供方 seam 词汇)
|
||||
|
||||
`dsh-fs` 拥有三个事件,由工具分发、策略插件监听,使发射方(`dsh-tool-fs`)与监听方(`dsh-fs-policy`)共享词汇,而发射方无需依赖策略插件。它们只携带 `dsh-fs` 词汇加一个不透明的 `object` actor,不含面向模型的概念,也不含 agent/session 所有者结构。
|
||||
`dsh-fs` 拥有三个事件,由工具分发、策略插件监听,使发射方(`dsh-tool-fs`)与监听方(`dsh-fs-policy`)共享词汇,而发射方无需依赖策略插件。它们只携带 `dsh-fs` 词汇加一个不透明的 `object` actor,不含面向模型的概念,也不含 agent/会话所有者结构。
|
||||
|
||||
`fs/write-intent` 与 `fs/edit-intent` 是**单槽决策 waterfall**:工具分发时附带一个默认 thunk(返回 `undefined`,即裸提供方),监听方完全决策而不调用 `next()`。该槽按注册顺序先到先得——由策略插件占据是部署约定,而非强制不变式。`fs/observed` 是一个即发即弃的记录事件,通过普通 `ctx.emit` 分发;其监听方必须是同步的、仅产生副作用,因为工具不守卫该 emit——抛异常的监听方会在一次已成功的变更上表现为工具的 `isError` 结果。生成的目录在 [events.md](../cordis-catalog/events.md) 中展示确切签名。
|
||||
|
||||
## 执行上下文(策略插件)
|
||||
|
||||
策略插件只需要足够的执行上下文,通过收窄 `fs/*` 事件携带的不透明 `object` actor 来推导观测状态的所有者。`ToolExecution` 满足此形状,因此 `dsh-tool-fs` 将其执行对象作为 actor 直接传递,而无需让 `dsh-fs-policy` 导入 tool、agent 或 session 包。
|
||||
策略插件只需要足够的执行上下文,通过收窄 `fs/*` 事件携带的不透明 `object` actor 来推导观测状态的所有者。`ToolExecution` 满足此形状,因此 `dsh-tool-fs` 将其执行对象作为 actor 直接传递,而无需让 `dsh-fs-policy` 导入工具、agent 或会话包。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -252,7 +252,7 @@ type FsErrorCode =
|
||||
| 'FS_ABORTED'
|
||||
```
|
||||
|
||||
目录列表使用 `FS_NOT_DIRECTORY`、`FS_PERMISSION_DENIED` 与 `FS_IO_ERROR` 区分已存在但并非目录的目标、被拒绝的列表操作和意外的后端 I/O 失败。`FS_SANDBOX_DENIED` 是强制执行 sandbox 的后端(`dsh-fs-sandbox`)所作的策略拒绝——模式边界拒绝了写入/编辑——与 `FS_PERMISSION_DENIED`(宿主内核拒绝)不同。`FS_NOT_OBSERVED` 表示策略插件没有此所有者的先前观察记录(或 `createIfAbsent` 遇到了现有文件)。`FS_STALE_VERSION` 表示后端版本不再与观察到的版本匹配(或编辑操作遇到缺失目标)。新鲜度授权没有部分/完整之分,因此不存在 `FS_PARTIAL_OBSERVATION`。
|
||||
目录列表使用 `FS_NOT_DIRECTORY`、`FS_PERMISSION_DENIED` 与 `FS_IO_ERROR` 区分已存在但并非目录的目标、被拒绝的列表操作和意外的后端 I/O 失败。`FS_SANDBOX_DENIED` 是强制执行沙箱的后端(`dsh-fs-sandbox`)所作的策略拒绝——模式边界拒绝了写入/编辑——与 `FS_PERMISSION_DENIED`(宿主内核拒绝)不同。`FS_NOT_OBSERVED` 表示策略插件没有此所有者的先前观察记录(或 `createIfAbsent` 遇到了现有文件)。`FS_STALE_VERSION` 表示后端版本不再与观察到的版本匹配(或编辑操作遇到缺失目标)。新鲜度授权没有部分/完整之分,因此不存在 `FS_PARTIAL_OBSERVATION`。
|
||||
|
||||
## 服务与插件
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
llm-streaming.md: cb99c935aea2dc9cc769e3056fdb98a2e5c9eacb
|
||||
llm-streaming.zh.md: 5dc270acc97cdeac8cf24a36c443d550e14122dc
|
||||
llm-streaming.zh.md: 36fb640a030645861a163f6b33b3c0b60cf5ed8e
|
||||
@@ -35,7 +35,7 @@ type StreamChunk =
|
||||
|
||||
## `LlmFailure`
|
||||
|
||||
每个抛出的失败或 final-adapter 带内失败都会规范化为一种可序列化、提供方中立的 payload。`providerRetryAfterMs` 是经校验、由提供方请求的正数延迟,而不是重试决策;`ProviderRequestId` 是用于诊断的不透明品牌字符串。
|
||||
每个抛出的失败或最终适配器的带内失败都会规范化为一种可序列化、提供方中立的 payload。`providerRetryAfterMs` 是经校验、由提供方请求的正数延迟,而不是重试决策;`ProviderRequestId` 是用于诊断的不透明品牌字符串。
|
||||
|
||||
```ts type-equiv
|
||||
/** Serializable provider-boundary facts; policy decides whether they are retryable. */
|
||||
@@ -59,18 +59,18 @@ interface LlmFailure {
|
||||
|
||||
- **`usage` 在 `finish` 之前,`finish` 之后不再有任何分片。** 将两者都推迟到提供方的流结束标记,这样尾部的 usage-only 分片就不会违反顺序。
|
||||
- **工具调用的 `arguments` 全程保持原始 JSON 字符串。** 部分片段通过 `argumentsDelta` 流式传输;如果提供方返回的是已解析的对象,适配器在 `block-end` 时重新序列化为字符串。
|
||||
- **两条受支持的错误路径,一种事实形状。** 失败可以从 `stream()` 抛出(传输/协议错误),**或者**以 `finish {kind:'error'|'aborted', failure}` 结束流(无法在流中途抛异常的适配器用它表示提供方带内错误)。`LlmError.failure` 携带同一个 `LlmFailure`。final adapter 边界保留被抛出的确切 `Error` 对象,并将不可变事实关联到该调用;agent loop 关闭失败的 step,再把错误、事实与不可变的先前已重试事实提供给 `agent/request-error`。若未恢复,结构化失败会成为 turn 错误,并且该次尝试不会提交正常 assistant 消息或工具副作用。
|
||||
- **两条受支持的错误路径,一种事实形状。** 失败可以从 `stream()` 抛出(传输/协议错误),**或者**以 `finish {kind:'error'|'aborted', failure}` 结束流(无法在流中途抛异常的适配器用它表示提供方带内错误)。`LlmError.failure` 携带同一个 `LlmFailure`。最终适配器边界保留被抛出的确切 `Error` 对象,并将不可变事实关联到该调用;agent loop 关闭失败的 step,再把错误、事实与不可变的先前已重试事实提供给 `agent/request-error`。若未恢复,结构化失败会成为 turn 错误,并且该次尝试不会提交正常 assistant 消息或工具副作用。
|
||||
- **一次适配器调用就是一次提供方尝试。** 适配器禁用库重试。agent 层恢复会打开另一个持久、带编号的 step;直接调用 `ctx.llm.stream()` 的调用方仍然只尝试一次。
|
||||
- **提供方停顿在传输层受到时限约束。** 两个已交付的远程适配器都暴露正数且有限的 `streamIdleTimeoutMs`,默认五分钟。watchdog 只在 iterator `next()` 尚未完成时启动,整个请求使用同一个稳定 signal,把自身到期映射为 `TIMEOUT`,并把更早发生的调用方中止保留为 `ABORTED`。
|
||||
- **上下文溢出只有一个规范 code。** 两个 DeepSeek 适配器都通过 `isContextWindowExceededError()` 对提供方的显式细节分类并暴露 `CONTEXT_WINDOW_EXCEEDED`,无论失败以抛出的 HTTP `LlmError` 还是带内 finish error 到达。消费方按 code 路由,绝不依赖提供方文本。
|
||||
- **每个提供方 HTTP 请求都携带应用归属头。** 适配器发送 `attributionHeaders()`(见下文)作为 `User-Agent` 基线,并通过协议级测试加以证明(mock 服务器断言收到的 header,或对基于库的适配器使用库的 header 钩子)。
|
||||
- **回放状态归适配器所有。** 成功的 `finish` 可以携带重建提供方原生响应所需的无损 JSON 状态。除非 `agent/step-result` listener 改写了内容,否则循环会将其与组装后的 assistant 消息一起存储。后续请求中,仅当历史提供方与目标提供方当前注册到完全相同的适配器实例时,`LlmService` 才会传递该状态。该适配器负责校验状态并拥有所有跨模型或跨提供方转换;其他适配器只会收到提供方中立的内容与 provenance,不会收到私有状态。
|
||||
|
||||
该契约由两个有意保持独立的实现锁定:`dsh-llm-deepseek`(手写 fetch/SSE)和 `dsh-llm-pi-ai`(通过 `@earendil-works/pi-ai` 实现的通用多提供方适配器)。基于库的适配器覆盖 finish-chunk 错误路径,而传输边界测试证明每个空闲 watchdog 都会停止其实际请求。
|
||||
该契约由两个有意保持独立的实现锁定:`dsh-llm-deepseek`(手写 fetch/SSE)和 `dsh-llm-pi-ai`(通过 `@earendil-works/pi-ai` 实现的通用多提供方适配器)。基于库的适配器覆盖 finish 分片错误路径,而传输边界测试证明每个空闲 watchdog 都会停止其实际请求。
|
||||
|
||||
## `AppIdentity`:应用归属
|
||||
|
||||
每个适配器都会向提供方发送的静态公开应用标识([`packages/llm/llm/src/attribution.ts`](../../packages/llm/llm/src/attribution.ts))。`attributionHeaders(identity?)` 只把它映射到标准 `User-Agent` header;该契约有意不支持 OpenRouter 特有的应用归属 header。默认 `APP_IDENTITY` 从包 manifest 获取版本;每个字段都是公开产品事实——不含 secret、路径、session id 或逐用户标识,且任何逐请求信息都不得影响这些值。设计理由见[强制 `User-Agent` 归属](../../.agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md)。
|
||||
每个适配器都会向提供方发送的静态公开应用标识([`packages/llm/llm/src/attribution.ts`](../../packages/llm/llm/src/attribution.ts))。`attributionHeaders(identity?)` 只把它映射到标准 `User-Agent` header;该契约有意不支持 OpenRouter 特有的应用归属 header。默认 `APP_IDENTITY` 从包 manifest 获取版本;每个字段都是公开产品事实——不含 secret、路径、会话 id 或逐用户标识,且任何逐请求信息都不得影响这些值。设计理由见[强制 `User-Agent` 归属](../../.agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md)。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -92,7 +92,7 @@ interface AppIdentity {
|
||||
|
||||
## `TokenUsage`
|
||||
|
||||
逐调用 token 记账。各计数**互不重叠**:`inputTokens` 只包含未缓存输入;缓存输入单独报告,计费输入是三者之和。若提供方把缓存命中折入单一 prompt 总数(如 DeepSeek 的 `prompt_tokens`),适配器会再将其扣除。`reasoningTokens` 存在时只是信息性细节,已经包含在 `outputTokens` 中;汇总时不得重复相加。
|
||||
逐调用 token 记账。各计数**互不重叠**:`inputTokens` 只包含未缓存输入;缓存输入单独报告,计费输入是三者之和。若提供方把缓存命中折入单一提示词总数(如 DeepSeek 的 `prompt_tokens`),适配器会再将其扣除。`reasoningTokens` 存在时只是信息性细节,已经包含在 `outputTokens` 中;汇总时不得重复相加。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -114,7 +114,7 @@ interface TokenUsage {
|
||||
|
||||
## `BlockAssembler`
|
||||
|
||||
`BlockAssembler`([`packages/llm/llm/src/assembler.ts`](../../packages/llm/llm/src/assembler.ts))是唯一的共享实现,负责把 `StreamChunk` 流折叠回 `ContentBlock`、usage、finish reason 与 replay state。循环在记录原始 chunk 的同时,把同一批 chunk 送入 assembler,再将组装后的 assistant 内容连同其 provider/model provenance 一起存储。需要组装结果、又不想重新实现 fold 的消费方使用它。
|
||||
`BlockAssembler`([`packages/llm/llm/src/assembler.ts`](../../packages/llm/llm/src/assembler.ts))是唯一的共享实现,负责把 `StreamChunk` 流折叠回 `ContentBlock`、usage、结束原因与回放状态。循环在记录原始分片的同时,把同一批分片送入 assembler,再将组装后的 assistant 内容连同其提供方/模型 provenance 一起存储。需要组装结果、又不想重新实现 fold 的消费方使用它。
|
||||
|
||||
```ts public-api
|
||||
/**
|
||||
@@ -156,7 +156,7 @@ declare class BlockAssembler {
|
||||
|
||||
## seam
|
||||
|
||||
`LlmAdapter` 是提供方 seam:创建子类、实现 `stream()`,再用 `ctx.llm.registerAdapter(providers, adapter)` 注册一个适配器实例。`GenerateOptions.provider` 选择已注册适配器;`GenerateOptions.model` 会传给该适配器,无需在生命周期启动时注册。重复提供方路由会原子失败。可选的 `providerInfo()` 与异步 `listModels()` 方法为 `LlmService.listProviders()` / `listModels()` 提供分离的 selector 元数据。该目录仅供参考,不是请求白名单:适配器仍是权威,并可接受未列出的模型 id。单独的 `resolveModelContext()` 查询会暴露确切路由上对正确性敏感的容量信息,但不会让目录成员关系具有权威性;缺失表示元数据未知,而不是路由无效。适配器查找发生在 `llm/stream` waterfall 的终端 continuation,因此 listener 可以在查找前短路调用,或路由一个可变的一次性请求。`block-start` / `block-end` 的 `index` 关联与 assembler 共同意味着适配器只需 emit 格式正确的 chunk——block 重组不是每个适配器各自的问题。消费方 surface(`ctx.llm.stream()`)与 `llm/stream` waterfall 见 [architecture.md § 内容 block 与流式传输](../architecture.md#content-blocks-and-streaming-dsh-llm)。
|
||||
`LlmAdapter` 是提供方 seam:创建子类、实现 `stream()`,再用 `ctx.llm.registerAdapter(providers, adapter)` 注册一个适配器实例。`GenerateOptions.provider` 选择已注册适配器;`GenerateOptions.model` 会传给该适配器,无需在生命周期启动时注册。重复提供方路由会原子失败。可选的 `providerInfo()` 与异步 `listModels()` 方法为 `LlmService.listProviders()` / `listModels()` 提供分离的 selector 元数据。该目录仅供参考,不是请求白名单:适配器仍是权威,并可接受未列出的模型 id。单独的 `resolveModelContext()` 查询会暴露确切路由上对正确性敏感的容量信息,但不会让目录成员关系具有权威性;缺失表示元数据未知,而不是路由无效。适配器查找发生在 `llm/stream` waterfall 的终端 continuation,因此 listener 可以在查找前短路调用,或路由一个可变的一次性请求。`block-start` / `block-end` 的 `index` 关联与 assembler 共同意味着适配器只需 emit 格式正确的分片——块重组不是每个适配器各自的问题。消费方 surface(`ctx.llm.stream()`)与 `llm/stream` waterfall 见 [architecture.md § 内容块与流式传输](../architecture.md#content-blocks-and-streaming-dsh-llm)。
|
||||
|
||||
```ts public-api
|
||||
/**
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
persistence.md: d4bb4ed1b65eb74ff03483bf3a71a1736300834e
|
||||
persistence.zh.md: 93a418b3b06dbf0d42978900ef79a0fb86ac15f7
|
||||
persistence.zh.md: ea0bff84bb939f0eab370375509e920ea45eb404
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
## flush 检查点
|
||||
|
||||
`session/event` 是一个*同步*通知;持久化插件会将其缓冲(write-behind)至 `session/flush`。循环会 await 普通 turn 的 checkpoint 后再领取下一个队列项;同步的 idle `inject()` 会调度自己的 checkpoint 而不阻塞 `send()`,dispose 仍会将其排空。成功 flush 会把已关闭 turn 作为一个单元持久提交;被拒绝的 flush 通过 `agent/error` 与 logger 报告——绝不会作为已关闭 turn 之后的 session 事件——而后端会保留已缓冲事件供下次 flush 使用。
|
||||
`session/event` 是一个*同步*通知;持久化插件会将其缓冲(write-behind)至 `session/flush`。循环会 await 普通 turn 的检查点后再领取下一个队列项;同步的 idle `inject()` 会调度自己的检查点而不阻塞 `send()`,dispose 仍会将其排空。成功 flush 会把已关闭 turn 作为一个单元持久提交;被拒绝的 flush 通过 `agent/error` 与 logger 报告——绝不会作为已关闭 turn 之后的会话事件——而后端会保留已缓冲事件供下次 flush 使用。
|
||||
|
||||
## 崩溃恢复保留被中断的轮次
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
## `SessionLocation`——可选的逐会话制品目标
|
||||
|
||||
`SessionPersistence.locate(meta)` 会同步解析一个归后端所有的独立制品,而不会读取、创建或 flush 它。JSONL 返回其绝对目标路径;SQLite 因各 session 共享一个数据库而返回 `undefined`。因此,返回的路径可能指向尚不存在、或还不包含当前未 flush turn 的文件;它是位置提示,不是授权或新鲜度保证。
|
||||
`SessionPersistence.locate(meta)` 会同步解析一个归后端所有的独立制品,而不会读取、创建或 flush 它。JSONL 返回其绝对目标路径;SQLite 因各会话共享一个数据库而返回 `undefined`。因此,返回的路径可能指向尚不存在、或还不包含当前未 flush turn 的文件;它是位置提示,不是授权或新鲜度保证。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -73,7 +73,7 @@ interface SessionHeader {
|
||||
|
||||
## `CreateSessionOptions`:seed 与元数据
|
||||
|
||||
通过 store 创建 `Session` 时会接收 `seed`(回放/fork 现有事件日志)与 `meta`(store 折叠进 `SessionHeader` 的存储层字段)。store 填充 `version`/`id` 并为 `createdAt` 提供默认值;调用方提供已校验的绝对 `cwd`、`parentSession` 谱系、`seedLength` 种子边界、`delegationDepth`,以及——仅在重建已持久化 session 时——需要保留的原始 `createdAt`。
|
||||
通过 store 创建 `Session` 时会接收 `seed`(回放/fork 现有事件日志)与 `meta`(store 折叠进 `SessionHeader` 的存储层字段)。store 填充 `version`/`id` 并为 `createdAt` 提供默认值;调用方提供已校验的绝对 `cwd`、`parentSession` 谱系、`seedLength` 种子边界、`delegationDepth`,以及——仅在重建已持久化会话时——需要保留的原始 `createdAt`。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -104,7 +104,7 @@ interface CreateSessionOptions {
|
||||
|
||||
两者都实现同一个抽象 `SessionPersistence`(在 `SessionEvent` 上执行 locate/create/append/load/list),并通过 `runPersistenceContract`,证明该 seam 确实与后端无关:
|
||||
|
||||
- **[dsh-session-persistence-jsonl](../../packages/session-persistence/session-persistence-jsonl)**——每个 session 一份仅追加的逻辑 JSONL 日志,默认存储为带 checksum 的连续 Zstandard frame,也可配置为原始行;支持崩溃安全的原子写入、中断 turn 恢复以及读取/回放路径。
|
||||
- **[dsh-session-persistence-jsonl](../../packages/session-persistence/session-persistence-jsonl)**——每个会话一份仅追加的逻辑 JSONL 日志,默认存储为带 checksum 的连续 Zstandard frame,也可配置为原始行;支持崩溃安全的原子写入、中断 turn 恢复以及读取/回放路径。
|
||||
- **[dsh-session-persistence-sqlite](../../packages/session-persistence/session-persistence-sqlite)**:基于 `node:sqlite`,每个 `SessionEvent` 一行。行结构 `(session_id, seq, type, time, data, source_event_seqs, surface_op)` 与事件 1:1 映射(包含可选的 surface 元数据),因此没有需要保持同步的并行持久化 schema。
|
||||
|
||||
共享同一磁盘 session 的多个后端通过[共享持久化写入协调器](../../.agents/notes/implemented/architecture/2026-06-18-shared-persistence-write-coordinator.md)协调写入。
|
||||
共享同一磁盘会话的多个后端通过[共享持久化写入协调器](../../.agents/notes/implemented/architecture/2026-06-18-shared-persistence-write-coordinator.md)协调写入。
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
sandbox.md: 9bc05fa06f22fdc9ac9e8aacd482c1e7c2f2edec
|
||||
sandbox.zh.md: dd02041dea998221f1fa7549cb829791137b5088
|
||||
sandbox.zh.md: 8db5520a125141cd043323ded4d26b92b81d1ea0
|
||||
@@ -40,7 +40,7 @@ type SandboxEnforcement = 'full' | 'partial'
|
||||
|
||||
## 逐调用策略
|
||||
|
||||
完整执行策略会按每次能力调用解析并携带。它包括 `danger-full-access`,因此消费方可以只解析一次策略,再决定是否绕过约束。普通工具调用从调用 session 的不可变 cwd 派生 `workspaceRoot`;部署配置是没有 agent 时的回退值。root 会先按文件系统语义规范化,再做词法规范化,因此包含 `symlink/..` 的 cwd 会标识所生成进程实际运行的目录。
|
||||
完整执行策略会按每次能力调用解析并携带。它包括 `danger-full-access`,因此消费方可以只解析一次策略,再决定是否绕过约束。普通工具调用从调用会话的不可变 cwd 派生 `workspaceRoot`;部署配置是没有 agent 时的回退值。root 会先按文件系统语义规范化,再做词法规范化,因此包含 `symlink/..` 的 cwd 会标识所生成进程实际运行的目录。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ interface SandboxExecutionPolicy {
|
||||
}
|
||||
```
|
||||
|
||||
`ctx.sandboxPolicy.resolve()` 接收活跃 session;对于已批准的重试,还接收显式模式。该服务拥有优先级与 root 回退规则,使 bash 和 fs 不必重复实现。
|
||||
`ctx.sandboxPolicy.resolve()` 接收活跃会话;对于已批准的重试,还接收显式模式。该服务拥有优先级与 root 回退规则,使 bash 和 fs 不必重复实现。
|
||||
|
||||
```ts type-equiv
|
||||
/** Inputs that select the sandbox policy for one capability call. */
|
||||
@@ -68,7 +68,7 @@ interface SandboxPolicyRequest {
|
||||
}
|
||||
```
|
||||
|
||||
只有受约束的执行会到达 `ctx.sandbox`;其提供方策略在保留同一 root 的同时收窄模式。这使并发 session、消费方与一次性提权重试可以向同一提供方请求不同边界,而无需改变提供方状态。
|
||||
只有受约束的执行会到达 `ctx.sandbox`;其提供方策略在保留同一 root 的同时收窄模式。这使并发会话、消费方与一次性提权重试可以向同一提供方请求不同边界,而无需改变提供方状态。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
scope.md: 73a697f2843293daffff85dabf4656346f7dcd04
|
||||
scope.zh.md: b5cc21cfb3d3dbb3890d179f9a813dccb1c318ec
|
||||
scope.zh.md: 5a40a3e964d2113752efba92beea4f4f333e94b3
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](scope.md) | 中文
|
||||
|
||||
[scope 包](../../packages/core/scope)提供 identity、carrier 与 scoped-layer 词汇,使同一个注册 context 同时代表逐 agent 可见性和共享生命周期所有权。它是库原语,而不是 Cordis 服务;生命周期设计理由由 [agent-scope 运行时设计 Agent Note(agent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-12-agent-scope-runtime-design.md#scope-routing-one-opaque-key-selects-one-layer)规定,registry-layer 决策由[共享存储 Agent Note](../../.agents/notes/implemented/architecture/2026-07-12-scoped-layers-store.md)规定,可调用 API 与过滤语义则由包 [README](../../packages/core/scope/README.md)规定。
|
||||
[scope 包](../../packages/core/scope)提供 identity、carrier 与 scoped-layer 词汇,使同一个注册上下文同时代表逐 agent 可见性和共享生命周期所有权。它是库原语,而不是 Cordis 服务;生命周期设计理由由 [agent-scope 运行时设计 Agent Note(agent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-12-agent-scope-runtime-design.md#scope-routing-one-opaque-key-selects-one-layer)规定,注册表层决策由[共享存储 Agent Note](../../.agents/notes/implemented/architecture/2026-07-12-scoped-layers-store.md)规定,可调用 API 与过滤语义则由包 [README](../../packages/core/scope/README.md)规定。
|
||||
|
||||
源码:[`packages/core/scope/src/index.ts`](../../packages/core/scope/src/index.ts) 与 [`packages/core/scope/src/store.ts`](../../packages/core/scope/src/store.ts)。
|
||||
|
||||
@@ -54,6 +54,6 @@ interface ScopeLayer {
|
||||
}
|
||||
```
|
||||
|
||||
`ScopedLayers<L>` 拥有立即创建的全局 layer,以及惰性创建的确切作用域 layer。读取不会创建 layer:`peek(undefined)` 表示没有 overlay,而 `merge()` 会物化按插入顺序排列的全局具名 entry,随后是带作用域的 shadow。注册使用同一个 context 表示可见性与 Cordis effect 所有权,在可选通知前收集一个同步 undo,返回 Cordis 的确切 disposer,并且只在带作用域 layer 的完整 `ScopeLayer` 为空时回收它。
|
||||
`ScopedLayers<L>` 拥有立即创建的全局 layer,以及惰性创建的确切作用域 layer。读取不会创建 layer:`peek(undefined)` 表示没有 overlay,而 `merge()` 会物化按插入顺序排列的全局具名 entry,随后是带作用域的 shadow。注册使用同一个上下文表示可见性与 Cordis effect 所有权,在可选通知前收集一个同步 undo,返回 Cordis 的确切 disposer,并且只在带作用域 layer 的完整 `ScopeLayer` 为空时回收它。
|
||||
|
||||
`NamedEntries<V>` 提供按插入顺序的查找与 live iteration,重复错误由调用方所有。`AnonymousEntries<V>` 为每次 append 分配唯一标识,使相等的值仍相互独立。迭代在同一非空 table generation 内保持 live;排空 table 会让现有 iterator 与后续插入脱离。两者都返回幂等的确切 entry undo;共享的 `EntryValues` 实现接口不公开。
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
session-query.md: 27d0dccc6cf32b4dafebeaf64a3b8aefa138b98d
|
||||
session-query.zh.md: 3b6d30916e7cc012859b86419fd326fadf02f571
|
||||
session-query.zh.md: 1c3abafb009cfc7376c96da4552b49959c09fef3
|
||||
@@ -1,8 +1,8 @@
|
||||
# Session Query
|
||||
# 会话查询
|
||||
|
||||
[English](session-query.md) | 中文
|
||||
|
||||
对优先使用 live 数据的逻辑 session 集合执行精确读取与关系追踪。[包契约](../../packages/session-query/session-query)拥有来源优先级、动态可选持久化、克隆、surface 分类、有界窗口、追踪校验与类型化失败。全文搜索属于另一个拟议的 SQLite 包。
|
||||
对优先使用 live 数据的逻辑会话集合执行精确读取与关系追踪。[包契约](../../packages/session-query/session-query)拥有来源优先级、动态可选持久化、克隆、surface 分类、有界窗口、追踪校验与类型化失败。全文搜索属于另一个拟议的 SQLite 包。
|
||||
|
||||
源码:[`packages/session-query/session-query/src/types.ts`](../../packages/session-query/session-query/src/types.ts)
|
||||
|
||||
@@ -57,7 +57,7 @@ interface SessionEventRecord {
|
||||
}
|
||||
```
|
||||
|
||||
## Session 谱系
|
||||
## 会话谱系
|
||||
|
||||
`SessionLineageTrace` 按由近及远的顺序携带已知 parent,并携带一片由直接 descendant 递归嵌套而成的森林。完整性判别字段使已知 root 与缺失 parent 互斥。
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
session.md: e07e38037e51c74886db88d24353266f6035b15a
|
||||
session.zh.md: 2125f5ac97d3649aab166927b1607d54888fa9f1
|
||||
session.zh.md: 82ddaa2f53585f41fd9a343c9f710b339b2399fd
|
||||
@@ -108,7 +108,7 @@ interface SessionEventMap {
|
||||
}
|
||||
```
|
||||
|
||||
`PromptMessageData.content` 始终是确切的模型可见内容。当附加 context 声明 `prompt-prefix` 放置方式时,AgentLoop 会依次把它的 block、一个 `## My request:` 分隔符以及最终生效的直接提示词拼接进该数组。可选且对模型隐藏的 `envelope` 会保留 `displayContent`,以及按顺序排列的前缀 context 来源/元数据描述信息,使 transcript(文本记录)、标题与重新引用消费方无需改变可重建历史,就能呈现人类提示词。`displayPromptContent()` 负责该选择,并为普通事件和较早的事件回退到 `content`。
|
||||
`PromptMessageData.content` 始终是确切的模型可见内容。当附加上下文声明 `prompt-prefix` 放置方式时,AgentLoop 会依次把它的块、一个 `## My request:` 分隔符以及最终生效的直接提示词拼接进该数组。可选且对模型隐藏的 `envelope` 会保留 `displayContent`,以及按顺序排列的前缀上下文来源/元数据描述信息,使 transcript(文本记录)、标题与重新引用消费方无需改变可重建历史,就能呈现人类提示词。`displayPromptContent()` 负责该选择,并为普通事件和较早的事件回退到 `content`。
|
||||
|
||||
### `OutOfBandSessionEventMap`:受限的带外追加显式准入
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
subagent.md: 97d6862c10a0757c41472f207f857c25f3f5d50f
|
||||
subagent.zh.md: 1e88562aed99122750e9d137cb0b722d4c38c36b
|
||||
subagent.zh.md: de0e5c89d508565b84ad1240a11f069dc3795e9c
|
||||
@@ -35,7 +35,7 @@ interface SubagentCapabilities {
|
||||
|
||||
## 启动请求
|
||||
|
||||
工具层根据模型输入和自身配置构建此请求;服务在 `start` 之前针对指定提供方进行校验。必填的 `parent` 提供会话 cwd、谱系与委派深度。可选的 output schema、depth、tool filter 和 persona 需要对应的能力 flag 匹配。不支持的 schema 在启动时即失败;进程内后端将 filter 和 persona 的作用域限定在子 agent 创建阶段,并通过强制 capture tool 实现所支持的 object-rooted schema。
|
||||
工具层根据模型输入和自身配置构建此请求;服务在 `start` 之前针对指定提供方进行校验。必填的 `parent` 提供会话 cwd、谱系与委派深度。可选的 output schema、depth、工具过滤器和 persona 需要对应的能力 flag 匹配。不支持的 schema 在启动时即失败;进程内后端将 filter 和 persona 的作用域限定在子 agent 创建阶段,并通过强制 capture 工具实现所支持的 object-rooted schema。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -200,7 +200,7 @@ interface SubagentRun {
|
||||
}
|
||||
```
|
||||
|
||||
本地 run 必须在 `start()` fulfill 前发布一个普通子 agent/session,将该子 session id 作为 `SubagentRun.id` 返回,以 `localAgent` 暴露确切子 agent,并在子 agent 的 `parentSession` header 中记录 `request.parent.session.id`。运行时所有权可以把子 agent 放在 parent、提供方或 root 作用域下。远程提供方则返回 parent 作用域的生命周期 id 与 `localAgent: undefined`。
|
||||
本地 run 必须在 `start()` fulfill 前发布一个普通子 agent/会话,将该子会话 id 作为 `SubagentRun.id` 返回,以 `localAgent` 暴露确切子 agent,并在子 agent 的 `parentSession` header 中记录 `request.parent.session.id`。运行时所有权可以把子 agent 放在 parent、提供方或 root 作用域下。远程提供方则返回 parent 作用域的生命周期 id 与 `localAgent: undefined`。
|
||||
|
||||
## 提供方 seam:`SubagentProvider`
|
||||
|
||||
@@ -244,5 +244,5 @@ interface SubagentProvider {
|
||||
|
||||
spawn 和 fork 后端通过 `parent.ctx` 创建一个普通 agent,将取消信号传入核心创建流程,并通过 `AgentHandle` 进行 dispose。移除提供方会阻止新的 start,但不会撤销已接受的 run。每个子 agent 获得一个新的扁平作用域,而非继承父级注册。深度与 fork 种子注入复用既有的 agent 和会话词汇:
|
||||
|
||||
- **委派深度**由持久 `SessionHeader.delegationDepth` 与可合并扩展的运行时字段 `AgentOptions.subagentDepth` 共同表示;缺失表示顶层深度为零,存在的较大值具有权威性。两个字段都归该 seam 所有——循环既不设置也不读取它们——因此进程内子 agent 会持久保存 parent 深度 + 1,resume 无法降低深度,而且每次 start 都会拒绝超出安全整数域、或高于已定义绝对 `request.maxDepth` 上限的派生深度。
|
||||
- **委派深度**由持久 `SessionHeader.delegationDepth` 与可合并扩展的运行时字段 `AgentOptions.subagentDepth` 共同表示;缺失表示顶层深度为零,存在的较大值具有权威性。两个字段都归该 seam 所有——循环既不设置也不读取它们——因此进程内子 agent 会持久保存 parent 深度 + 1,恢复无法降低深度,而且每次 start 都会拒绝超出安全整数域、或高于已定义绝对 `request.maxDepth` 上限的派生深度。
|
||||
- **Fork 种子注入**使用 `CreateAgentOptions.seed`(一个 `SessionEvent[]` 前缀,经由 `AgentLoop.createAgent` → `ctx.sessions.prepare({ seed })` 传递,与 `resume` 使用的原语相同)。fork 后端传入父级日志的一段*平衡的已完成轮次前缀*——父级事件直到并包括其最后一个 `turn/end`——因此种子从 0 连续,[invariants](../../packages/support/invariants) 回放可以接受它(进行中的、未平衡的轮次被排除在外)。
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
system-prompt.md: 63a750c74300b4353f132d3dae9da52a10631f23
|
||||
system-prompt.zh.md: 748e9e4406fe8ca1071aa87f5926bd043378ccdb
|
||||
system-prompt.zh.md: e04510dbf8b97d01a568ffbd878de691be267272
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](system-prompt.md) | 中文
|
||||
|
||||
[system-prompt 包](../../packages/core/system-prompt)负责管理 prompt 贡献者与一次组装调用之间交换的数据。该包的 [README](../../packages/core/system-prompt/README.md) 记录了注册、排序、作用域与渲染行为;本页固定各插件实现或传递的跨包字面形状。
|
||||
[system-prompt 包](../../packages/core/system-prompt)负责管理提示词贡献者与一次组装调用之间交换的数据。该包的 [README](../../packages/core/system-prompt/README.md) 记录了注册、排序、作用域与渲染行为;本页固定各插件实现或传递的跨包字面形状。
|
||||
|
||||
源码:[`packages/core/system-prompt/src/index.ts`](../../packages/core/system-prompt/src/index.ts)。
|
||||
|
||||
@@ -37,7 +37,7 @@ interface ToolProviderResult {
|
||||
}
|
||||
```
|
||||
|
||||
## Prompt 段落
|
||||
## 提示词段落
|
||||
|
||||
`PromptSection` 是一份只读的同进程注册契约。其文本可以是静态的,也可以从当前组装上下文动态解析。
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
tools.md: ce14a37da33f89b8b90d6d8e70756f94e3d690dd
|
||||
tools.zh.md: 869480fc38688a15a8681648903e1a4a0959c79d
|
||||
tools.zh.md: c85ebe4d77d60e83eadb91aa5dbddd6009913614
|
||||
@@ -310,7 +310,7 @@ type PostToolDecision =
|
||||
|
||||
调用 `next()` 获取默认决策,或直接返回一个决策以短路。前置策略可以 deny 或 ask;只有 `allowed-once` 才继续执行,而未授权、缺少审批通道或服务、或无 agent 的请求都会变为拒绝。Guard 仍可施加最终拒绝。参数不可被改写,因为历史记录、审计、UI 和执行必须保持一致。
|
||||
|
||||
后置策略可以替换内容;block 会变为包含纠正反馈的 `isError` 结果。`tools/result` 在归一化后接收冻结的执行和结果;观察者无法对其进行变换,观察者的失败也会被隔离。未知工具和抛出异常的工具都会变为结构化错误(`ToolNotFoundError` 映射为 `UNKNOWN_TOOL`),调用失败但不终止当前轮次。
|
||||
后置策略可以替换内容;块会变为包含纠正反馈的 `isError` 结果。`tools/result` 在归一化后接收冻结的执行和结果;观察者无法对其进行变换,观察者的失败也会被隔离。未知工具和抛出异常的工具都会变为结构化错误(`ToolNotFoundError` 映射为 `UNKNOWN_TOOL`),调用失败但不终止当前轮次。
|
||||
|
||||
## 结构化输出 schema 子集
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
web.md: 20d07240c9d9fea2f1f5abbac810f349a3e81f9b
|
||||
web.zh.md: d4d9259db5c834349c1cf9c72ed5c74b0a02b32b
|
||||
web.zh.md: ab786f83457c16202f879be58ff55885523f874b
|
||||
@@ -8,7 +8,7 @@ Web 访问 seam 是一个[能力 seam](../../.agents/notes/implemented/architect
|
||||
|
||||
## 为什么两项能力合为一个 seam
|
||||
|
||||
搜索与抓取既不共享请求 schema,也不共享业务逻辑,但它们被有意设计为同一个 `ctx.web` 中间层:一个提供方选择策略的所有者、一套 abort/error 词汇、一个面向产品的「此 harness 如何访问 Web」配置界面。代价是服务上并行的 `searchX`/`fetchX` 方法对;这种并行是有意为之,而非遗漏的提取。提供方注册的是**能力**(`WebSearchProvider` 或 `WebFetchProvider`),而非工具;面向模型的名称、schema、prompt 引导与展示全部集中在唯一的消费方 `dsh-tool-web` 中。
|
||||
搜索与抓取既不共享请求 schema,也不共享业务逻辑,但它们被有意设计为同一个 `ctx.web` 中间层:一个提供方选择策略的所有者、一套 abort/error 词汇、一个面向产品的「此 harness 如何访问 Web」配置界面。代价是服务上并行的 `searchX`/`fetchX` 方法对;这种并行是有意为之,而非遗漏的提取。提供方注册的是**能力**(`WebSearchProvider` 或 `WebFetchProvider`),而非工具;面向模型的名称、schema、提示词引导与展示全部集中在唯一的消费方 `dsh-tool-web` 中。
|
||||
|
||||
## 搜索请求与结果
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
workflow.md: 8d271b89e71de6f6bef548aa8da61402ef9ada6e
|
||||
workflow.zh.md: 335f08cefe057bdc0d0f78a90e8301d5e457aae7
|
||||
workflow.zh.md: f603a50fc33fb9ab4260e8281ee04e892f677255
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
工作流 seam:一个 agent(智能体)运行由模型编写的编排脚本(SCRIPT),扇出 subagent。与 [subagent](subagent.md) 一样,它是**一项可选能力**,不属于 agent loop(智能体循环)主干,因此其词汇定义在此处而非 [core.md](core.md)。与 subagent 注册表不同,它采用 bash 形态:每个上下文只有一个引擎实现提供 `ctx.workflows`;没有命名提供方注册表(第二个引擎是插件替换,而非共存)。
|
||||
|
||||
接口:[dsh-workflow](../../packages/workflow/workflow)(`ctx.workflows` + 下文词汇)。实现是 [dsh-workflow-workerthread](../../packages/workflow/workflow-workerthread)(一个 `node:worker_threads` 引擎——每个 run 一个 worker,脚本的 vm context 位于其中);面向模型的消费方是 [dsh-tool-workflow](../../packages/workflow/tool-workflow)。提案与设计理由见 [dynamic-workflows Agent Note(agent 决策记录)](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。
|
||||
接口:[dsh-workflow](../../packages/workflow/workflow)(`ctx.workflows` + 下文词汇)。实现是 [dsh-workflow-workerthread](../../packages/workflow/workflow-workerthread)(一个 `node:worker_threads` 引擎——每个 run 一个 worker,脚本的 vm 上下文位于其中);面向模型的消费方是 [dsh-tool-workflow](../../packages/workflow/tool-workflow)。提案与设计理由见 [dynamic-workflows Agent Note(agent 决策记录)](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。
|
||||
|
||||
源码:[`packages/workflow/workflow/src/types.ts`](../../packages/workflow/workflow/src/types.ts)
|
||||
|
||||
## 启动请求
|
||||
|
||||
调用方启动 run 时提出的请求。普通 workflow 工具根据模型的 `{ script, meta, args }` 调用与发起调用的 agent 构建它;专用消费方还可以为该 run 选择一个引擎级 `subagentProvider` 并调低 `maxTotalAgents`,但脚本无法观察或替换这两项策略。`meta` 与 `args` 是普通 JSON 数据(引擎会对 `meta` 做形状校验,并在任何内容运行前大声拒绝——绝不会通过求值脚本文本来获取它)。`parent` 是必填字段——脚本生成的每个子 agent 都归属于它(cwd、谱系与深度通过 [subagent seam](subagent.md) 流转)。
|
||||
调用方启动 run 时提出的请求。普通工作流工具根据模型的 `{ script, meta, args }` 调用与发起调用的 agent 构建它;专用消费方还可以为该 run 选择一个引擎级 `subagentProvider` 并调低 `maxTotalAgents`,但脚本无法观察或替换这两项策略。`meta` 与 `args` 是普通 JSON 数据(引擎会对 `meta` 做形状校验,并在任何内容运行前大声拒绝——绝不会通过求值脚本文本来获取它)。`parent` 是必填字段——脚本生成的每个子 agent 都归属于它(cwd、谱系与深度通过 [subagent seam](subagent.md) 流转)。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
0001-acp-default-export-drops-inject.md: ab3efc880cb5290dc149b6bacb276ccf581968c1
|
||||
0001-acp-default-export-drops-inject.zh.md: caf60dc086e892af4ae3563b0bf073c7e6373602
|
||||
0001-acp-default-export-drops-inject.zh.md: 38bbecb4444eef1a7cdc4f48235bb83d597ce1e5
|
||||
@@ -76,7 +76,7 @@ while (true) {
|
||||
|
||||
遍历**仅向祖先方向**进行。`sessionPersistence` 既不在 `AgentLoop` 的 fiber store 中(不在其 `static inject` 中),也不在通往 root 的任何祖先上(它位于一个*兄弟*分支),因此遍历到达根 fiber 后抛错。
|
||||
|
||||
为什么内存中的 `AgentLoop` resume 测试没有捕获这个问题?因为它们从测试代码直接调用 `ctx.agents.resume(...)`——*在任何插件 fiber 之外*。此时 `ctx.fiber.runtime` 为 `null`,代理处理器走了一条提前绕过的路径:
|
||||
为什么内存中的 `AgentLoop` 恢复测试没有捕获这个问题?因为它们从测试代码直接调用 `ctx.agents.resume(...)`——*在任何插件 fiber 之外*。此时 `ctx.fiber.runtime` 为 `null`,代理处理器走了一条提前绕过的路径:
|
||||
|
||||
```ts ignore-check
|
||||
if (!ctx.fiber.runtime) return ctx.reflect.get(prop, false) // ← direct global-store lookup, no fiber walk
|
||||
@@ -91,7 +91,7 @@ if (!ctx.fiber.runtime) return ctx.reflect.get(prop, false) // ← direct glob
|
||||
两个 bug 共享同一个流程缺口:**没有任何测试通过插件的真实加载路径或真实调用拓扑来驱动它。**
|
||||
|
||||
- 内存 harness 通过手动构建插件对象来挂载 bridge:`ctx.plugin({ name, inject, apply })`。这手动提供了 `inject`,因此永远无法复现 Bug #1——`unwrapExports` 只被 *Loader* 调用,`ctx.plugin` 从不调用它。即使 `ctx.plugin(NamespaceImport)` 也无法捕获。
|
||||
- 同一个 harness 将所有内容平铺挂载在一个根上下文上,因此从中触达的 `AgentLoop` resume 要么运行在顶层(`!runtime` 绕过),要么通过一个 origin 仍然解析在 root 上的 shadow——掩盖了 Bug #2 的祖先遍历失败。
|
||||
- 同一个 harness 将所有内容平铺挂载在一个根上下文上,因此从中触达的 `AgentLoop` 恢复要么运行在顶层(`!runtime` 绕过),要么通过一个 origin 仍然解析在 root 上的 shadow——掩盖了 Bug #2 的祖先遍历失败。
|
||||
- 唯一的无 key e2e 发送 `initialize` 并检查 stdout 纯净性。`initialize` 从不触达 factory,因此两个 bug 都安然通过。
|
||||
- 唯一驱动 `session/new`/`session/load` 的测试需要 key 才能运行,因此 CI(无 key)跳过了它——而本地它之所以「通过」,只是因为一个陈旧的已构建 `lib/`(包含旧代码)恰好满足了模块解析。
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
0002-js-expression-disabled-filesystem-tools.md: 30ff9d920821a8d55c4bea5f120f1aeeca6634b3
|
||||
0002-js-expression-disabled-filesystem-tools.zh.md: d171ce7fe0fea830375f7494be2aac38630c8d6a
|
||||
0002-js-expression-disabled-filesystem-tools.zh.md: 7b7c34bb77a141eaef383fd9a655caff2a484ce1
|
||||
@@ -35,7 +35,7 @@ Cordis Include 将每个 `!!js` 标量解析为一个表达式对象。Loader
|
||||
|
||||
## 已添加的防护措施
|
||||
|
||||
- 文件系统场景启动 `fs.cordis.yml`:一个显式的固定全权限 overlay,配有对应的 replay 配置和独立的 request-header 类。
|
||||
- 文件系统场景启动 `fs.cordis.yml`:一个显式的固定全权限 overlay,配有对应的回放配置和独立的 request-header 类。
|
||||
- [`AGENTS.md`](../../AGENTS.md) 与 [Cordis 入门](../cordis-primer.md#loader-configuration)明确说明 `!!js` 仅在插件 `config` 内有效,条件式组合应使用 overlay。
|
||||
- `verify-cordis-config` 解析仓库中的 Cordis YAML,拒绝 Loader 入口元数据中的表达式节点(包括 include patch 和插入的入口)。
|
||||
- `dsh-acp-snapshot` 在新鲜运行和已提交的会话 fixture 中拒绝结构化的 `UNKNOWN_TOOL` 结果,防止其被提交为预期输出。
|
||||
|
||||
Reference in New Issue
Block a user