Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions docs/architecture/rfcs/harness-selection-dsh-pi-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,21 @@ Open gaps before this binding is a promoted production default:
(`@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.
- the host-mode plan used to map the unattended intent to the compatibility
path: `isolated_headless_turn` carried `turn_host: generic-cli`
(`loopx/host_mode_planner.py`), so the `loopx turn plan` command it printed
named `--host generic-cli` instead of the selected `dsh` default recorded
above, which is correct as a labelled rollback path but was not labelled as
one. **Decided and shipped:** the plan takes the "write out the resolved
default" option. The preview command pins no host, the pinned compatibility
variant is reported as `plan_command_rollback`, and the typed
`turn_mapping.host_selection` states which of the two a command is; targets
that genuinely need a visible identity (transitions into `visible_tui`) still
pin their host. `docs/reference/protocols/host-mode-plan-v0.md` defines
`turn_mapping.host` as the mode's declared host and scheduler context rather
than as an already-resolved concrete host. The plan's `--host-identity` list
still covers visible hosts only, because a headless-only host such as `dsh`
cannot own a visible session.

## Evidence Baseline

Expand Down
14 changes: 9 additions & 5 deletions docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,17 @@ CLI 的情况下把管家悄悄换成 operator 模型。
- LoopX 的 DSH Turn 组合必须显式列出托管动作所需的工具行
(`@deepseek-ai/dsh-tool-fs`、`@deepseek-ai/dsh-tool-bash`)。缺少它们时,真实模型
只能作答而无法动手,Turn 会以验证失败而不是产出工作结束。
- 宿主模式计划仍把无人值守意图映射到兼容路径:`isolated_headless_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` 变体 / 把该命令标注为回滚路径"之间做出决定。
默认值;作为回滚路径本身没错,但没有被标注为回滚路径。**已决并已落地:**计划采用
"写出解析后的默认值"这一支——预览命令不再 pin 任何 host,pin 死的兼容变体报为
`plan_command_rollback`,类型化的 `turn_mapping.host_selection` 说明该命令属于
哪一种;真正需要可见身份的路径(转入 `visible_tui` 的 transition)仍然 pin 具体
宿主。`docs/reference/protocols/host-mode-plan-v0.md` 已把 `turn_mapping.host`
定义为该模式的声明宿主与调度上下文,而不是"具体宿主已经解析完成"。该计划的
`--host-identity` 列表仍只覆盖可见宿主,因为像 `dsh` 这种仅 headless 的宿主无法
拥有可见会话。

## 证据基线

Expand Down
20 changes: 19 additions & 1 deletion docs/reference/protocols/host-mode-plan-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,23 @@ Each `mode_options[]` entry includes the connector id, readiness, required
host capabilities, Turn mapping when one exists, scheduler execution context,
quota guard command, and required proofs.

`selected_turn_mapping` fields mean:

- `host` is the mode's **declared** host: the scheduler context the readiness
statement and capability requirements are written against. It is not a claim
that this concrete host has already been resolved for the run; the runtime
resolves the concrete host (including a credential- or configuration-resolved
default) when `plan_command` runs.
- `host_selection` is `resolved_default` when the command deliberately leaves
host resolution to `loopx turn plan`/`run-once`, and `pinned` when the command
carries an explicit `--host`.
- `plan_command` is the command to run for this mapping. When `host_selection`
is `resolved_default` it pins no host, so it cannot freeze the compatibility
adapter path as the product default.
- `plan_command_rollback` is the pinned compatibility variant for an operator
who deliberately wants that path instead of the resolved default. It is
present only when the mapping resolves its host.

## Functional Points

The selector provides four concrete functions:
Expand All @@ -150,7 +167,8 @@ The selector provides four concrete functions:
users and agents to infer visible/headless/gateway/timer behavior manually.
2. **Turn mapping:** for unattended execution, print the exact `loopx turn plan`
preview that preserves host, execution mode, scheduler owner, agent id, and
available capabilities.
available capabilities, while leaving the concrete host to the runtime's
resolved default and reporting the pinned variant as the rollback command.
3. **Readiness surface:** report which advertised capabilities are missing
before a mode can be trusted.
4. **Safe handoff plan:** name transitions such as visible bootstrap to
Expand Down
17 changes: 17 additions & 0 deletions examples/host-mode-plan-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,23 @@ def test_hybrid_requires_two_ready_modes_and_names_handoffs() -> None:
assert transition["preserves_agent_id"] is True, transition
assert transition["spends_quota"] is False, transition
assert "--agent-id codex-main-control" in transition["guard_command"], transition
# The transition policy is the same rule as the mode preview: a target that
# resolves its host must not pin the compatibility adapter path, while a
# target that needs a visible identity still pins it.
by_id = {item["transition"]: item for item in two_ready["transitions"]}
for transition_id in (
"visible_bootstrap_to_isolated_headless_turn",
"im_gateway_to_isolated_headless_turn",
):
command = by_id[transition_id]["target_turn_plan_command"]
assert "--host" not in command, (transition_id, command)
for transition_id in (
"isolated_headless_turn_to_visible_tui_escalation",
"shell_service_to_visible_tui_escalation",
):
command = by_id[transition_id]["target_turn_plan_command"]
assert "--host codex-cli" in command, (transition_id, command)
assert "--execution-mode interactive-visible" in command, (transition_id, command)


def test_identity_gate_and_no_spend_boundary() -> None:
Expand Down