Skip to content
2 changes: 1 addition & 1 deletion docs/developers/qwen-serve-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -2439,7 +2439,7 @@ Return aggregate persisted session counts for the selected workspace without cha
}
```

`active`, `archived`, and `total` count local JSONL sessions. `live` is the matching in-memory bridge count and is omitted for a registered untrusted secondary workspace because that persisted-only read must not query live state. `expensive` is always `true` and `cost` is always `"disk_scan"`; clients must call this endpoint infrequently rather than poll it. If the scan reaches its safety limit or cannot classify every candidate file, the response adds `"truncated": true` and the persisted counts are lower bounds. Missing storage returns zero persisted counts. The plural route uses the same workspace selector and trust policy as the plural session catalog; an untrusted primary still returns `403 untrusted_workspace`.
`active`, `archived`, and `total` count user-visible local JSONL sessions; internal daemon host sessions are excluded. `live` is the matching user-visible in-memory bridge count and is omitted for a registered untrusted secondary workspace because that persisted-only read must not query live state. `expensive` is always `true` and `cost` is always `"disk_scan"`; clients must call this endpoint infrequently rather than poll it. If the scan reaches its safety limit or cannot classify every candidate file, the response adds `"truncated": true` and the persisted counts are lower bounds. Missing storage returns zero persisted counts. The plural route uses the same workspace selector and trust policy as the plural session catalog; an untrusted primary still returns `403 untrusted_workspace`.

The TypeScript daemon SDK exposes the plural route through `workspaceById(...)` or `workspaceByCwd(...)`, followed by `getWorkspaceSessionInfo()`.

Expand Down
96 changes: 66 additions & 30 deletions docs/plans/2026-09-06-multi-agent-board-collaboration.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Multi-agent collaboration on a shared thread

> Status: Revised after source-backed review. The admission foundation and three
> runtime preparation contracts are committed on #11206; the workspace
> transaction, durability contract, and execution path remain unbuilt.
> Status: Revised after source-backed review. Admission, runtime preparation,
> capability, and versioned storage are committed on #11206. The hidden-host
> launcher is locally implemented on its stacked step branch; dispatch remains
> unbuilt.
> Baseline: `origin/main` @ `703678136a` (2026-09-06)
> Verification: targeted tests, build, typecheck, and lint are recorded in §0.2;
> no agent has run this design end to end
Expand Down Expand Up @@ -85,12 +86,17 @@ continuations already emit `EXTERNAL_MESSAGE`, and cold revival explicitly
seeds the continuation prompt in the transcript. Mesh still uses structured
input because correlation, not transcript presence, is the missing contract.

**Verified locally in steps 2-3, not yet end to end.** The capability table and
**Verified locally in steps 2-4, not yet end to end.** The capability table and
shell predicate pass their named tests. The versioned store tests exercise
newer-version refusal, v0 migration and backup recovery, two-process sequence
allocation, source-first outbox replay, persisted admission outcomes, and
tree-wide token accounting. These are local observations only until the merged
step is green in #11206; they still do not launch an agent or run a dispatcher.
tree-wide token accounting. The step-4 tests exercise invocation-time tool
refusal, typed launcher outcomes, a singleton hidden host, default-catalog
exclusion, and host reload before a subsequent launch. The ACP bridge and child
handler have focused route tests. These are local observations only until the
merged step is green in #11206; the reload test runs the real bridge reaper
in-process with a fake ACP child, not a daemon process, and no mesh agent or
dispatcher has run against a live model.

**Still never prototyped end to end.** No mesh agent has been launched, no
thread has been dispatched, no prompt in §6 has been sent to a model. The
Expand Down Expand Up @@ -519,16 +525,20 @@ action on the child caused it.

## 5. Module map

Pre-existing on this branch (five production files plus two tests; nothing
starts an agent yet):

| File | Responsibility |
| ----------------------------------------- | ---------------------------------------------- |
| `core/src/agents/mesh/types.ts` | Entities and limits |
| `core/src/agents/mesh/mesh-store.ts` | Paths, validation, locking, CRUD |
| `core/src/agents/mesh/mentions.ts` | `@name` → agent ids |
| `core/src/agents/mesh/dispatch-policy.ts` | `decideDispatch` — pure |
| `core/src/agents/mesh/thread-actions.ts` | `postMessage` — append and book under one lock |
Implemented through the step-4 stacked branch (the launcher exists, but no
dispatcher invokes it yet):

| File | Responsibility |
| ----------------------------------------- | ------------------------------------------------------ |
| `core/src/agents/mesh/types.ts` | Entities and limits |
| `core/src/agents/mesh/mesh-store.ts` | Paths, validation, locking, CRUD, singleton host claim |
| `core/src/agents/mesh/mentions.ts` | `@name` → agent ids |
| `core/src/agents/mesh/dispatch-policy.ts` | `decideDispatch` — pure |
| `core/src/agents/mesh/thread-actions.ts` | `postMessage` — append and book under one lock |
| `core/src/agents/mesh/capability.ts` | Read-only name and invocation boundary |
| `core/src/agents/mesh/launcher.ts` | Persona conversion and typed local launch |
| `cli/src/serve/mesh/mesh-host-session.ts` | Hidden ACP host ownership, keepalive, reload |
| `acp-bridge` + `cli/src/acp-integration/` | Private daemon-to-host launch control |

### 5.1 Local review correction — committed and verified

Expand All @@ -545,11 +555,14 @@ starts an agent yet):
5. Stale daemon-session comments and the unreachable `explicit_routing` outcome
were removed. The latter remains a target-resolution rule.

Steps 1-3 now cover admission, capability classification, and the versioned
storage protocol. Delivery acknowledgement, assignment triggers, status
commands, provenance producers, and transcript slices remain scheduled below;
their storage fields exist because v1 deliberately batches the full §3 schema,
not because those behaviors have run.
Steps 1-3 cover admission, capability classification, and the versioned storage
protocol. Step 4 now has a source-tested hidden host and typed launcher; it is
not complete until the #11206 CI gate passes. Its daemon-process observation is
deferred to step 6, where the dispatcher first gives the host owner a server
caller. Delivery acknowledgement, assignment triggers, status commands,
provenance producers, and transcript slices remain scheduled below; their
storage fields exist because v1 deliberately batches the full §3 schema, not
because those behaviors have run.

### 5.2 Order of work

Expand All @@ -573,6 +586,9 @@ Dependencies, with an early vertical proof before reliability and UI breadth.
launch path. Prove resident continue and transcript-backed revive separately;
definition absence produces `agent_unavailable`, while registry saturation
produces `capacity_wait`. Runtime preparation is integrated on this branch.
The source implementation reuses the background-agent launch path and the
scheduled-task keepalive resume deadline; its stacked step remains subject
to the #11206 whole-branch CI gate and live-model validation in step 7.
5. **Run envelope and tools** — populate the §3 delivery/provenance fields, add
the prompt assembler, correlated mesh external-input/consumed events,
per-turn ambient mesh context, incremental run usage recording, and minimal `thread_post`,
Expand Down Expand Up @@ -618,7 +634,7 @@ child PR merges:
```bash
cd packages/core
npx vitest run src/agents/mesh/capability.test.ts
# 1 file, 10 tests passed
# 1 file, 12 tests passed (step 4 adds invocation and definition-narrowing checks)
```

Supporting local evidence for step 3; its gate is likewise #11206 CI after the
Expand All @@ -634,10 +650,32 @@ npx vitest run src/agents/mesh/mesh-store.test.ts \
# 5 files, 59 tests passed
```

The earlier foundation's targeted lint and core typecheck passed. Step 3 has
run only the named tests above; compile/style health waits for the whole-branch
CI gate. None of these checks validate the unbuilt execution path. Update the
test counts above when the implementation changes.
Supporting local evidence for step 4; #11206 CI remains its gate:

```bash
cd packages/core
npx vitest run src/agents/background-agent-resume.test.ts \
src/agents/background-tasks.test.ts \
src/agents/mesh/capability.test.ts \
src/agents/mesh/launcher.test.ts
# 4 files, 218 tests passed

cd packages/acp-bridge
npx vitest run src/bridge.test.ts
# 1 file, 914 tests passed

cd packages/cli
npx vitest run src/acp-integration/acpAgent.test.ts
# 1 file, 629 tests passed
npx vitest run src/serve/scheduled-task-keepalive.test.ts \
src/serve/mesh/mesh-host-session.test.ts
# 2 files, 34 tests passed; in-process bridge reload 4.3 ms after a 20 ms reap
```

The earlier foundation's targeted lint and core typecheck passed. Step 4's
targeted `acp-bridge` package build passed; whole-branch compile/style health
still waits for #11206 CI. None of these checks validates a mesh turn against a
live model. Update the test counts above when the implementation changes.

Future unit coverage is required for: all twelve admission outcomes; unknown
mention suppressing assignee fallback; assignment and parent-dependency triggers;
Expand All @@ -664,12 +702,10 @@ Still to build:

| Piece | Where |
| ----------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| Invocation-time enforcement of the read-only shell predicate | launcher/tool hook in steps 4-5 |
| Hidden host-session owner and programmatic launcher | `core/src/agents/` |
| Run envelope, delivery state, prompt assembler, ambient run context | `core/src/agents/mesh/` |
| Consume the integrated correlated external-input runtime contract | `core/src/agents/mesh/` |
| Thread tools: `thread_post`, `thread_wait`, `thread_block`, `thread_review`, `thread_create`, `thread_read` | `core/src/tools/` |
| Dispatcher, reconciliation, FIFO, sweeper, and keepalive | `cli/src/serve/mesh/` |
| Dispatcher, reconciliation, FIFO, and sweeper | `cli/src/serve/mesh/` |
| REST: agents, threads, posts, runs | `cli/src/serve/routes/mesh.ts` |
| Channel notifications for the four events | reuse the channel workers |
| Web Shell: roster, thread list, thread view, run transcripts | `web-shell/client/` |
Expand Down Expand Up @@ -991,6 +1027,6 @@ write code, which decision 1 defers until isolation is settled.

**规则修正**:turn gate 改为每线程,token gate 保持根树维度;子线程继承父线程当前 turn 计数;running coalesce 也计 turn;未知 @ 不再误唤醒 assignee;无目标、agent unavailable、capacity wait、launch failure、done/cancel、assignment trigger 都有明确语义;跨线程 queued run 按锁内分配的 `(queueSequence, runId)` 全局 FIFO,`queuedAt` 只用于显示。全局锁只处理并发,跨文件父报告和通知由可重放 outbox 保证,token 则从各 run 的逐轮 usage 推导;`blocked/in_review` 按所有 agent 的 run 聚合,不再由最后一个 agent 覆盖。

**验证边界**:当前规则/存储层已有定向测试、类型与 lint 证据;launcher线程工具、dispatcher、delivery watermark、恢复、REST、Web Shell、通知都还没端到端跑通。§5.2 把 live vertical slice 提前,§9 记录 11 个仍需产品或存储取舍的问题
**验证边界**:当前规则、存储、capability 和 launcher 路径已有定向测试与编译证据;隐藏 host 的 reload 测试使用注入 bridge,mesh agent 尚未对真实模型运行。线程工具、dispatcher、delivery watermark、恢复、REST、Web Shell、通知都还没端到端跑通。§5.2 把 live vertical slice 提前,§9 记录仍需产品或存储取舍的问题

</details>
23 changes: 18 additions & 5 deletions docs/plans/2026-09-07-mesh-implementation-acceptance.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,21 @@ Supporting local observation: `mesh-store.test.ts`, `workspace-lock.test.ts`,
### Step 4 — Hidden host session, keepalive, launcher

Lands: one hidden `Config` + registry per workspace; keepalive registration reusing `scheduled-task-keepalive.ts`; `launchMeshAgent(agent)` that builds the persona through `convertToRuntimeConfig` and starts a background agent; typed launch results `started | capacity_wait | agent_unavailable | launch_failed`.
Gate: (a) with `QWEN_CODE_MAX_BACKGROUND_AGENTS=1`, launching a second agent returns `capacity_wait` and books nothing; (b) an agent whose `agentType` names no definition returns `agent_unavailable` and no runtime is created; (c) the host session does not appear in the session list API; (d) after the reaper closes the host session, keepalive reloads it and the next launch succeeds without a daemon restart; (e) `continueResidentAgent` returns `continued` for a completed resident and `capacity_wait` never triggers a cold revive (#11204's tests, now on this branch).
Evidence: (d) is the one that needs the daemon; report the reaper timeout used and the observed reload latency.
Gate: (a) with `QWEN_CODE_MAX_BACKGROUND_AGENTS=1`, launching a second agent returns `capacity_wait` and books nothing; (b) an agent whose `agentType` names no definition returns `agent_unavailable` and no runtime is created; (c) the host session does not appear in the session list API; (d) after the real bridge reaper closes the host session, keepalive reloads it and the next launch succeeds without recreating the bridge; (e) `continueResidentAgent` returns `continued` for a completed resident and `capacity_wait` never triggers a cold revive (#11204's tests, now on this branch).
Evidence: report the reaper timeout and observed reload latency from an in-process `AcpSessionBridge` with a fake ACP child. Step 4 deliberately has no server-bootstrap caller before the dispatcher exists, so step 7 repeats this observation through the daemon dispatcher instead of adding unused wiring here.

Supporting local observations on the stacked step branch: `capability.test.ts`
and `launcher.test.ts` pass 16 tests; `background-agent-resume.test.ts` passes
52 tests including cold-revive capability restoration; `background-tasks.test.ts`
passes 150 tests including typed resident continuation; `mesh-host-session.test.ts`
and `scheduled-task-keepalive.test.ts` pass 34 tests; `bridge.test.ts` passes
914 tests; and `acpAgent.test.ts` passes 629 tests. A targeted `acp-bridge`
package build also succeeds. The real `AcpSessionBridge` reaper was configured
to 20 ms in-process with a fake ACP child; it closed the host, a second channel
resumed the same session, and the next launch returned `started` after a
measured 4.3 ms reload (1,000 ms resume deadline), without recreating the
bridge. The child merge and #11206's whole-branch CI remain the step gate; the
daemon-process observation is part of step 7 for the reason above.

### Step 5 — Run envelope, tools, runtime correlation

Expand All @@ -66,12 +79,12 @@ Evidence: the three tests. No live model.

Lands: nothing new; this is a run.
Gate: the §8 demo steps 1-5 complete against two real agents on a build-capable machine, plus: a forced `queueExternalInput` miss (kill the agent between its last tool round and finish) is rebooked and delivered on the next run; a synthetic ping-pong between two _running_ agents on one thread stops at 12 with `turn_budget_exhausted` in the thread and one channel-less notification record.
Evidence: the thread JSON files after the run, the two agents' transcript slices, and the observed wall-clock between the child's `thread_review` and the parent's wake. If any prompt in §6 had to change to make the model close its run explicitly, the changed prompt and the failure it fixed.
Evidence: the thread JSON files after the run, the two agents' transcript slices, the observed wall-clock between the child's `thread_review` and the parent's wake, and the host reaper timeout plus daemon-observed reload latency. If any prompt in §6 had to change to make the model close its run explicitly, the changed prompt and the failure it fixed.

### Step 8 — Dispatcher reliability

Lands: `delivery_race` detach/rebook; `launch_failed` with `failureStage`; done/cancel (`cancelling` state, runtime abort); restart recovery (`running` → reconcile → resume once → terminal on second failure); stall sweeper; full outbox replay on startup.
Gate: failure injection at each named point, as separate tests: enqueue returns false; process exit after `acceptedMessageIds` write; process exit after transcript record but before `consumedMessageIds` write; process exit after parent apply but before acknowledge; daemon restart with one `running` and one `queued` run; N-minute stall. Each test asserts the thread file's final state and that no message id is both unconsumed and unbooked.
Lands: `delivery_race` detach/rebook; `launch_failed` with `failureStage`; done/cancel (`cancelling` state, runtime abort); restart recovery (`running` → reconcile → resume once → terminal on second failure); stale host-session binding replacement after a definitive resume failure; stall sweeper; full outbox replay on startup.
Gate: failure injection at each named point, as separate tests: enqueue returns false; process exit after `acceptedMessageIds` write; process exit after transcript record but before `consumedMessageIds` write; process exit after parent apply but before acknowledge; daemon restart with one `running` and one `queued` run; a stored host session that cannot be resumed is replaced once; N-minute stall. Each test asserts the thread file's final state and that no message id is both unconsumed and unbooked.
Evidence: the injection matrix as a table in the PR, one row per test, with the asserted final state.

### Step 9 — REST and Web Shell
Expand Down
Loading
Loading