diff --git a/docs/architecture/rfcs/harness-selection-dsh-pi-v0.md b/docs/architecture/rfcs/harness-selection-dsh-pi-v0.md index ecdf7e31ab..765a656dcf 100644 --- a/docs/architecture/rfcs/harness-selection-dsh-pi-v0.md +++ b/docs/architecture/rfcs/harness-selection-dsh-pi-v0.md @@ -13,6 +13,122 @@ The first choice minimizes the cost of qualifying an existing passive observer; the second requires lifecycle, provider, crash-recovery and outcome evidence that a plugin event fixture cannot supply. No quantitative winner is claimed. +Two DSH roles appear in this document and must not be conflated. The **bounded +managed Turn host** (LoopX's adapter choice for one governed Turn) is +credential-bound; its default-host resolution lands with the managed stack +recorded below, not with `main` today. The **L1 event source and session-owning +runtime** role stays opt-in and is not promoted by that binding; it still needs +the C0, C1, overhead, retention and Mode B rows. + +## Managed Execution Surface (2026-09-15) + +Selection is constrained by what the repository ships today, not only by what an +upstream harness can do. The managed bounded execution unit is the governed Turn: + +- `loopx turn run-once` accepts `--host codex-cli|dsh|generic-cli` with + `--execution-mode isolated-headless`: LoopX decides, a host adapter invokes the + agent CLI, an independent validator proves the postcondition, and only a passing + result is committed; +- `loopx host-mode-plan` selects `isolated_headless_turn` for the + `continue_without_ui` intent only when the host declares `typed_host_adapter`; + without that declaration it reports the mode as not ready and names the missing + capability; +- session ownership (`managed_runtime` versus `attached_host`) is not decided + here. It belongs to + [Agent Session Execution Modes](./agent-session-execution-modes-v0.md), which + also owns the M1-M4 integration milestones and the cross-frontend projection row. + +Three evidence states appear below and must not be read across them. The list is +dated 2026-09-15 and is written to land with the managed stack: + +- **already on `main` on 2026-09-15:** `loopx turn run-once --host + codex-cli|dsh|generic-cli`, the `dsh` host adapter, the `host-mode-plan` gate + above, and the dsh pin `deepseek-harness-sdk==0.1.2a3`; +- **not on `main` on 2026-09-15; expected to land with this document:** the + explicit host selection (`loopx/control_plane/turn_driver/host_binding.py`, + PR #4443), the steward channel's explicit executor selection + (`loopx/chat_manager.py`, PR #4446) and the `0.1.5rc1` dsh pin (PR #4420). A + later reader who finds those PRs merged can read those rows as shipped; a + reader who does not must treat them as stack-only; +- **local live qualification, not a repository gate:** rows marked as local + evidence below. Reproducing them needs an operator credential, and CI asserts + none of them. + +| Role | Source | Selection today | Promotion gate | +| --- | --- | --- | --- | +| Default managed execution host | LoopX Turn plus the `dsh` host adapter, bound to an operator-supplied model endpoint | shipped product default: `dsh` for bounded managed Turns, environment-independent; `LOOPX_TURN_HOST` re-points it and an explicit `--host` wins; in the managed stack (PR #4443), not yet on `main` | keep the typed host request/result, independent validation, and the operator-owned credential boundary; do not replace it without an equal or stronger contract | +| Steward channel executor | the interactive Chat transport the steward answers on | shipped product default: `codex`; `LOOPX_MANAGER_ENDPOINT` re-points it; in the managed stack (PR #4446), not yet on `main` | an interactive Chat transport on the managed host, so the steward channel can select it; a discovered credential is never the promotion signal | +| Supported alternative Turn host | LoopX Turn plus the `codex-cli` adapter | explicitly selectable; it is the `individual` executor kind, so it is billed to one person's CLI login | no managed lane may silently depend on an individual's personal CLI subscription; an individual lane must be selected, not reached by default | +| L1 event source and session-owning runtime candidate | DSH | opt-in, not promoted; the bounded Turn host role is the default row above | the C0, C1, overhead, retention and Mode B rows in this document being run and reviewed | +| Optional visible host loop | Pi | not a managed runtime | declare a per-binding session mode with readback, prove single-executor behavior under restart, "conversation is not a receipt", non-authoritative host-local state, and one real-host restart row | + +### Managed host binding and live qualification (2026-09-15) + +A managed host binding names four things: the host adapter, the provider, the +model, and where the credential comes from. The DSH binding is the DSH Turn host +with provider `deepseek-official`, model `deepseek-flash` (DeepSeek V4.1 Flash), +an endpoint from the operator environment (`DEEPSEEK_BASE_URL`) and a credential +from the operator environment (`DEEPSEEK_API_KEY`). + +LoopX **selects** the default host for bounded managed Turns and never infers it +(`loopx/control_plane/turn_driver/host_binding.py`): the shipped default is `dsh`, +`LOOPX_TURN_HOST` re-points it, and an explicit `--host` wins over both. The +operator credential is not a selection input. This distinction is the whole +point of the binding: discovering a key is not a decision to change where work +runs, and a surface that resolves its host from the environment makes a chosen +configuration indistinguishable from an incidental one. A lane selected onto the +DSH host therefore never depends on an individual developer's CLI subscription +being available, funded, or logged in. + +The steward channel is a **different** surface with a different default. Its +shipped executor is `codex`, because that is the only transport that can hold an +interactive steward session today; `LOOPX_MANAGER_ENDPOINT` re-points it, and a +credential never re-points it. Its model follows the executor it selected rather +than the environment, so a configured coordinator credential does not swap the +steward onto an operator model while the executor stays on the CLI endpoint. + +Evidence for this binding, separated by source: + +- repository-covered without any provider call: the shipped default is `dsh`, an + explicit `LOOPX_TURN_HOST` re-points it, an explicit `--host` still wins, and a + configured credential changes none of those selections (tests in PR #4443, not + yet on `main`); +- local live qualification with the real SDK and runtime + (`deepseek-harness-sdk==0.1.5rc1`, the pin PR #4420 proposes; `main` still + pins `0.1.2a3` and the same pair also passed there): the in-process + `--host dsh` path and the `generic-cli` subprocess path; +- local live qualification: one governed Turn reached `validated_progress` — the + host executed the bounded action, an independent validator proved the + postcondition, and only then did writeback and quota spend follow; +- local live qualification: a Turn whose postcondition was not proved + fail-closed instead — no writeback, and the quota slot spend count stayed at + zero. + +Open gaps before this binding is a promoted production default: + +- the runtime snapshot bundled as `deepseek-harness-runtime-bin==0.1.5rc1` + cannot boot the stock `headless` profile as shipped: a profile row pulls + `@deepseek-ai/dsh-session-title-first-prompt-llm`, which imports the omitted + `@deepseek-ai/dsh-session-title-llm`, and resolution runs inside the packaged + snapshot, so installing that package into a profile directory does not change + it. The current local workaround is a binding overlay that disables the + affected row. The managed host path is unaffected: it does not select + `headless`, and the default `sdk` profile boots and exits cleanly; +- the LoopX DSH Turn composition must name the tool rows a managed action needs + (`@deepseek-ai/dsh-tool-fs`, `@deepseek-ai/dsh-tool-bash`). Without them a live + model can answer but cannot act, and the Turn ends in a validation failure + rather than in work. +- the host-mode plan still maps the unattended intent to the compatibility path: + `isolated_headless_turn` carries `turn_host: generic-cli` + (`loopx/host_mode_planner.py`), so the `loopx turn plan` command it prints + names `--host generic-cli` instead of the selected `dsh` default recorded above, + which is correct as a labelled rollback path but is not labelled as one. The + plan's `--host-identity` list deliberately covers visible + hosts only, because a headless-only host such as `dsh` cannot own a visible + session; the unattended mapping itself still needs a decision between naming + the resolved default, offering a `dsh` variant, or labelling the emitted + command as the rollback path. + ## Evidence Baseline LoopX was inspected at `bf217e1e01bec79f357c9ecbd580cf2dfa73db8b`. @@ -29,6 +145,14 @@ The implementation paths below are repository-relative: - `apps/desktop/loopx-control-plane/src-tauri/src/services.rs`: service process management must not be mistaken for the complete managed Agent lifecycle. +The dsh pin moved in two steps, and reading this document needs both states. +`main` today pins `deepseek-harness-sdk==0.1.2a3`. The managed stack moves that +pin to the newest released upstream channel rather than an unreleased tag: +`deepseek-harness-sdk==0.1.5rc1` / `deepseek-harness-runtime-bin==0.1.5rc1` on +PyPI (PR #4420), matching `latest` for `@deepseek-ai/dsh` on npm (checked +2026-09-15). Upstream `next` and `alpha` tags are newer than that channel and are +not adopted here. + Upstream references were inspected on 2026-09-06, pinned independently of the versions validated by LoopX: @@ -60,6 +184,15 @@ These are integration-cost and contract observations, not claims that Pi lacks events or DSH cannot support other models. Both expose control-capable APIs; passivity is a property of the selected adapter and its loaded dependencies. +The two LoopX surfaces that depend on dsh do not move together. The bounded Turn +host uses the Python SDK/runtime pin recorded above (`0.1.5rc1`, the released +channel). The dsh-side plugin (`packages/dsh-loopx-plugin`) still builds its +development and client surfaces against `0.1.1-rc.2` while its clean-Docker smoke +already asserts `dsh --version == 0.1.5-rc.1`, and the 0.1.5 line no longer +publishes `@deepseek-ai/dsh-client-runtime` (last released 0.1.1-rc.2), moving +the client runner to `@deepseek-ai/dsh-cordis-client-runner`. That upgrade is +tracked as its own pin item and does not change the L1 observer contract above. + ## Data and Authority Flow The operator needs to distinguish missing evidence, unhealthy execution and @@ -134,6 +267,13 @@ Keep raw logs and credentials owner-local. Public evidence should contain only generalized methodology, pinned revisions, aggregate results and safe references. No live model execution or retention deletion is authorized by this document. +Milestone ownership stays with +[Agent Session Execution Modes](./agent-session-execution-modes-v0.md). This +document owns the C0, C1, overhead and retention evidence for the L1 observer +arm, and the Mode B acceptance above for a session-owning runtime; the M1-M4 +integration milestones and the cross-frontend projection row remain that +document's, and nothing here defines mode inference or a second executor. + ## Delivery Sequence This comparison plus combined CLI readback can be reviewed now. A Mode B panel @@ -144,3 +284,63 @@ Implement deletion only after the owner selects the retention profile. Revisit runtime preference if Pi satisfies the same isolation/lifecycle tests at lower measured integration and operational cost, or DSH fails them. Do not introduce L2 advice, retry control or a new scheduler to make an L1 experiment pass. + +## Steward Channel Chat Transport (2026-09-15) + +The governed Turn surface and the steward (manager) chat channel resolve their +default host from the same operator credential, but they need different host +shapes. A Turn is one bounded work segment, which the shipped DSH adapter +serves today. The steward channel additionally needs a transport that can hold +an interactive session, and the shipped DSH surface explicitly does not promise +cross-turn DSH session continuity. + +Behaviour in the managed stack (PR #4446, not yet on `main`): the steward channel +selects `codex` as its shipped executor and keeps the vendor model default, and an +operator credential re-points neither one -- it is reported as a fact +(`operator_credential_configured`, variable name only) and authenticates only the +endpoint that actually runs on the operator provider. Selecting the managed host +for this channel reports `available: false` with the typed +`managed_host_chat_transport_unsupported` reason, and a session request for that +host fails as the same typed host-tool gate instead of silently falling back to an +individual CLI login. Promoting the managed host to the steward default is gated +on that transport, not on a credential. + +| Option | Shape | Cost and risk | +| --- | --- | --- | +| A. Turn-backed steward transport (preferred) | Each steward chat turn runs one governed Turn on the managed host (`loopx turn run-once --host dsh`, `isolated-headless`), with bounded chat history as context | No duplex streaming and no cross-turn host session; each turn is a fresh segment. Requires an explicit tool/sandbox authority and a per-turn cost bound before it ships | +| B. ACP or stdio adapter | Reuse the ACP stdio adapter path (as the Kiro CLI chat endpoint does) when the managed host exposes such an interface | Lowest transport cost, but depends on an upstream interface that no shipped evidence covers yet | +| C. Codex endpoint bound to the operator provider | Start the Codex app-server itself against the operator provider so the existing transport and tool surface stay | Keeps streaming, but must prove the session no longer authenticates with an individual login; the provider config becomes host-state authority and needs its own gate | + +Selection rule: prefer A, because it reuses the Turn authority, typed host +failure, journal and quota semantics LoopX already validates; keep B as the +cheaper replacement if the upstream interface appears; evaluate C only if +duplex streaming is required for the steward experience. Whichever option ships +must demonstrate, for one steward session, that the model work lands on the +operator credential and that no default path reaches an individual +subscription. This document authorizes no new scheduler, retry authority or +second monitoring subsystem to make that demonstration pass. + +## Steward Channel Readiness by Milestone (2026-09-15) + +The steward channel consumes both this document's host selection and the manager +milestones in +[capable-manager-semantic-handoff-v0](./capable-manager-semantic-handoff-v0.md). +This section records which steward-channel behaviours those milestones can rely +on today and which stay unverified. It states product contracts, not conversation +content: no live channel transcript, audience identity, dated incident or +operator-local path is recorded here. + +| Milestone | Steward-channel contract in scope | Evidence state on 2026-09-15 | +| --- | --- | --- | +| Manager M1 — useful host agent | The channel resolves and reports its effective executor, model and source, the executor selection does not follow a credential, and a host without a chat transport fails with a typed reason instead of a silent individual-login fallback | Stack-only (PR #4446, with the Turn-side readback in PR #4443): selected endpoint and model with their sources, `executor_kind`, the `channel_binding` readback, and the gated `managed_host_chat_transport_unsupported` request. The upstream **session identity** is still not projected to the channel, so a channel answer cannot yet prove which session served it | +| Manager M2 — semantic continuation | Receiver resolution across registered running lanes; typed per-source coverage and freshness; a goal-level milestone the report can lead with instead of coverage disclaimers | Not implemented. Delegation resolves against the supplied delegation catalog, so a request whose owning lane is absent from that catalog is refused or routed to an unrelated lane; a provider read failure surfaces as raw error text instead of a typed source row; the manager context exposes deliveries and coverage but no goal-level milestone field to synthesize from | +| Manager M3 — automatic complete exchange | A persisted answer that exceeds or violates the channel's outbound text contract is split and re-sent under a stable answer identity; an ambiguous or failed send is reconciled instead of replaced by a local notice; the return path survives a transport restart; rich markdown renders as structured text | Partially mitigated. `loopx/extensions/lark/outbound.py` fails closed on an over-limit or malformed payload, and the channel reports that local failure without re-delivering the persisted answer; one answer carries no idempotency identity, so a retry can duplicate it; structured rendering is not guaranteed | +| Host modes M0-M1 | The channel's executor selection and its bounded one-segment execution | Selection is covered by PR #4446 and the Turn-side selection by PR #4443; bounded one-segment execution is covered by the Mode B acceptance above. The steward channel itself still runs on the interactive CLI transport, so the managed host's own one-segment execution is not yet reachable from the channel | +| Host modes M2-M3 | Attached-host parity, typed unavailability, and mode-aware projection with no mode inference and no second executor | Not implemented for the channel; an external audience still degrades to `restricted`, and the channel projects neither its mode nor its session status | + +Two boundaries stay fixed across all five rows. The channel remains an entry point +and projection of one manager Session: it owns no profile, no permission state, no +second executor and no work authority, so a richer answer contract must not widen +what the channel may read or change. And no row is promoted by this document; the +M1-M4 integration milestones and the cross-frontend projection row still belong to +[Agent Session Execution Modes](./agent-session-execution-modes-v0.md). diff --git a/docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md b/docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md index d91cf53bc6..b1fabf1715 100644 --- a/docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md +++ b/docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md @@ -12,6 +12,99 @@ Pi 保留为 managed runtime 候选。前者利用已存在的被动 observer 必须证明生命周期、provider、崩溃恢复和真实结果,插件事件 fixture 不能替代这些证据。 本评估不提供缺乏测量依据的评分或性能排名。 +本文区分 DSH 的两个角色,二者不可混同:**托管有界 Turn 宿主**(LoopX 为一次受治理 +Turn 选择的适配器)是出货默认值,宿主由配置显式选择、从不由凭据推断,该选择随下文 +记录的托管栈落地,而不是今天的 `main`;**L1 事件源与会话归属 runtime** 角色仍是 +opt-in,不因前者被晋级,仍需本文 C0、C1、开销、保留与 Mode B 各行。 + +## 托管执行面(2026-09-15) + +选型受仓库今天实际交付的能力约束,而不只取决于上游 harness 能做什么。托管的单次执行 +单元是有界 Turn: + +- `loopx turn run-once` 接受 `--host codex-cli|dsh|generic-cli` 与 + `--execution-mode isolated-headless`:LoopX 决定,宿主适配器调用 agent CLI,独立 + validator 证明后置条件,只有通过的结果才会被提交; +- `loopx host-mode-plan` 只有在宿主声明 `typed_host_adapter` 时,才为 + `continue_without_ui` 意图选择 `isolated_headless_turn`;缺少该声明时报该模式未就绪, + 并指出缺失的能力; +- 会话归属(`managed_runtime` 与 `attached_host`)不在本文决定,属于 + [Agent 会话执行模式](./agent-session-execution-modes-v0.zh-CN.md),该文档同时拥有 + M1-M4 接入里程碑与跨前端投影行。 + +下文区分三种证据状态,不可互相套用;该列表以 2026-09-15 为准,并计划与托管栈一起 +落地: + +- **2026-09-15 已在 `main` 上**:`loopx turn run-once --host + codex-cli|dsh|generic-cli`、`dsh` 宿主适配器、上面的 `host-mode-plan` 门槛,以及 + `main` 固定的 `deepseek-harness-sdk==0.1.2a3`; +- **2026-09-15 尚未进入 `main`、计划随本文落地**:显式选择宿主的默认值 + (`loopx/control_plane/turn_driver/host_binding.py`,PR #4443)与 `0.1.5rc1` 的 dsh + 固定版本(PR #4420)。后续读者若看到这两个 PR 已合并,可把这两行读作已交付; + 否则只能按栈内状态理解; +- **本地真实验证,不是仓库门禁**:下文标注为本地证据的行。它们需要 operator 凭据 + 才能复现,CI 不做断言。 + +| 角色 | 来源 | 当前选型 | 晋级门槛 | +| --- | --- | --- | --- | +| 默认托管执行宿主 | LoopX Turn 加 `dsh` 宿主适配器,并绑定到运维方提供的模型端点 | 出货默认值:托管有界 Turn 走 `dsh`,与环境无关;`LOOPX_TURN_HOST` 可改指,显式 `--host` 优先;在托管栈中(PR #4443),尚未进入 `main` | 保持类型化 host request/result、独立验证与凭据归属运维方的边界;没有同等或更强的契约不替换 | +| 管家通道执行器 | 管家回答所依赖的交互式 Chat 传输 | 出货默认值:`codex`;`LOOPX_MANAGER_ENDPOINT` 可改指;在托管栈中(PR #4446),尚未进入 `main` | 托管宿主具备交互式 Chat 传输,管家通道才能选择它;凭据的存在从不是晋级信号 | +| 受支持的替代 Turn 宿主 | LoopX Turn 加 `codex-cli` 适配器 | 可显式选择;它属于 `individual` 执行器类型,账落在某个人的 CLI 登录上 | 任何托管通道都不得静默依赖某个人的 CLI 订阅;个人通道必须被显式选择,而不是默认走到 | +| L1 事件源与会话归属 runtime 候选 | DSH | opt-in,未晋级;有界 Turn 宿主角色见上一行默认值 | 本文 C0、C1、开销、保留与 Mode B 各行被真实执行并通过评审 | +| 可选的可见宿主循环 | Pi | 不是 managed runtime | 先声明按绑定持久化且可回读的会话模式,证明重启下的单执行器行为、"对话不是回执"、宿主本地状态非权威,并提供一条真实宿主重启行 | + +### 托管宿主绑定与真实环境验证(2026-09-15) + +一个托管宿主绑定要说明四件事:宿主适配器、provider、模型,以及凭据来自哪里。 +DSH 绑定是 DSH Turn 宿主 + provider `deepseek-official` + 模型 `deepseek-flash` +(DeepSeek V4.1 Flash),端点取自运维方环境(`DEEPSEEK_BASE_URL`),凭据取自 +运维方环境(`DEEPSEEK_API_KEY`)。 + +LoopX **选择**托管有界 Turn 的默认宿主,而从不由环境推断 +(`loopx/control_plane/turn_driver/host_binding.py`):出货默认值是 `dsh`, +`LOOPX_TURN_HOST` 可改指,显式 `--host` 优先于两者。operator 凭据不是选型输入。 +这个区分正是该绑定的意义:发现一把 key 不等于决定换运行位置;一个按环境解析宿主的 +面,会让"选定的配置"和"偶然生效的配置"无法区分。因此被选到 DSH 宿主的通道不会依赖 +某个开发者本机 CLI 订阅是否可用、是否还有额度或是否已登录。 + +管家通道是**另一个**面,默认值也不同。它的出货执行器是 `codex`,因为这是今天唯一 +能承载交互式管家会话的传输;`LOOPX_MANAGER_ENDPOINT` 可改指,凭据不能改指。它的 +模型跟随它所选择的执行器而不是跟随环境,因此配置了 operator 凭据不会在端点仍是 +CLI 的情况下把管家悄悄换成 operator 模型。 + +该绑定的证据按来源区分: + +- 仓库覆盖、无需任何 provider 调用:出货默认值是 `dsh`,显式 `LOOPX_TURN_HOST` + 可改指,显式 `--host` 仍然优先,且配置凭据不改变以上任何一项选择 + (PR #4443 的测试,尚未进入 `main`); +- 本地真实验证:在真实 SDK 与 runtime(`deepseek-harness-sdk==0.1.5rc1`,即 PR + #4420 提出的固定版本;`main` 今天仍固定在 `0.1.2a3`,同一对路径在那里也通过)下, + 进程内 `--host dsh` 路径与 `generic-cli` 子进程路径均通过; +- 本地真实验证:一次托管 Turn 达到 `validated_progress`,宿主执行有界动作,独立 + validator 证明后置条件,随后才发生写回与配额扣减; +- 本地真实验证:一次后置条件未被证明的 Turn 反向失败关闭,没有写回,配额槽消耗计数 + 保持为 0。 + +在该绑定成为正式默认值之前仍存在的缺口: + +- `deepseek-harness-runtime-bin==0.1.5rc1` 捆绑的 runtime 快照无法按原样启动 + `headless` profile:其中一行会拉起 + `@deepseek-ai/dsh-session-title-first-prompt-llm`,该包 import 了未被收录的 + `@deepseek-ai/dsh-session-title-llm`;解析发生在打包快照内部,因此把该包装进 + profile 目录不会改变结果。当前本地做法是用一条绑定 overlay 关闭受影响的行。 + 托管宿主路径不受影响:它不选择 `headless` profile,默认 `sdk` profile 能正常 + 启动并干净退出; +- LoopX 的 DSH Turn 组合必须显式列出托管动作所需的工具行 + (`@deepseek-ai/dsh-tool-fs`、`@deepseek-ai/dsh-tool-bash`)。缺少它们时,真实模型 + 只能作答而无法动手,Turn 会以验证失败而不是产出工作结束。 +- 宿主模式计划仍把无人值守意图映射到兼容路径:`isolated_headless_turn` 的 + `turn_host` 取 `generic-cli`(`loopx/host_mode_planner.py`),因此它打印的 + `loopx turn plan` 命令写的是 `--host generic-cli`,而不是上文记录的已选 `dsh` + 默认值;作为回滚路径本身没错,但没有被标注为回滚路径。该计划的 + `--host-identity` 列表只覆盖可见宿主是有意为之——像 + `dsh` 这种仅 headless 的宿主无法拥有可见会话;但无人值守映射本身仍需在"写出解析 + 后的默认值 / 提供 `dsh` 变体 / 把该命令标注为回滚路径"之间做出决定。 + ## 证据基线 LoopX 检查基线为 `bf217e1e01bec79f357c9ecbd580cf2dfa73db8b`: @@ -27,6 +120,13 @@ LoopX 检查基线为 `bf217e1e01bec79f357c9ecbd580cf2dfa73db8b`: - `apps/desktop/loopx-control-plane/src-tauri/src/services.rs`:已有服务进程管理不等于 RFC 所要求的完整 managed Agent 生命周期。 +dsh 固定版本经历了两步,理解本文需要同时知道这两个状态:今天的 `main` 固定 +`deepseek-harness-sdk==0.1.2a3`;托管栈把该固定版本升到最新发布通道,而不是未发布的 +tag:PyPI 上的 `deepseek-harness-sdk==0.1.5rc1` / +`deepseek-harness-runtime-bin==0.1.5rc1`(PR #4420),与 npm `@deepseek-ai/dsh` 的 +`latest` 一致(2026-09-15 核对)。上游 `next` 与 `alpha` tag 比该通道更新,这里不 +采纳。 + 2026-09-06 独立检查的上游版本,不等同于 LoopX 已验证的安装版本: - [DSH d347e703 README](https://github.com/deepseek-ai/deepseek-harness/blob/d347e703908d0406b7a7ef80e3a0e594d86b2215/README.md): @@ -56,6 +156,14 @@ LoopX 检查基线为 `bf217e1e01bec79f357c9ecbd580cf2dfa73db8b`: 这是接入成本和合同差异,不是说 Pi 没有事件,或 DSH 不能使用其他模型。 两个 harness 都有控制 API;“被动”是具体 adapter 和实际加载依赖的性质。 +依赖 dsh 的两个 LoopX 面并不一起移动:有界 Turn 宿主使用上文记录的 Python +SDK/runtime 固定版本(`0.1.5rc1`,已发布通道);而 dsh 侧插件 +(`packages/dsh-loopx-plugin`)的开发与客户端面仍构建在 `0.1.1-rc.2` 上,尽管其 +clean-Docker smoke 已断言 `dsh --version == 0.1.5-rc.1`。0.1.5 线不再发布 +`@deepseek-ai/dsh-client-runtime`(最后发布版本为 `0.1.1-rc.2`),客户端 runner 改为 +`@deepseek-ai/dsh-cordis-client-runner`。该升级作为独立的 pin 项跟踪,不改变上文的 +L1 observer 契约。 + ## 数据流与权限 用户需要区分“没有证据”“执行有异常”“观察过程不可信”,而不是只得到一个绿灯: @@ -114,6 +222,12 @@ goal ledger 不得被标成当前 session 健康。输出只供操作者,不 原始日志、凭据留在 owner-local;公共材料只保留通用方法、固定版本、聚合结果和 安全引用。本文件不授权真实模型执行或删除现有记录。 +里程碑归属仍由 +[Agent 会话执行模式](./agent-session-execution-modes-v0.zh-CN.md) 决定:本文负责 +L1 observer 这条臂的 C0、C1、开销与保留证据,以及上面针对会话归属 runtime 的 Mode B +验收;M1-M4 接入里程碑与跨前端投影行仍归该文档,本文不定义模式推断,也不定义第二个 +执行器。 + ## 后续交付次序 先评审对比结论和 CLI 读取增量;Mode B 面板必须先具备精确 session 读取与有界刷新, @@ -121,3 +235,50 @@ goal ledger 不得被标成当前 session 健康。输出只供操作者,不 提交仓库。删除功能等 retention profile 决定后再做。若 Pi 在相同隔离及生命周期 验收下具有更低的实测接入/运维成本,或 DSH 无法通过,再调整偏好。 不得为了让 L1 实验通过而加入 L2 建议、重试权限或新 scheduler。 + +## 管家通道的会话传输(2026-09-15) + +受治理的 Turn 面与管家(manager)会话通道各自**显式选择**宿主,互不推断;但两者需要 +的宿主形态不同:Turn 是一次有界工作片段,现有 DSH adapter 已支持;管家通道还需要一个 +能持有交互会话的传输,而现有 DSH 面明确不承诺跨 turn 的 DSH 会话连续性。 + +托管栈中的行为(PR #4446,尚未进入 `main`):管家通道把 `codex` 作为出货执行器,并 +保持厂商默认模型,operator 凭据不改指其中任何一项——它只作为事实被回报 +(`operator_credential_configured`,仅变量名),并且只为真正跑在 operator provider 上 +的那个端点提供认证。若为该通道选择托管宿主,解析结果给出 `available: false` 与 typed +`managed_host_chat_transport_unsupported`;针对该宿主的会话请求以同一个 typed +host-tool gate 失败,不会静默回落到个人 +CLI 登录。 + +| 路线 | 形态 | 代价与风险 | +| --- | --- | --- | +| A. turn-backed 管家传输(优先) | 每个管家 chat turn 在托管宿主上执行一次受治理 Turn(`loopx turn run-once --host dsh`,`isolated-headless`),把有界会话历史作为上下文 | 无双工流式、无跨 turn 宿主会话,每个 turn 都是新 segment;上线前需要明确的工具/沙箱权威与单 turn 成本上限 | +| B. ACP 或 stdio 适配 | 当托管宿主暴露此类接口时,复用 ACP stdio 适配路径(Kiro CLI chat 端点已走此路) | 传输成本最低,但依赖上游接口,目前没有已交付证据 | +| C. codex 端点绑定 operator provider | 让 Codex app-server 直接以 operator provider 启动,保留现有传输与工具面 | 保留流式,但必须证明会话不再以个人登录认证;provider 配置成为宿主状态权威,需要单独 gate | + +选型规则:优先 A,因为它复用 LoopX 已经验证过的 Turn 权威、typed host failure、 +journal 与配额语义;B 作为上游接口出现时的低成本替代;只有在管家体验确需双工 +流式时才评估 C。无论采用哪条路线,都必须证明「一次管家会话的模型工作落在 +operator 凭据上,且不存在任何默认指向个人订阅的路径」。本文件不授权为此新增 +scheduler、重试权限或第二套监控子系统。 + +## 按里程碑看管家通道的就绪度(2026-09-15) + +管家通道同时消费本文的宿主选型与 +[管家语义交接](./capable-manager-semantic-handoff-v0.zh-CN.md) 中的管家里程碑。 +本节记录这些里程碑当前可以依赖管家通道的哪些行为、哪些仍未验证。这里只写产品 +契约,不写会话内容:不记录真实会话原文、受众身份、带日期的具体事故,或 operator +本机路径。 + +| 里程碑 | 管家通道在范围内的契约 | 2026-09-15 的证据状态 | +| --- | --- | --- | +| 管家 M1 — 可用的宿主 agent | 通道解析并回报其生效执行器、模型与来源,执行器选型不跟随凭据;没有 chat 传输的宿主以 typed reason 失败,而不是静默回落到个人登录 | 仅存在于托管栈(PR #4446,配套 Turn 侧读取见 PR #4443):带来源的已选端点与模型、`executor_kind`、`channel_binding` 读取,以及被 gate 的 `managed_host_chat_transport_unsupported` 请求。上游**会话身份**尚未投影到通道,因此通道回答还无法证明是哪一次会话给出的 | +| 管家 M2 — 语义续接 | 跨所有已注册运行中 lane 的接收者解析;按来源的 typed 覆盖与新鲜度;报告可以先用目标级里程碑开头,而不是先给覆盖免责声明 | 未实现。委托只按传入的委托目录解析,因此拥有该事项的 lane 不在目录中时会被拒绝或投给无关 lane;provider 读取失败以原始错误文本出现在回答里,而不是 typed 来源行;管家上下文只提供交付与覆盖,没有可综合的目标级里程碑字段 | +| 管家 M3 — 自动完成一次交流 | 超出或违反通道出站文本契约的已保存回答,按稳定答案身份分片重发;含糊或失败的发送要协调而不是用本地提示替代;回传路径要能跨传输重启存活;富文本要渲染成结构化文本 | 部分缓解。`loopx/extensions/lark/outbound.py` 在超限或载荷不合法时 fail closed,通道只回报这个本地失败、不重新投递已保存的回答;一条回答没有幂等身份,重试可能重复发送;结构化渲染没有保证 | +| 宿主模式 M0-M1 | 通道的执行器选型与其有界单段执行 | 选型由 PR #4446 覆盖,Turn 侧选型由 PR #4443 覆盖;有界单段执行由上面的 Mode B 验收覆盖。管家通道本身仍跑在交互式 CLI 传输上,因此托管宿主自己的单段执行尚未能从通道抵达 | +| 宿主模式 M2-M3 | attached-host 对齐、typed 不可用,以及不做模式推断、不引入第二执行器的模式感知投影 | 通道尚未实现;外部受众仍降级为 `restricted`,通道既不投影模式也不投影会话状态 | + +五行的两条边界固定不变:通道始终是同一个 manager Session 的入口与投影,不拥有 +profile、权限状态、第二执行器或工作权威,因此更丰富的回答契约不得扩大通道可读或 +可改的范围;本文也不提升任何一行的状态——M1-M4 接入里程碑与跨前端投影行仍归 +[Agent 会话执行模式](./agent-session-execution-modes-v0.zh-CN.md)。