Merge remote-tracking branch 'origin/master' into xtr/react-loop-simplification
# Conflicts: # .agents/notes/implemented/architecture/2026-07-12-agent-scope-runtime-design.i18n.yaml # .agents/notes/implemented/architecture/2026-07-31-goal-owned-durable-events.i18n.yaml # .agents/notes/implemented/feature/2026-07-19-model-facing-goal-tools.i18n.yaml # .agents/notes/implemented/feature/2026-07-22-docked-web-goal-bar.i18n.yaml # docs/architecture.i18n.yaml # docs/architecture.md # docs/architecture.zh.md # docs/config-catalog.md # docs/cordis-catalog/events.md # docs/cordis-catalog/services.md # docs/core-data-structures/core.i18n.yaml # docs/core-data-structures/core.md # docs/core-data-structures/core.zh.md # docs/event-producer-consumer.md # docs/module-graph.md # docs/persistence-catalog.md # examples/acp-agent/tests/snapshots/advanced-toolchain/session.1.jsonl # examples/acp-agent/tests/snapshots/advanced-toolchain/session.2.jsonl # examples/acp-agent/tests/snapshots/code-mode-workspace-context/session.jsonl # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl # examples/acp-agent/tests/snapshots/skill-load/session.jsonl # examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/session.1.jsonl # examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/session.2.jsonl # examples/acp-agent/tests/snapshots/subagent-fork/session.1.jsonl # examples/acp-agent/tests/snapshots/subagent-mixed/session.1.jsonl # examples/acp-agent/tests/snapshots/subagent-mixed/session.2.jsonl # examples/acp-agent/tests/snapshots/subagent-multi/session.1.jsonl # examples/acp-agent/tests/snapshots/subagent-multi/session.2.jsonl # examples/acp-agent/tests/snapshots/subagent-spawn/session.1.jsonl # examples/acp-agent/tests/snapshots/workflow-run/session.1.jsonl # examples/acp-agent/tests/snapshots/workspace-context/session.jsonl # examples/headless-agent/tests/snapshots/advanced-toolchain/session.1.jsonl # examples/headless-agent/tests/snapshots/advanced-toolchain/session.2.jsonl # examples/headless-agent/tests/snapshots/advanced-toolchain/session.jsonl # examples/headless-agent/tests/snapshots/pty-tools/session.jsonl # examples/headless-agent/tests/subagent-inheritance-snapshots/parent-override/child.expected.jsonl # examples/jsonrpc-agent/tests/snapshots/subagent-spawn/notifications.expected.jsonl # examples/jsonrpc-agent/tests/snapshots/subagent-spawn/session.1.jsonl # packages/acp/acp/README.i18n.yaml # packages/acp/acp/src/index.ts # packages/client/connection/src/client/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/runtime/src/client/sessions/session.ts # packages/client/runtime/tests/queue-store.spec.ts # packages/client/ui-conversation/src/client/input/contract.ts # packages/client/ui-goal/README.i18n.yaml # packages/compact/compact-basic/src/index.ts # packages/context/time-context/tests/time-context.spec.ts # packages/context/tmux-context/tests/tmux-context.spec.ts # packages/context/workspace-context/tests/workspace-context.spec.ts # packages/cordis/tool-cordis/src/api-catalog.ts # packages/core/agent-loop/README.i18n.yaml # packages/core/agent-loop/README.md # packages/core/agent-loop/README.zh.md # packages/core/agent-loop/src/agent.ts # packages/core/agent-loop/tests/agent.spec.ts # packages/core/agent-loop/tests/loop.spec.ts # packages/core/agent/README.i18n.yaml # packages/core/agent/README.md # packages/core/agent/README.zh.md # packages/core/agent/src/types.ts # packages/core/session/README.i18n.yaml # packages/core/session/README.md # packages/core/session/README.zh.md # packages/goal/command-goal/tests/command-goal.spec.ts # packages/goal/goal/tests/goal.spec.ts # packages/goal/tool-goal/README.i18n.yaml # packages/goal/tool-goal/README.md # packages/goal/tool-goal/README.zh.md # packages/host/apiproxy/README.i18n.yaml # packages/host/apiproxy/README.md # packages/host/apiproxy/README.zh.md # packages/host/apiproxy/src/api-proxy.ts # packages/host/apiproxy/src/api/events.schema.ts # packages/host/apiproxy/src/api/events.ts # packages/host/apiproxy/src/api/rpc.ts # packages/host/apiproxy/tests/api-proxy-commands.spec.ts # packages/host/apiproxy/tests/api-proxy-projections.spec.ts # packages/host/apiproxy/tests/rpc-schemas.spec.ts # packages/pty/pty-local/tests/index.spec.ts # packages/pty/pty-local/tests/local.spec.ts # packages/pty/tool-pty/tests/loader-composition.spec.ts # packages/pty/tool-pty/tests/tools.spec.ts # packages/session-persistence/session-checkpoint-policy/src/index.ts # packages/session-persistence/session-persistence/tests/coordinator-contract.ts # packages/skill/tool-skill/tests/tool-skill.spec.ts # packages/subagent/subagent-inprocess/README.i18n.yaml # packages/subagent/subagent-inprocess/README.md # packages/subagent/subagent-inprocess/README.zh.md # packages/subagent/subagent-inprocess/src/index.ts # packages/subagent/subagent-inprocess/tests/subagent-inprocess.spec.ts # packages/support/acp-snapshot/src/harness.ts # packages/ui/tui/tests/tui.spec.ts
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# 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 .agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md
|
||||
2026-07-26-merge-subagent-control-service.md: ae7214f7c8c28f8a4b7d754839acbc938270f125
|
||||
2026-07-26-merge-subagent-control-service.zh.md: dc05e03c5d38df0317291aee2ffc1395dab8e362
|
||||
@@ -0,0 +1,41 @@
|
||||
# Agent Note: Merge subagent control into the subagent service
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-26-merge-subagent-control-service.zh.md)
|
||||
|
||||
The public operation set is refined by [Intent-named subagent continuation operations](2026-07-27-intent-named-subagent-continuation-operations.md) and again by [Continuable subagents](../feature/2026-07-28-continuable-subagent-conversations.md), which keeps the single merged service while removing provider `resume` dispatch and the Task-backed continuation lifecycle.
|
||||
|
||||
## Problem
|
||||
|
||||
Continuable-child orchestration originally lived in a separate `ctx.subagentControl` service above the raw `ctx.subagents` provider seam. That split kept provider dispatch independent of Tasks and persistence, and gave model and human adapters one orchestration contract. In practice the two services described one capability family, every continuable caller needed both, and the provider-bound delegation tool had to infer policy from `provider.resume` and inspect whether the control service and `send_message` tool happened to be loaded. This made sibling plugin presence decide execution semantics and coupled starting continuable work to an optional follow-up surface.
|
||||
|
||||
## Decision
|
||||
|
||||
`SubagentService` is the only public service. It exposes ordinary `start(name, request)`, Task-backed `startContinuable(spec)`, and intent-named `followup(...)`; provider resume dispatch remains private to its continuation manager. The standalone `@deepseek-ai/dsh-subagent-control` package and `ctx.subagentControl` key are absent; the optional `@deepseek-ai/dsh-tool-subagent-control` package injects `ctx.subagents` directly.
|
||||
|
||||
The merged service and its providers expose one `SubagentError` taxonomy. Stable codes distinguish provider lookup and capability failures from continuation routing, authorization, cancellation, persistence, and delivery failures; the removed service does not retain a separate error class.
|
||||
|
||||
The continuation implementation remains an internal manager rather than expanding the provider registry's core state. `SubagentService` creates it through `ctx.inject(['tasks', 'agents'], ...)`, so the injected Cordis child fiber owns its Task completion listener and teardown effects. Loading the provider registry does not require Tasks or persistence. The manager exists only while Tasks and Agents are available, and each continuation operation resolves session persistence at the point it needs durability. Disposing that fiber cancels and settles active continuations before releasing their associations.
|
||||
|
||||
`startContinuable` remains distinct from raw `start` because it has a different ownership and timing contract: it allocates the durable child id, creates the Task, and returns both ids synchronously while startup continues inside the Task. Raw `start` instead awaits provider publication and transfers a holder-owned run. Folding the method onto `start` through flags or return unions would broaden the low-level contract and create more change than keeping the existing explicit entry.
|
||||
|
||||
Each `@deepseek-ai/dsh-tool-subagent` instance selects `backgroundMode: 'one-shot' | 'continuable'`, defaulting to `one-shot`. This configuration is policy; `provider.resume` is only the capability check for configured continuable mode. A resumable provider can therefore still run one-shot background work. The `send_message` tool is an independent adapter: loading or omitting it neither enables nor disables `startContinuable`.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep the separate service.** This preserves the strongest dependency separation, but every production continuable path composes both services and the extra public key exposes an architectural distinction callers do not need. The internal manager preserves optional Task and persistence dependencies without a second service.
|
||||
|
||||
**Infer continuable mode from `provider.resume`.** Method presence correctly states cold-resume capability but not deployment policy. It forced every resumable provider into continuable background semantics and made missing sibling plugins a runtime error. Explicit tool configuration separates choice from capability.
|
||||
|
||||
**Register continuation access or inspect the follow-up tool.** A registry could tell the delegation tool whether a continuation surface exists, but starting durable work does not require any follow-up adapter. Such a registry would encode UI composition into execution policy and recreate the sibling dependency under another name.
|
||||
|
||||
**Merge raw and continuable starts into one method.** A flag on `start` would return either a published one-shot run or immediate Task and child identities, weakening a simple ownership boundary. Keeping `startContinuable` is the smaller change and preserves both contracts explicitly.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The service topology has one public key and one package fewer while raw provider dispatch remains usable without Tasks or persistence.
|
||||
- Continuable mode fails at provider mount when the configured provider lacks `resume`; missing Tasks, Agents, or persistence still fail at the earliest operation that requires them.
|
||||
- Follow-up delivery remains optional. Deployments may start and collect continuable work through Task tools without exposing `send_message`.
|
||||
- The continuation manager is still Task- and persistence-aware inside the `dsh-subagent` package, so the package declares optional peer dependencies on those services even though ordinary `start` callers do not need them.
|
||||
- Existing continuation races, authorization, durability, cancellation, and settle-then-dispose semantics are unchanged and remain pinned by the migrated `subagent` tests.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Agent Note: 将 subagent 控制合并到 subagent 服务
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-26-merge-subagent-control-service.md) | 中文
|
||||
|
||||
公开操作集合由[以意图命名的 subagent 继续执行操作](2026-07-27-intent-named-subagent-continuation-operations.md)进一步细化,并由[可继续的 subagent](../feature/2026-07-28-continuable-subagent-conversations.md)再次细化——后者保留这一个合并后的服务,同时移除提供方 `resume` 派发和基于 Task 的继续执行生命周期。
|
||||
|
||||
## 问题
|
||||
|
||||
可继续 child 的编排最初位于原始 `ctx.subagents` 提供方 seam 之上的独立 `ctx.subagentControl` 服务中。该拆分使提供方分发与 Task 和持久化无关,并为模型与人工适配器提供统一的编排契约。实践中,两个服务属于同一组功能,每个可继续调用方都需要二者,而绑定提供方的委派工具必须根据 `provider.resume` 推断策略,并检查控制服务与 `send_message` 工具是否碰巧已加载。如此一来,配套插件是否存在会决定执行语义,并将可继续工作的启动耦合到可选的后续操作接口。
|
||||
|
||||
## 决策
|
||||
|
||||
`SubagentService` 是唯一的公开服务。它公开普通的 `start(name, request)`、由 Task 支撑的 `startContinuable(spec)`,以及按意图命名的 `followup(...)`;提供方的 resume 分发仍封装在其继续执行管理器内部。独立的 `@deepseek-ai/dsh-subagent-control` 包(package)和 `ctx.subagentControl` 键均不存在;可选的 `@deepseek-ai/dsh-tool-subagent-control` 包则直接注入 `ctx.subagents`。
|
||||
|
||||
合并后的服务及其提供方公开一套 `SubagentError` 分类体系。稳定错误码把提供方查找失败和功能检查失败,与继续执行路由、鉴权、取消、持久化和送达失败区分开来;已移除的服务不保留单独的错误类。
|
||||
|
||||
继续执行的实现仍是内部管理器,不会扩展提供方注册表的核心状态。`SubagentService` 通过 `ctx.inject(['tasks', 'agents'], ...)` 创建该管理器,因此注入的 Cordis child fiber 拥有自身的 Task 完成监听器和拆卸 effect。加载提供方注册表不要求 Task 或持久化。只有 Task 和 Agent 可用时,该管理器才会存在;每项继续执行操作都在需要持久性时解析会话持久化服务。dispose(资源释放)该 fiber 会先取消并结算活跃的继续执行,再释放其关联。
|
||||
|
||||
`startContinuable` 与底层 `start` 保持分离,因为二者的所有权与时序契约不同:前者分配持久化 child id、创建 Task,并同步返回两个 id,而启动过程继续在 Task 内运行;底层 `start` 则等待提供方发布,并移交一个由持有方负责的 run。若通过标志或返回值联合类型将该方法并入 `start`,会扩大底层契约,改动反而多于保留现有的显式入口。
|
||||
|
||||
每个 `@deepseek-ai/dsh-tool-subagent` 实例都会选择 `backgroundMode: 'one-shot' | 'continuable'`,默认值为 `one-shot`。这项配置表示策略;`provider.resume` 只用于检查所配置的可继续模式是否受提供方支持。因此,可恢复的提供方仍可执行一次性后台工作。`send_message` 工具是独立适配器:加载或省略该工具既不会启用也不会禁用 `startContinuable`。
|
||||
|
||||
## 已考虑的替代方案
|
||||
|
||||
**保留独立服务。** 这样能保持最严格的依赖分离,但每条生产环境中的可继续路径都要组合两个服务,而额外的公开键会暴露调用方并不需要的架构差异。内部管理器无需第二个服务,也能保留可选的 Task 和持久化依赖。
|
||||
|
||||
**根据 `provider.resume` 推断可继续模式。** 方法是否存在可以准确表示从持久化存储恢复的功能,却不能表示部署策略。这会迫使每个可恢复的提供方都采用可继续后台语义,并使配套插件缺失成为运行时错误。显式的工具配置将选择与功能分离。
|
||||
|
||||
**注册继续执行访问入口,或检查后续操作工具。** 注册表可以告诉委派工具继续执行接口是否存在,但启动具备持久性的工作不需要任何后续操作适配器。这样的注册表会把 UI 组合编码进执行策略,并以另一个名称重新建立插件间依赖关系。
|
||||
|
||||
**将底层启动与可继续启动合并为一个方法。** `start` 上的标志会使该方法返回已发布的一次性 run,或立即返回 Task 和 child 标识,从而削弱简单的所有权边界。保留 `startContinuable` 改动更小,也能明确保留两项契约。
|
||||
|
||||
## 影响
|
||||
|
||||
- 服务拓扑少了一个公开键和一个包,同时底层提供方分发仍可在没有 Task 或持久化时使用。
|
||||
- 配置的提供方缺少 `resume` 时,可继续模式会在提供方挂载阶段失败;缺少 Task、Agent 或持久化时,仍会在需要它们的最早操作处失败。
|
||||
- 后续消息投递仍为可选功能。部署可以通过 Task 工具启动并收集可继续工作,而不公开 `send_message`。
|
||||
- `dsh-subagent` 包内的继续执行管理器仍然感知 Task 和持久化,因此该包会将这些服务声明为可选的对等依赖(peer dependency),即使普通的 `start` 调用方并不需要它们。
|
||||
- 现有的继续执行竞态、授权、持久性、取消及先结算再 dispose 的语义均保持不变,并继续由迁移后的 `subagent` 测试固定。
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# 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 .agents/notes/implemented/simplification/2026-07-27-intent-named-subagent-continuation-operations.md
|
||||
2026-07-27-intent-named-subagent-continuation-operations.md: e74d62b7582e92f8e5ce68327a677259c8453d24
|
||||
2026-07-27-intent-named-subagent-continuation-operations.zh.md: dae4dd37fa9950f0b8d1ba6ec5c46b99977a7201
|
||||
@@ -0,0 +1,38 @@
|
||||
# Agent Note: Intent-named subagent continuation operations
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-27-intent-named-subagent-continuation-operations.zh.md)
|
||||
|
||||
The current activation-based realization is owned by [Continuable subagents](../feature/2026-07-28-continuable-subagent-conversations.md). It retains the `followup` operation this record names, returns the accepted `MessageId`, uses the bare `Agent` parameter as exact live-direct-parent authority, and limits provider participation in continuable children to `prepareContinuable`.
|
||||
|
||||
## Problem
|
||||
|
||||
Merging continuable-child orchestration into `ctx.subagents` left provider dispatch and caller intent on the same public service. `resume(name, request)` accepted a descriptor, authorized parent, durable child id, and activation signal that only the internal continuation manager could resolve correctly. `sendMessage(...)` exposed transport wording rather than the `followup` intent already used by `Agent`, and its separate source and signal parameters widened an operation every caller had to use atomically.
|
||||
|
||||
The durability boundary also exposed both `SessionStore.flush()` and `flushRequired()`. They performed the same scoped parallel dispatch and differed only in whether an empty listener snapshot was accepted, so the session interface encoded one consumer's policy as a second operation.
|
||||
|
||||
## Decision
|
||||
|
||||
`SubagentService` separates four execution intents: `start(name, request)` returns an ordinary holder-owned one-shot run; `startContinuable(spec)` establishes a durable child and returns its id plus the accepted initial `MessageId`; `followup(parent, childId, content, { source, signal })` sends later parent content; and `reportFrom(child, content, { delivery, signal })` sends selected child content to its direct parent. `followup` matches `Agent.followup()`, while `SubagentRun.steer()` remains the narrower confirmed live-run capability. The model-facing tools keep their stable `send_message` and `report` names and delegate routing to the corresponding intent methods.
|
||||
|
||||
Caller and provider requests are distinct. `SubagentStartRequest` contains caller-supplied one-shot data; `ResolvedSubagentStartRequest` adds the service-resolved descriptor before `SubagentProvider.start()`. For continuable creation, the manager passes a `ContinuableCreateRequest` to optional `SubagentProvider.prepareContinuable()` and receives detached creation data only. `SubagentService.resume()` and provider resume dispatch are absent: the continuation manager loads the descriptor, authorizes the parent, and owns Agent materialization, prompt delivery, cold resume, and teardown.
|
||||
|
||||
`SessionStore.flush(session)` is the single durability barrier and returns `Promise<boolean>`. It resolves `true` after at least one scoped listener participates successfully, resolves `false` for an empty listener snapshot, and rejects with the first registered listener failure after all listeners settle. Participation cannot identify whether a selected persistence backend stored the state. Ordinary checkpoints may ignore the boolean; the continuation manager also treats its final flush as a best-effort barrier, deliberately ignores participation, logs rejection, and still disposes the child and releases ownership.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep public provider resume dispatch.** No production caller outside the continuation manager owns descriptor lookup, direct-parent authorization, Agent materialization, Activation ownership, and child-first teardown. A public method would expose resolved implementation data without a valid independent intent; providers instead contribute detached first-creation data through `prepareContinuable` and never participate in cold resume.
|
||||
|
||||
**Keep `sendMessage` on the service.** The model tool sends a message, but the service operation represents a follow-up that may steer or cold-resume. `followup` aligns with the structural `Agent` interface and does not promise a particular route.
|
||||
|
||||
**Keep `flushRequired()`.** A second method hides only an empty-listener check. Returning participation from the existing barrier keeps dispatch in one implementation and lets each caller state whether absence is acceptable.
|
||||
|
||||
**Fold ordinary and continuable starts together.** A flag would make one method return either an awaited holder-owned one-shot run or immediate durable child and message identities. Separate intent methods preserve the ownership and timing distinction without a return union.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The Cordis service catalog contains only caller operations; a provider can opt into continuable first creation through `SubagentProvider.prepareContinuable?()` without receiving Agent lifecycle authority or a public resume operation.
|
||||
- Follow-up source and cancellation travel in one options object, matching the intent-helper shape on `Agent` while retaining the existing live-delivery and cold-resume semantics.
|
||||
- Session durability has one barrier operation. Its participation result remains observable, but no continuable-child path treats arbitrary listener participation as proof that a persistence backend stored the state.
|
||||
- The `send_message` and `report` schemas, accepted message identities, `AgentHandle` ownership, durable event vocabulary, and model-visible transcript follow the activation-based realization linked above.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Agent Note: 按意图命名的 subagent 继续执行操作
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-27-intent-named-subagent-continuation-operations.md) | 中文
|
||||
|
||||
当前基于 Activation 的实现由[可继续的 subagent](../feature/2026-07-28-continuable-subagent-conversations.md)负责。它保留本记录命名的 `followup` 操作,返回已接受的 `MessageId`,使用裸 `Agent` 参数作为确切的在线直属父级权限,并将提供方对可继续 child 的参与限制为 `prepareContinuable`。
|
||||
|
||||
## 问题
|
||||
|
||||
将可继续 child 的编排合并到 `ctx.subagents` 后,提供方分发与调用方意图共存于同一个公开服务中。`resume(name, request)` 接受描述符、已鉴权的 parent、持久化 child id 与激活信号,而只有内部继续执行管理器才能正确解析这些数据。`sendMessage(...)` 暴露的是传输层措辞,而不是 `Agent` 已采用的 `followup` 意图;它还将来源与信号拆成独立参数,扩大了操作接口,而每个调用方都必须以原子方式同时使用二者。
|
||||
|
||||
持久性边界还同时公开了 `SessionStore.flush()` 与 `flushRequired()`。二者执行相同的作用域内并行分发,唯一差别是是否接受空的监听器快照,因此会话接口将一个消费方的策略编码为第二项操作。
|
||||
|
||||
## 决策
|
||||
|
||||
`SubagentService` 分离四种执行意图:`start(name, request)` 返回普通的、由持有方负责的 one-shot run;`startContinuable(spec)` 建立持久化 child,并返回其 id 与已接受的初始 `MessageId`;`followup(parent, childId, content, { source, signal })` 发送后续 parent 内容;`reportFrom(child, content, { delivery, signal })` 将选定的 child 内容发送给其直接 parent。`followup` 与 `Agent.followup()` 一致,而 `SubagentRun.steer()` 仍是范围更窄的能力,仅向已确认仍在运行的 run 提供 steering。面向模型的工具保留稳定的 `send_message` 与 `report` 名称,并将路由委托给对应的意图方法。
|
||||
|
||||
调用方请求与提供方请求相互分离。`SubagentStartRequest` 包含调用方提供的 one-shot 数据;`ResolvedSubagentStartRequest` 会在调用 `SubagentProvider.start()` 前加入由服务解析的描述符。创建可继续 child 时,管理器将 `ContinuableCreateRequest` 传给可选的 `SubagentProvider.prepareContinuable()`,且只接收分离的创建数据。`SubagentService.resume()` 与提供方恢复分发均不存在:继续执行管理器加载描述符、对 parent 进行鉴权,并负责 Agent 实体化、提示词投递、冷恢复与 teardown。
|
||||
|
||||
`SessionStore.flush(session)` 是唯一的持久性屏障,并返回 `Promise<boolean>`。至少一个作用域内监听器成功参与后,它解析为 `true`;监听器快照为空时解析为 `false`;所有监听器结算后,如有失败,则以注册顺序最靠前的监听器错误拒绝。参与结果无法表明所选的持久化后端是否已经存储状态。普通检查点可以忽略该布尔值;继续执行管理器同样将最终 flush 视为 best-effort 屏障,有意忽略参与结果,记录拒绝日志,并仍会对 child 执行 dispose(资源释放)并释放所有权。
|
||||
|
||||
## 已考虑的替代方案
|
||||
|
||||
**保留公开的提供方恢复分发。** 继续执行管理器之外,没有任何生产调用方同时负责安全调用所需的描述符查找、直接 parent 鉴权、Agent 实体化、Activation 所有权与 child-first teardown。公开方法会暴露已解析的实现数据,却没有合理的独立调用意图;提供方改为通过 `prepareContinuable` 贡献分离的首次创建数据,且从不参与冷恢复。
|
||||
|
||||
**在服务上保留 `sendMessage`。** 面向模型的工具发送消息,但服务操作表达的是后续操作,既可能对运行中的激活执行 steering,也可能从持久化存储恢复。`followup` 与结构化 `Agent` 接口保持一致,也不承诺特定路由。
|
||||
|
||||
**保留 `flushRequired()`。** 第二个方法只封装了空监听器检查。由现有屏障返回是否有监听器参与,可以让分发只保留一套实现,并让每个调用方自行判定缺少监听器是否可接受。
|
||||
|
||||
**合并普通启动与可继续启动。** 一个标志会让同一方法要么等待由持有方负责的 one-shot run 就绪后返回,要么立即返回持久化 child 与消息标识。按意图拆分的方法无需返回值联合类型即可保留所有权与时序差异。
|
||||
|
||||
## 影响
|
||||
|
||||
- Cordis 服务目录只包含调用方操作;提供方可以通过 `SubagentProvider.prepareContinuable?()` 选择参与可继续 child 的首次创建,但不会获得 Agent 生命周期权限或公开恢复操作。
|
||||
- 后续操作的来源与取消信号通过同一个选项对象传递,与 `Agent` 上按意图命名的辅助方法形态一致,同时保留在线投递与从持久化存储恢复的语义。
|
||||
- 会话持久性只有一个屏障操作。参与结果仍可观测,但任何可继续 child 路径都不会将任意监听器参与视为持久化后端已存储状态的证明。
|
||||
- `send_message` 与 `report` schema、已接受的消息标识、`AgentHandle` 所有权、持久化事件词汇与模型可见的 transcript(文本记录)遵循上文链接的基于 Activation 的实现。
|
||||
Reference in New Issue
Block a user