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
8 changes: 7 additions & 1 deletion docs/integrations/codex-subagent-orchestration.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,9 @@ for the current requester and projects as many as fit the existing context byte
budget; `authorized_count` and `routes_truncated` make omissions explicit. Each
route carries only binding/Agent/Todo/runtime
identity, separate `runtime_readiness` and `readiness` observations, an optional public-safe execution profile and
one stable `loopx delegation` entrypoint. A separate, explicit
the existing host owner's `probe_scope` for a supplied non-null probe, and one stable
`loopx delegation` entrypoint. Full probe/remediation facts are disclosed by
inspecting that binding, not inlined into the planning budget. A separate, explicit
`agent-context --phase after_delegate_result` read may include bounded
operation-status and recovery-required counts. Automatic planning and managed
return paths do not enumerate the operation journal. These reads do not launch,
Expand Down Expand Up @@ -508,6 +510,10 @@ surfaces consume this same capability context, not another route configuration.
`readiness=unknown`、`preflight=required`,通过现有 `loopx delegation inspect`
核验权威状态、任务验证与 Turn 准入。未知不等于禁用,ready 运行库也不等于可执行。
用户的异构偏好影响批次选择,不要求每次心跳重启所有路线,不绕过任一真实门禁。
投影保留 host owner 非空 probe 的 `probe_scope`,完整 probe 与修复代码通过原
绑定预检渐进式披露,不额外发起探测或决定准入。`probing_interpreter` 的模块缺失只针对本次检查的解释器,不能
推断整机没有运行库;未探测的通用适配器仍为 `null/unknown`。修复原绑定环境后必须
重新核验,不通过创建替代 worker、静默换模型或复用旧任务成功来伪造恢复。

中文:可在现有 `multi_subagent` 能力中配置
`.loopx/config/delegations.json` 指针,让当前请求 Agent 在规划前看到自己已获授权的
Expand Down
28 changes: 28 additions & 0 deletions docs/reference/local-delegation.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ filter used by `delegate list` considers at most six public-safe planning routes
and byte-bounds the projected subset; `authorized_count` and `routes_truncated`
make omissions explicit. Managed-host availability comes from the existing Turn host/profile owner;
unprobed generic adapters are `unknown`, not optimistically ready.
Routes disclose that owner's `probe_scope` for a supplied non-null probe. Read full
`runtime_probe` and `unavailable_remediation` observations through
`delegation inspect` on the same binding; planning keeps its existing byte budget.

This planning projection is read-only. It does not start, resume, accept,
enumerate operations or periodically poll work. A ready observation is not an
Expand All @@ -286,6 +289,9 @@ no legacy Session setting is copied back into the registry automatically.
启动、恢复、验收或周期轮询工作;需要时可显式读取 `after_delegate_result` 阶段的有界
operation 状态摘要。ready 和状态计数都不是执行或父级验收回执。清除指针不会撤销
授权;真正撤销仍须修改 operator binding 文件。
路由对非空 probe 渐进式披露同一 host owner 的 `probe_scope`。按原绑定读取下述
`delegation inspect`,可获得完整 `runtime_probe` 和 `unavailable_remediation`;
规划保持原有字节预算,不内联全部诊断。

### Recover work without remembered operation ids

Expand Down Expand Up @@ -354,6 +360,28 @@ Normal start still reads current admission and independently validates output.
If the existing Turn rejects preflight, inspection reports that error rather than
manufacturing a launchable result; no request is created.

`executor.runtime_probe` preserves the host's bounded probe scope: a DSH
`probing_interpreter` result concerns module availability in the interpreter
running this inspection, not every installation or remote host. A
`configured_runner` result concerns the explicit runner. A `null` probe means
this executor was not probed; older previews may omit the optional field.
`executor.unavailable_remediation` contains bounded operator-action codes from
the same host owner, not commands or permission to switch providers. For
`configure_dsh_runtime`, check the original launcher's interpreter and its
`deepseek-harness` optional dependency or original runner configuration before
requalification. A successful module probe alone does not prove credentials,
profile, task acceptance or remote capacity. Inspection exposes no interpreter
paths, credential/endpoint configuration, or provider payloads.

中文:`executor.runtime_probe` 保留 host 的有界探测范围。DSH 的
`probing_interpreter` 只说明执行本次检查的解释器是否能找到模块,不代表整机或远端
所有安装;`configured_runner` 针对已显式配置的 runner。`null` 表示未探测,旧预览
可以缺省此兼容字段。`executor.unavailable_remediation` 是同一 host owner 提供的
有界操作代码,不是命令,也不授予切换 provider 的权限。遇到
`configure_dsh_runtime`,先核对原启动器绑定的解释器及其 `deepseek-harness` 可选依赖
或原 runner 配置,再重新核验。模块可用不证明凭据、profile、任务验收或远端容量;
此检查不暴露解释器路径、凭据/endpoint 配置或 provider 原始数据。

Enabled MCP exposes `inspect_execution_binding`; newly enrolled Goal Chat tools
accept `action=inspect` with `binding_id`. Existing native thread schemas remain
unchanged. Owners can use **Team execution** directly below the Goal conversation
Expand Down
31 changes: 30 additions & 1 deletion loopx/control_plane/collaboration/delegation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,34 @@ export function delegationTurnPlanDecision(params: JsonObject): JsonObject {
};
}

/** Preserve the host owner's public diagnosis, not its private configuration.
* Optional fields keep older host previews compatible; null means unprobed. */
export function delegationRuntimeFacts(executor: JsonObject): JsonObject {
const facts: JsonObject = {};
if (Object.hasOwn(executor, "runtime_probe")) {
if (executor.runtime_probe === null) facts.runtime_probe = null;
else {
const probe = requireJsonObject(executor.runtime_probe, "runtime probe");
requireThat(probe.schema_version === "managed_runtime_probe_v0"
&& typeof probe.scope === "string"
&& ["probing_interpreter", "configured_runner"].includes(probe.scope)
&& (probe.module === null || (typeof probe.module === "string"
&& probe.module.length <= 128 && /^[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*$/.test(probe.module)))
&& typeof probe.available === "boolean", "invalid runtime probe observation");
facts.runtime_probe = {schema_version: probe.schema_version, scope: probe.scope,
module: probe.module, available: probe.available};
}
}
if (Object.hasOwn(executor, "unavailable_remediation")) {
const remedies = executor.unavailable_remediation;
requireThat(Array.isArray(remedies) && remedies.length <= 8
&& remedies.every(code => typeof code === "string" && /^[a-z][a-z0-9_]{0,79}$/.test(code)),
"invalid runtime remediation codes");
facts.unavailable_remediation = [...remedies];
}
return facts;
}

/** Read the actual dry-run route/profile, never infer readiness from assignment. */
export function delegationPreflight(params: JsonObject): JsonObject {
const binding = requireJsonObject(params.binding, "binding identity");
Expand Down Expand Up @@ -115,7 +143,8 @@ export function delegationPreflight(params: JsonObject): JsonObject {
authority_state: "promoted", authority_next_action: "none",
promotion_from_surface_allowed: false,
executor: {host: executor.executor, available: executor.available,
reason: executor.unavailable_reason, profile: executor.execution_profile},
reason: executor.unavailable_reason, profile: executor.execution_profile,
...delegationRuntimeFacts(executor)},
effects,
note: "Point-in-time preflight, not an execution permit or evidence of running work. "
+ "Start rechecks admission; inspect original operations before dispatching replacements. "
Expand Down
5 changes: 5 additions & 0 deletions loopx/control_plane/collaboration/delegation_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ def _route(binding: dict[str, Any]) -> dict[str, Any]:
reason = str(executor.get("unavailable_reason") or "").strip()
if reason:
row["reason_code"] = reason
# Transport the existing host owner's public observations unchanged. The
# Python adapter neither reprobes nor derives another admission decision.
for key in ("runtime_probe", "unavailable_remediation"):
if key in executor:
row[key] = executor[key]
return row


Expand Down
6 changes: 6 additions & 0 deletions loopx/control_plane/subagent_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { AGENT_CONTEXT_PHASES, projectAgentContext, type AgentContextProvider } from "./agent_context.ts";
import type { JsonObject } from "./effect_program.ts";
import { jsonObject, requireJsonObject } from "./runtime_decode.ts";
import { delegationRuntimeFacts } from "./collaboration/delegation.ts";

export const subagentContextProvider: AgentContextProvider = {
hookId: "multi_subagent.coordinator", capabilityId: "multi_subagent", revision: "v5",
Expand Down Expand Up @@ -131,6 +132,11 @@ function boundedDelegationContext(value: unknown): JsonObject | null {
if (executorKind) compact.executor_kind = executorKind;
if (profile) compact.execution_profile = profile;
if (reason) compact.reason_code = reason;
// Planning discloses scope only; inspect the same binding for full probe
// and remedies. Keep the existing route/contribution budgets unchanged.
const runtimeFacts = delegationRuntimeFacts(route);
const probe = jsonObject(runtimeFacts.runtime_probe);
if (probe) compact.probe_scope = probe.scope;
return [compact];
}) : [];
const rawReceipts = jsonObject(source.operation_receipts);
Expand Down
48 changes: 48 additions & 0 deletions tests/control_plane/test_delegation_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import json
from pathlib import Path

import pytest

from loopx.control_plane.agent_context import project_goal_agent_context
from loopx.control_plane.collaboration import delegation_context
from loopx.control_plane.quota.live_decision import build_live_quota_should_run_decision
Expand Down Expand Up @@ -148,6 +150,52 @@ def operations(_self, *, limit, cursor=None):
}


@pytest.mark.parametrize("runner_configured", [False, True])
def test_planning_retains_original_probe_scope_and_remediation_without_admission(
tmp_path: Path, monkeypatch, runner_configured: bool
) -> None:
from loopx.control_plane.turn_driver.host_binding import managed_executor_binding

project, registry, runtime = _fixture(tmp_path)
calls = []
executor = managed_executor_binding(
"dsh", environ={}, module_probe=lambda _: False,
dsh_runner_configured=runner_configured,
)

def binding(*args, **_kwargs):
calls.append(args)
return {**executor, "credential_env": "PRIVATE_CREDENTIAL",
"endpoint_env": "PRIVATE_ENDPOINT"}

monkeypatch.setattr(delegation_context, "managed_executor_binding_from_host_args", binding)
packet = delegation_context.project_delegation_context(
runtime_root=runtime, registry_path=registry, goal_id="goal-a",
agent_id="coordinator", project=project, execution_config=".loopx/config/delegations.json",
)
route = packet["routes"][0]
assert len(calls) == 1
assert route["runtime_probe"] == executor["runtime_probe"]
assert route["unavailable_remediation"] == executor["unavailable_remediation"]
assert route["runtime_readiness"] == ("ready" if runner_configured else "blocked")
assert route["readiness"] == ("unknown" if runner_configured else "blocked")
assert packet["preflight"] == "required"
assert "operation_receipts" not in packet
assert "PRIVATE_" not in json.dumps(packet)


def test_unprobed_generic_route_keeps_explicit_null_probe(tmp_path: Path) -> None:
project, registry, runtime = _fixture(tmp_path)
packet = delegation_context.project_delegation_context(
runtime_root=runtime, registry_path=registry, goal_id="goal-a",
agent_id="coordinator", project=project, execution_config=".loopx/config/delegations.json",
)
route = packet["routes"][0]
assert route["runtime_probe"] is None
assert route["unavailable_remediation"] == []
assert route["runtime_readiness"] == route["readiness"] == "unknown"


def test_repeated_live_quota_planning_never_reads_operation_inventory(
tmp_path: Path, monkeypatch
) -> None:
Expand Down
34 changes: 34 additions & 0 deletions tests/control_plane_ts/agent_context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,40 @@ test("maximum delegation directory stays within provider budget", () => {
assert.ok(Buffer.byteLength(JSON.stringify(packet.contributions[0])) <= 2048);
});

test("planning progressively discloses probe scope without crowding out a second route", () => {
const probe = {schema_version: "managed_runtime_probe_v0", scope: "probing_interpreter",
module: "deepseek_harness", available: false};
const routes = [
{binding_id: "managed-evidence/" + "x".repeat(34),
agent_id: "managed-worker-" + "x".repeat(12), todo_id: "todo_" + "a".repeat(12), runtime_id: "dsh",
readiness: "blocked", runtime_readiness: "blocked", executor_kind: "managed",
execution_profile: "deepseek-v4-flash@high", reason_code: "dsh_runtime_unavailable",
runtime_probe: {...probe, private_path: "/private/interpreter"},
unavailable_remediation: ["configure_dsh_runtime", "select_individual_host"]},
{binding_id: "generic-verifier/" + "x".repeat(34),
agent_id: "generic-worker-" + "x".repeat(12), todo_id: "todo_" + "b".repeat(12), runtime_id: "generic-cli",
readiness: "unknown", runtime_readiness: "unknown", executor_kind: "generic",
runtime_probe: null, unavailable_remediation: []},
];
for (const phase of ["before_plan", "before_delegate"]) {
const packet = evaluateSubagentContext({phase, scope, orchestration: policy, observations: {
delegation_context: {schema_version: "loopx_delegation_context_v0", configuration_state: "ready",
observed_at: "2026-09-19T04:20:00+00:00", authorized_count: 2, projected_count: 2, routes},
}})!;
assert.deepEqual(packet.failures, []);
const [contribution] = packet.contributions as Record<string, any>[];
const context = contribution.facts.delegation_context;
assert.equal(context.projected_count, 2);
assert.equal(context.preflight, "required");
assert.deepEqual(context.routes.map((route: any) => route.probe_scope), ["probing_interpreter", undefined]);
assert.deepEqual(context.routes.map((route: any) => route.readiness), ["blocked", "unknown"]);
assert.ok(Buffer.byteLength(JSON.stringify(context)) <= 900);
assert.ok(Buffer.byteLength(JSON.stringify(contribution)) <= 2048);
assert.ok(!JSON.stringify(packet).includes("/private/interpreter"));
assert.ok(!JSON.stringify(packet).includes("configure_dsh_runtime"));
}
});

// Rich model identifiers and the complete participation guidance must survive
// the actual provider budget, not disappear as an isolated provider failure.
test("coordinator participation guidance survives all bounded lifecycle projections", () => {
Expand Down
64 changes: 64 additions & 0 deletions tests/control_plane_ts/delegation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,70 @@ test("preflight separates task admission, acceptance binding and runtime availab
assert.throws(() => delegationPreflight({...params, preview: {...preview, effects: {...effects, host_invoked: true}}}));
});

const runtimePreflight = (executor: Record<string, unknown>) => delegationPreflight({
binding, validation_files_current: true, acceptance: {todo_id: binding.todo_id, state: "ready"},
preview: {dry_run: true, status: "preview",
effects: {host_invoked: false, state_written: false, quota_spent: false, scheduler_acknowledged: false},
route: {kind: "ready_for_host", would_invoke_host: true, selected_todo_id: binding.todo_id},
managed_executor: executor},
});

test("preflight retains scoped runtime facts without leaking host configuration", () => {
const probe = {schema_version: "managed_runtime_probe_v0", scope: "probing_interpreter",
module: "deepseek_harness", available: false};
const remedies = ["configure_dsh_runtime", "select_individual_host"];
const result = runtimePreflight({executor: "dsh", available: false,
unavailable_reason: "dsh_runtime_unavailable", execution_profile: "explicit-profile",
runtime_probe: {...probe, private_path: "/private/interpreter"}, unavailable_remediation: remedies,
credential_env: "PRIVATE_CREDENTIAL", endpoint_env: "PRIVATE_ENDPOINT"});
assert.equal(result.state, "runtime_unavailable");
assert.deepEqual(result.executor, {host: "dsh", available: false,
reason: "dsh_runtime_unavailable", profile: "explicit-profile",
runtime_probe: probe, unavailable_remediation: remedies});
assert.equal(JSON.stringify(result).includes("PRIVATE_"), false);
assert.equal(JSON.stringify(result).includes("/private/interpreter"), false);
assert.equal(Object.values(result.effects as Record<string, boolean>).some(Boolean), false);
});

test("runtime probes cannot override credential failure or unprobed generic readiness", () => {
const result = runtimePreflight({executor: "dsh", available: false,
unavailable_reason: "operator_credential_unconfigured", execution_profile: "explicit-profile",
runtime_probe: {schema_version: "managed_runtime_probe_v0", scope: "configured_runner",
module: null, available: true},
unavailable_remediation: ["configure_operator_credential", "select_individual_host"]});
assert.equal(result.state, "runtime_unavailable");
assert.equal((result.executor as Record<string, unknown>).available, false);
assert.deepEqual((result.executor as Record<string, unknown>).runtime_probe,
{schema_version: "managed_runtime_probe_v0", scope: "configured_runner", module: null, available: true});
const generic = runtimePreflight({executor: "generic-cli", available: null,
unavailable_reason: null, execution_profile: null, runtime_probe: null, unavailable_remediation: []});
assert.equal(generic.state, "runtime_unverified");
assert.deepEqual(generic.executor, {host: "generic-cli", available: null, reason: null,
profile: null, runtime_probe: null, unavailable_remediation: []});
});

test("malformed scoped runtime facts fail closed; legacy omissions remain compatible", () => {
const probe = {schema_version: "managed_runtime_probe_v0", scope: "probing_interpreter",
module: "deepseek_harness", available: false};
const executor = {executor: "dsh", available: false, unavailable_reason: "dsh_runtime_unavailable",
execution_profile: "explicit-profile", runtime_probe: probe, unavailable_remediation: ["configure_dsh_runtime"]};
for (const runtime_probe of [false, {}, {...probe, schema_version: "other"},
{...probe, scope: "whole_machine"}, {...probe, scope: ["probing_interpreter"]},
{...probe, module: "/private/path"},
{...probe, available: "false"}]) {
assert.throws(() => runtimePreflight({...executor, runtime_probe}), /runtime probe/);
}
for (const unavailable_remediation of [null, "configure_dsh_runtime", ["/private/path"],
["x".repeat(81)], Array(9).fill("configure_dsh_runtime")]) {
assert.throws(() => runtimePreflight({...executor, unavailable_remediation}), /runtime remediation/);
}
const legacy = runtimePreflight({executor: "dsh", available: true, unavailable_reason: null,
execution_profile: "explicit-profile"});
assert.equal(legacy.state, "launchable");
assert.deepEqual(legacy.executor,
{host: "dsh", available: true, reason: null, profile: "explicit-profile"});
});

test("requester adoption needs accepted downstream use, not reading, revision or prose", () => {
const artifact = {ref: "result.json", sha256: "a".repeat(64)};
const source = {operation_id: "source", status: "accepted", artifacts: [artifact]};
Expand Down
Loading
Loading