@bolloon/bolloon-agent 0.5.5 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/execution-entry.js +10 -0
- package/dist/agents/goal-flywheel/wiring/seams.js +5 -1
- package/dist/agents/parse-tool-call.js +6 -1
- package/dist/agents/pi-sdk-session-factory.js +36 -3
- package/dist/agents/pi-sdk-tools.js +18 -8
- package/dist/agents/pi-sdk.js +749 -1390
- package/dist/agents/run-context.js +20 -0
- package/dist/agents/runner-resolver.js +3 -1
- package/dist/agents/tool-capability/index.js +51 -0
- package/dist/agents/workflow-pivot-loop.js +31 -10
- package/dist/agents/write-verify.js +60 -0
- package/dist/bollharness-integration/integration.js +8 -0
- package/dist/bollharness-integration/skill-adapter.js +32 -3
- package/dist/electron-build/agents/goal-flywheel/wiring/seams.js +5 -1
- package/dist/electron-build/agents/goal-flywheel/wiring/seams.js.map +1 -1
- package/dist/electron-build/agents/parse-tool-call.js +6 -1
- package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-session-factory.js +36 -3
- package/dist/electron-build/agents/pi-sdk-session-factory.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-tools.js +21 -11
- package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-types.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +750 -1389
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/agents/run-context.js +24 -0
- package/dist/electron-build/agents/run-context.js.map +1 -0
- package/dist/electron-build/agents/runner-resolver.js +3 -1
- package/dist/electron-build/agents/runner-resolver.js.map +1 -1
- package/dist/electron-build/agents/tool-capability/index.js +90 -0
- package/dist/electron-build/agents/tool-capability/index.js.map +1 -0
- package/dist/electron-build/agents/workflow-pivot-loop.js +31 -10
- package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
- package/dist/electron-build/agents/write-verify.js +99 -0
- package/dist/electron-build/agents/write-verify.js.map +1 -0
- package/dist/electron-build/bollharness-integration/integration.js +8 -0
- package/dist/electron-build/bollharness-integration/integration.js.map +1 -1
- package/dist/electron-build/bollharness-integration/skill-adapter.js +32 -3
- package/dist/electron-build/bollharness-integration/skill-adapter.js.map +1 -1
- package/dist/electron-build/kernel/channel-actor.js +339 -0
- package/dist/electron-build/kernel/channel-actor.js.map +1 -0
- package/dist/electron-build/kernel/control.js +102 -0
- package/dist/electron-build/kernel/control.js.map +1 -0
- package/dist/electron-build/kernel/model-runtime.js +508 -0
- package/dist/electron-build/kernel/model-runtime.js.map +1 -0
- package/dist/electron-build/kernel/run-lifecycle.js +146 -0
- package/dist/electron-build/kernel/run-lifecycle.js.map +1 -0
- package/dist/electron-build/kernel/transport.js +119 -0
- package/dist/electron-build/kernel/transport.js.map +1 -0
- package/dist/electron-build/llm/native-adapter.js +122 -0
- package/dist/electron-build/llm/native-adapter.js.map +1 -0
- package/dist/index.js +43 -19
- package/dist/ios/bolloon-web.json +3 -3
- package/dist/ios/client.js +13 -7
- package/dist/ios/mobile-core.js +7762 -6289
- package/dist/ios/mobile.js +64 -0
- package/dist/ios/routes-mobile-tasks.js +2 -1
- package/dist/ios/routes-tasks.js +16 -1
- package/dist/ios/routes-x402-info.js +28 -1
- package/dist/ios/server-storage.js +26 -5
- package/dist/ios/server.js +840 -862
- package/dist/ios/sw.js +1 -1
- package/dist/ios/ui/message-renderer.js +473 -29
- package/dist/ios/ui/step-timeline.js +13 -12
- package/dist/kernel/channel-actor.js +321 -0
- package/dist/kernel/control.js +95 -0
- package/dist/kernel/gate-scan.js +1350 -0
- package/dist/kernel/model-runtime.js +494 -0
- package/dist/kernel/plan-channel-actor.js +305 -0
- package/dist/kernel/plan-communication.js +163 -0
- package/dist/kernel/plan-constraint.js +152 -0
- package/dist/kernel/plan-deletion.js +32 -0
- package/dist/kernel/plan-harness.js +78 -0
- package/dist/kernel/plan-modelruntime.js +60 -0
- package/dist/kernel/plan-runcontext.js +163 -0
- package/dist/kernel/plan.js +149 -0
- package/dist/kernel/roster.js +244 -0
- package/dist/kernel/run-lifecycle.js +138 -0
- package/dist/kernel/transport.js +111 -0
- package/dist/llm/native-adapter.js +117 -0
- package/dist/web/bolloon-web.json +3 -3
- package/dist/web/client.js +12 -7
- package/dist/web/routes-tasks.js +16 -1
- package/dist/web/server.js +838 -862
- package/dist/web/sw.js +1 -1
- package/dist/web/ui/message-renderer.js +473 -29
- package/dist/web/ui/step-timeline.js +13 -12
- package/package.json +1 -1
- package/scripts/build-web.ts +74 -1
- package/dist/constraint-runtime/_archive_helper.d.ts +0 -1
- package/dist/constraint-runtime/_archive_helper.js +0 -9
- package/dist/constraint-runtime/assistant/index.d.ts +0 -11
- package/dist/constraint-runtime/assistant/index.js +0 -12
- package/dist/constraint-runtime/bootstrap/index.d.ts +0 -11
- package/dist/constraint-runtime/bootstrap/index.js +0 -12
- package/dist/constraint-runtime/bridge/index.d.ts +0 -11
- package/dist/constraint-runtime/bridge/index.js +0 -12
- package/dist/constraint-runtime/buddy/index.d.ts +0 -11
- package/dist/constraint-runtime/buddy/index.js +0 -12
- package/dist/constraint-runtime/cli/index.d.ts +0 -11
- package/dist/constraint-runtime/cli/index.js +0 -12
- package/dist/constraint-runtime/components/index.d.ts +0 -11
- package/dist/constraint-runtime/components/index.js +0 -12
- package/dist/constraint-runtime/constants/index.d.ts +0 -11
- package/dist/constraint-runtime/constants/index.js +0 -12
- package/dist/constraint-runtime/coordinator/index.d.ts +0 -11
- package/dist/constraint-runtime/coordinator/index.js +0 -12
- package/dist/constraint-runtime/cost_hook.d.ts +0 -2
- package/dist/constraint-runtime/cost_hook.js +0 -4
- package/dist/constraint-runtime/entrypoints/index.d.ts +0 -11
- package/dist/constraint-runtime/entrypoints/index.js +0 -12
- package/dist/constraint-runtime/execution_registry.d.ts +0 -20
- package/dist/constraint-runtime/execution_registry.js +0 -44
- package/dist/constraint-runtime/hooks/index.d.ts +0 -11
- package/dist/constraint-runtime/hooks/index.js +0 -12
- package/dist/constraint-runtime/ink.d.ts +0 -1
- package/dist/constraint-runtime/ink.js +0 -4
- package/dist/constraint-runtime/keybindings/index.d.ts +0 -11
- package/dist/constraint-runtime/keybindings/index.js +0 -12
- package/dist/constraint-runtime/memdir/index.d.ts +0 -11
- package/dist/constraint-runtime/memdir/index.js +0 -12
- package/dist/constraint-runtime/migrations/index.d.ts +0 -11
- package/dist/constraint-runtime/migrations/index.js +0 -12
- package/dist/constraint-runtime/moreright/index.d.ts +0 -11
- package/dist/constraint-runtime/moreright/index.js +0 -12
- package/dist/constraint-runtime/native_ts/index.d.ts +0 -11
- package/dist/constraint-runtime/native_ts/index.js +0 -12
- package/dist/constraint-runtime/output_styles/index.d.ts +0 -11
- package/dist/constraint-runtime/output_styles/index.js +0 -12
- package/dist/constraint-runtime/plugins/index.d.ts +0 -11
- package/dist/constraint-runtime/plugins/index.js +0 -12
- package/dist/constraint-runtime/port_manifest.d.ts +0 -7
- package/dist/constraint-runtime/port_manifest.js +0 -11
- package/dist/constraint-runtime/query.d.ts +0 -6
- package/dist/constraint-runtime/query.js +0 -1
- package/dist/constraint-runtime/remote/index.d.ts +0 -11
- package/dist/constraint-runtime/remote/index.js +0 -12
- package/dist/constraint-runtime/schemas/index.d.ts +0 -11
- package/dist/constraint-runtime/schemas/index.js +0 -12
- package/dist/constraint-runtime/screens/index.d.ts +0 -11
- package/dist/constraint-runtime/screens/index.js +0 -12
- package/dist/constraint-runtime/server/index.d.ts +0 -11
- package/dist/constraint-runtime/server/index.js +0 -12
- package/dist/constraint-runtime/services/index.d.ts +0 -11
- package/dist/constraint-runtime/services/index.js +0 -12
- package/dist/constraint-runtime/state/index.d.ts +0 -11
- package/dist/constraint-runtime/state/index.js +0 -12
- package/dist/constraint-runtime/system_init.d.ts +0 -1
- package/dist/constraint-runtime/system_init.js +0 -20
- package/dist/constraint-runtime/types/index.d.ts +0 -11
- package/dist/constraint-runtime/types/index.js +0 -12
- package/dist/constraint-runtime/upstream_proxy/index.d.ts +0 -11
- package/dist/constraint-runtime/upstream_proxy/index.js +0 -12
- package/dist/constraint-runtime/utils/index.d.ts +0 -11
- package/dist/constraint-runtime/utils/index.js +0 -12
- package/dist/constraint-runtime/vim/index.d.ts +0 -11
- package/dist/constraint-runtime/vim/index.js +0 -12
- package/dist/constraint-runtime/voice/index.d.ts +0 -11
- package/dist/constraint-runtime/voice/index.js +0 -12
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **K5 台账: Channel Actor** (leo 2026-10-02 定口径)
|
|
3
|
+
*
|
|
4
|
+
* 从 K2 移交过来的背景: `messageHistory` / `currentChannelId` / `currentAgentId` / `currentGoalId`
|
|
5
|
+
* 四个字段**不是 Run 状态, 是会话状态** —— 它们不属于 RunContext, 该由 Channel Actor 统一承载。
|
|
6
|
+
* K2 已把它们标 `scope: 'session'` 并逐字保留在 Pi 实例上 (零搬迁), 这里记的是**最终归属与迁移步骤**。
|
|
7
|
+
*
|
|
8
|
+
* 判据: `src/test/kernel-channel-actor.test.ts` —— 台账必须与**盘上事实同步**:
|
|
9
|
+
* 标了"容器未建"就必须真的不存在 `src/kernel/channel-actor.ts`; 进度只许增; 未建/未迁的不许标成已完成。
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* 进度历史 (台账只许前进, 每次前进都要留下日期与"这一步交付了什么"):
|
|
13
|
+
* · 2026-10-02 not-started → **container-built**: `src/kernel/channel-actor.ts` 落地
|
|
14
|
+
* (ActorState 9 项 · SerialMailbox 串行队列 · ChannelActor.submit/abort/beginCancellation);
|
|
15
|
+
* 串行语义由 `kernel-channel-actor.test.ts` **真跑**验证; **尚未接任何入口** ⇒ 行为零改变。
|
|
16
|
+
* · 2026-10-02 container-built → **registry-built**: 注册表 `getOrCreateActor(peekActor/actorCount/resetActors)`
|
|
17
|
+
* 落地, 并由 **session factory** 在会话创建时按 channel 绑定 (`attachActor`) ⇒ 一个 channel 一个 actor 成立;
|
|
18
|
+
* `fieldsMigrated` 仍 0/4 · `entriesWired` 仍 0/4 (只做归属, 没有把执行投递进 mailbox, 状态仍在 Pi 实例上)。
|
|
19
|
+
* · 2026-10-02 **第 4 步第一版被全量回归否掉, 已回退** (证据留在 §28):
|
|
20
|
+
* 曾把 history 本体搬进 `actor.state.messageHistory` (Pi 侧改成访问器 + `attachActor` 收养)。
|
|
21
|
+
* 全量 **4742 测试 ⇒ 6 红**, 两类根因:
|
|
22
|
+
* ① **会话隔离被打破** (5 红): actor 的注册键是 `peerId` 的 `:` 前段 (或 `default`), 而
|
|
23
|
+
* **会话身份 (SessionStore key) 在 hydrate 时才出现** ⇒ 两个独立 session 共用同一个 actor,
|
|
24
|
+
* 新 session 一构造就看到别人的 history (测试实测 `expected 2 to be 0`)。
|
|
25
|
+
* ② **K2 门拦下** (1 红): 「session 级字段必须**仍是实例字段**」—— 第 4 步落地前不许留半搬状态。
|
|
26
|
+
* ⇒ **结论 (写进 K5 约束)**: history 归属**不能按 channel 前缀**, 必须按**会话身份**。
|
|
27
|
+
* · 2026-10-02 **第二版 (按约束重做, 成功)**: 注册键改成**会话身份** (`loadSessionKey` 优先, 否则整条 `peerId`),
|
|
28
|
+
* **不做前缀归并, 也没有 default 兜底桶** (没有身份 ⇒ 不归属: 宁可不共享, 不许串台);
|
|
29
|
+
* `messageHistory` 本体搬进 `actor.state.messageHistory` (Pi 侧访问器 + `attachActor` 收养当地历史)。
|
|
30
|
+
* 同时把 K2 门那条「session 级字段必须仍是实例字段」改成**读 K5 台账**的交接契约 (声明了才放行)。
|
|
31
|
+
* `fieldsMigrated` 0 → **1/4** (messageHistory); `entriesWired` 仍 0/4。
|
|
32
|
+
* · 2026-10-02 同一格继续: **history 的 hydrate / persist 实现体搬进 Actor**
|
|
33
|
+
* (`hydrateHistory` = load→filter→截断→替换, `historySnapshot` = persist 的取数拍, `appendMessage` 备用),
|
|
34
|
+
* 业务侧只交**纯回调** (内核不 import 业务模块) —— 三个操作**走邮箱** ⇒ 同 Channel 内与 append 串行,
|
|
35
|
+
* 不再有"读-改-写三拍被并发踩掉"和"边写边读抓到半截状态"。
|
|
36
|
+
* `historyOpsMigrated 2/4` (hydrate · persist)。
|
|
37
|
+
* · 2026-10-02 同格继续: **append 收敛** —— `pi-sdk.ts` 里 history 的写入面 (实测 push 31 · pop 1 · 整体赋值 3)
|
|
38
|
+
* 全部收敛到唯一漏斗 `pushHistory` / `popHistory` / `replaceHistory` ⇒ 每个直写模式只剩漏斗自身 1 处,
|
|
39
|
+
* 判据 `scanHistoryWriteSites` 断言这一点。**漏斗有意做成同步**: 调用点写完立刻要读 (length/索引/slice),
|
|
40
|
+
* 改成 await 会改变同拍可见性 —— 它交付的是**归属与可数性**, 并发安全由入口投递负责 (K5 第 5 步)。
|
|
41
|
+
* `historyOpsMigrated 3/4` (hydrate · append · persist)。
|
|
42
|
+
* · 2026-10-02 同格收官: **compact 落地拍** —— 压缩是 async, 从取快照到落地之间有 await 窗口,
|
|
43
|
+
* 期间 append 的消息原先会被整块替换**丢掉** (lost update) ⇒ 新增 `actor.rebaseHistory(compacted, snapshotLen)`
|
|
44
|
+
* (走邮箱 + 把快照之后的尾部原样接回); 同步压缩路径经核实是"同一拍相邻两行"(无窗口), 只留注释警戒。
|
|
45
|
+
* `historyOpsMigrated **4/4**` (hydrate · append · compact · persist)。
|
|
46
|
+
* · 2026-10-02 **步骤③ (三个会话绑定迁入 Actor)**: `currentChannelId` / `currentAgentId` / `currentGoalId`
|
|
47
|
+
* 的本体住进 `actor.state.channelId` / `.agentId` / `.goalBinding` (Pi 侧改成访问器 + `attachActor` 收养
|
|
48
|
+
* 构造期已设的值)。**这三个字段的访问数不变** (24 / 21 / 22) —— 迁的是**所有权**而非删访问,
|
|
49
|
+
* 所以本次**不动** K2 台账的冻结值 (判据的"少一处才红"在这里不适用, 因为一处也没少)。
|
|
50
|
+
* `fieldsMigrated **4/4**`。
|
|
51
|
+
* · 2026-10-02 **步骤④ 起手 (入口投递)**: `deliverThroughActor(holder, run)` 落进内核 —— 有 actor 就投进
|
|
52
|
+
* 它的 mailbox (同一会话身份的输入**排队**), 无 actor 直接跑 (行为不变)。**web 用户路径已接 3 处**
|
|
53
|
+
* (用户消息 / 第二条路径 / 重新生成), `web/server.ts` 里共 8 处入口执行点 ⇒ `entrySites { total: 8, wired: 3 }`,
|
|
54
|
+
* 两个数字都由判据 `scanEntryDelivery` **从盘上重算**, 自报无效。
|
|
55
|
+
* 四个**粗粒度**入口 (web / CLI / P2P / Supervisor) 的 `entriesWired` 仍 **0/4** —— 一条入口要全部执行点接完才算。
|
|
56
|
+
* · 2026-10-02 (K8 第二步/第三步): server.ts 11 → **8** —— 3 处 `promptStream(...)` 改走 `runExecution` (统一入口),
|
|
57
|
+
* 它们不再是"通道直呼 agent"的执行点 ⇒ 逐文件表跟着重建 (仍全部 wired)。
|
|
58
|
+
* · 2026-10-02 **步骤④ 完成**: web (server.ts 11/11 + routes-tasks 1/1) · CLI (index.ts) · P2P 入站 · Supervisor/子Agent
|
|
59
|
+
* (runner-resolver.ts 1/1) ⇒ `entriesWired **4/4**`。
|
|
60
|
+
* **关键发现 (口径漏了一整类入口)**: P2P 入站调的是 `a.summarizeDocument(...)` / `a.improveDocument(...)` ——
|
|
61
|
+
* 不叫 `prompt`, 只数 prompt/promptStream 会让这条入口**永远数不到** (实测 index.ts 漏 4 处)。
|
|
62
|
+
* ⇒ 口径的方法名单 `AGENT_ENTRY_METHODS` 也做成台账数据 (冻结), 并配判别力用例。
|
|
63
|
+
*/
|
|
64
|
+
/** K5 第 4 步: 四个 history 操作 (唯一来源; 台账 `historyOpsNames` 必须 ⊆ 这里, 且数量与进度位一致) */
|
|
65
|
+
export const HISTORY_OPS = ['hydrate', 'append', 'compact', 'persist'];
|
|
66
|
+
/** Actor 状态容器必须承载的 9 项 (leo 定的清单) */
|
|
67
|
+
export const ACTOR_STATE_ITEMS = [
|
|
68
|
+
{ name: 'channelId', why: "会话绑定的通道 —— 迁移前是 Pi 实例字段 (match 计数 24)", owner: 'actor' },
|
|
69
|
+
{ name: 'agentId', why: "会话身份 —— 迁移前 21 处", owner: 'actor' },
|
|
70
|
+
{ name: 'goalBinding', why: "会话的 Goal 默认绑定 —— **保留为 Actor 级默认**, 但每次执行开始必须把最终绑定写进 RunContext/Run 记录", owner: 'actor' },
|
|
71
|
+
{ name: 'messageHistory', why: "**Channel 的连续记忆** (不是 Run 历史): 同 Channel 串行访问, 跨 Channel 必须隔离", owner: 'actor' },
|
|
72
|
+
{ name: 'mailbox', why: "入站消息队列 —— 同 Channel 内只能排队, 不能并发改 history", owner: 'actor' },
|
|
73
|
+
{ name: 'activeRun', why: "当前活跃 Run (取代 Pi 上的 currentRunId 播种)", owner: 'actor' },
|
|
74
|
+
{ name: 'cancellation', why: "取消位 (取代 Pi 上的 currentSignal)", owner: 'actor' },
|
|
75
|
+
{ name: 'outboundStream', why: "出站流 (取代 Pi 上的 currentOnStream)", owner: 'actor' },
|
|
76
|
+
{ name: 'serialLock', why: "**同 Channel 串行执行锁** (1 个 channel 同时只有 1 个 Run 在改状态)", owner: 'actor' },
|
|
77
|
+
];
|
|
78
|
+
/** K5 验收标准 (leo 2026-10-02; 其中「并发 Run 的 history 不互相污染」是从 K2 移过来的) */
|
|
79
|
+
export const K5_ACCEPTANCE = [
|
|
80
|
+
'① 同 Channel 消息串行 (输入只能排队, 不能并发改 history)',
|
|
81
|
+
'② 不同 Channel history 完全隔离 (不交叉)',
|
|
82
|
+
'③ 同一 Channel 的多个 Run 不互相污染 (Actor 串行调度 + Run 边界隔离)',
|
|
83
|
+
'④ 页面 / CLI / P2P / Supervisor 都进入同一 Actor mailbox',
|
|
84
|
+
'⑤ Actor 崩溃后可由 Supervisor 恢复',
|
|
85
|
+
'⑥ messageHistory 不再由 Pi 直接拥有',
|
|
86
|
+
];
|
|
87
|
+
/** 8 步迁移步骤 (leo 定的顺序) —— 每步都要带真跑 */
|
|
88
|
+
export const K5_STEPS = [
|
|
89
|
+
'① 创建 Actor 状态容器 (9 项)',
|
|
90
|
+
'② messageHistory 的 hydrate / compact / append / persist 迁入 Actor',
|
|
91
|
+
'③ currentChannelId / currentAgentId / currentGoalId 迁入 Actor',
|
|
92
|
+
'④ 所有入口改为投递消息 (submit → mailbox)',
|
|
93
|
+
'⑤ 每个 Channel 内加串行执行锁',
|
|
94
|
+
'⑥ currentRunId 改成 Actor 的 activeRun / ExecutionFrame',
|
|
95
|
+
'⑦ Pi 只接收一次性的 ExecutionRequest',
|
|
96
|
+
'⑧ 删除 Pi 中对应字段',
|
|
97
|
+
];
|
|
98
|
+
/** 删除旧字段的 7 条前置条件 (全满足才许删 —— 对应 §7.3 删除条件的 K5 细化) */
|
|
99
|
+
/**
|
|
100
|
+
* K5 的 7 条删除前置。每条**必须点名"谁证明它"** (`backedBy`) —— 说"已验证"却没点名, 或点了一个盘上不存在的文件/判据,
|
|
101
|
+
* 都由 `scanPreconditionBacking` 判红 (不谎称可验证性: 判据只核"背书存在", 不代替真跑)。
|
|
102
|
+
*/
|
|
103
|
+
export const K5_DELETION_PRECONDITIONS = [
|
|
104
|
+
{
|
|
105
|
+
text: 'PiAgentSession 不再拥有 session 状态',
|
|
106
|
+
backedBy: ['src/test/kernel-channel-actor.test.ts', 'K5_FIELD_DELETION'],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
text: '所有入口都经过 Channel Actor',
|
|
110
|
+
backedBy: ['src/test/kernel-channel-actor.test.ts'], // scanEntryDelivery: wiredTotal === total
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
text: '同 Channel 串行、跨 Channel 并发**真跑通过**',
|
|
114
|
+
backedBy: ['src/test/kernel-channel-actor.test.ts'], // 串行语义 + ALS 防重入自锁
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
text: '重启后 history / Goal / Run 仍能正确恢复',
|
|
118
|
+
backedBy: ['src/test/session-resume-e2e.test.ts', 'src/test/persistence-e2e-flow.test.ts'],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
text: 'currentRunId 不再由 Pi 负责播种',
|
|
122
|
+
backedBy: ['scanRunIdSeed'], // CURRENT_RUN_ID_SEED_READS/_SEED_SITES
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
text: 'Pi 的字段访问只剩推理所需的临时变量',
|
|
126
|
+
backedBy: ['K5_ACCESSOR_SURFACE'],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
text: '旧字段零引用门禁通过',
|
|
130
|
+
backedBy: ['K5_FIELD_DELETION'], // scanStagingFieldDeletion 双向
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
/**
|
|
134
|
+
* **K5 步骤⑧ 的棘轮**: "本体已进 actor"之后, pi-sdk 里对这批**访问器**的引用数只许减、不许再增。
|
|
135
|
+
* 删访问器本身要改 ~129 处 + 10 个文件 (跨 web/CLI/agents), 属大范围改名 ⇒ 立棘轮而不是硬改。
|
|
136
|
+
* 计数变化 ⇒ 台账必须在**同一次提交**里跟上 (台账与盘上事实必须同步)。
|
|
137
|
+
*/
|
|
138
|
+
export const K5_ACCESSOR_SURFACE = {
|
|
139
|
+
accessorFields: ['messageHistory', 'currentChannelId', 'currentAgentId', 'currentGoalId', 'currentRunId'],
|
|
140
|
+
frozenInPiSdk: { messageHistory: 0, currentChannelId: 0, currentAgentId: 0, currentGoalId: 0, currentRunId: 0 }, // 批次5: 5 个访问器全部归零
|
|
141
|
+
where: 'src/agents/pi-sdk.ts',
|
|
142
|
+
why: '字段本体已删 (K5_FIELD_DELETION); 这些访问器只剩"读门面"作用 ⇒ 只许减',
|
|
143
|
+
};
|
|
144
|
+
/** 从 K2 移交的 4 个 session 字段 (现仍是 Pi 实例字段, match 口径冻结值) */
|
|
145
|
+
export const K5_INHERITED_FIELDS = [
|
|
146
|
+
{ name: 'messageHistory', into: 'actor.messageHistory', accesses: 0 }, // 步骤⑧ 批次 1: pi-sdk 侧 0 引用
|
|
147
|
+
// K5 步骤⑦ 起 +1: `applyExecutionRequest` 把请求里的绑定写进这三个字段 (各一处写)
|
|
148
|
+
{ name: 'currentChannelId', into: 'actor.channelId', accesses: 0 }, // 步骤⑧ 批次4: pi-sdk 侧 0 引用
|
|
149
|
+
{ name: 'currentAgentId', into: 'actor.agentId', accesses: 0 }, // 步骤⑧ 批次2: pi-sdk 侧 0 引用
|
|
150
|
+
{ name: 'currentGoalId', into: 'actor.goalBinding', accesses: 0 }, // 步骤⑧ 批次3: pi-sdk 侧 0 引用
|
|
151
|
+
];
|
|
152
|
+
/**
|
|
153
|
+
* 从 K2 移交的四个 session 字段的**当前**访问数 (match 口径, 与 K2 台账逐字相等 —— 跨台账判据强制)。
|
|
154
|
+
* ⚠️ `messageHistory` 由 56 降到 **22**: K5 第 4 步把 35 处写入 (push 31 · pop 1 · 整体赋值 3)
|
|
155
|
+
* 收敛进唯一漏斗 (`pushHistory`/`popHistory`/`replaceHistory`) 后这些点不再直接访问实例字段;
|
|
156
|
+
* 随后 compact 落地又**加了 1 处** (`snapshotLen = this.messageHistory.length`, 压缩的取快照拍)。
|
|
157
|
+
* 这不是"泄漏消失", 是**迁移动作的可见痕迹** —— 数字对不上就必须回去核 (门已两次拦下这类不同步)。
|
|
158
|
+
*/
|
|
159
|
+
/**
|
|
160
|
+
* **启动一次执行的 AgentSession 方法名单** (判据口径的数据来源, 冻结)。
|
|
161
|
+
* 为什么必须列全: 只数 `prompt/promptStream` 会让"走文档摘要/改写进来的入口"(P2P 入站)
|
|
162
|
+
* **永远数不到** —— 实测 `index.ts` 因此漏了 4 处 (`summarizeDocument` ×2 · `improveDocument` ×2)。
|
|
163
|
+
* 故意不计的: `readDocument` (纯 IO) · `suggestRename` (单次小调用, 不写会话历史) ·
|
|
164
|
+
* `runWorkflow` (内部会再调 prompt ⇒ 计入会双算)。
|
|
165
|
+
*/
|
|
166
|
+
export const AGENT_ENTRY_METHODS = [
|
|
167
|
+
'prompt', 'promptStream', 'promptWithPivotLoop', 'summarizeDocument', 'improveDocument',
|
|
168
|
+
// K5 步骤⑦ 新增的唯一入口: 请求式投递 (`runExecution`) 也是**启动一次执行** ——
|
|
169
|
+
// 不列进来会让"改写成请求式"的站点从执行点计数里**消失** (实测: 一改成请求式, 该文件 total 就从 11 掉到 10)。
|
|
170
|
+
'runExecution',
|
|
171
|
+
];
|
|
172
|
+
/**
|
|
173
|
+
* **K5 步骤⑤ channel 级串行锁的状态** (数据; 判据要求 `enabled` 与盘上调用点数一致):
|
|
174
|
+
* · 证据 (为什么默认不启用): web 的 channel 只有一个 `currentSessionId` ⇒ 用户消息都投到**同一个会话身份**
|
|
175
|
+
* ⇒ 身份级串行 (mailbox) **已经把这个 channel 的活跃会话串起来了** (验收①在活跃会话上已成立);
|
|
176
|
+
* · 什么时候才需要它: 会话切换后旧会话仍在内存 ⇒ 两个身份可能并行 (**各写自己的 history, 无污染**);
|
|
177
|
+
* 若要"跨会话切换也串行", 才需要 channel 级锁;
|
|
178
|
+
* · 代价: 同 channel 的**多个 agent** (P2P 多智能体) 也会被串起来 ⇒ 吞吐下降。
|
|
179
|
+
* ⇒ 是否全局启用 = **意图层决定** (leo); 能力 (`deliverThroughActor(..., {serializeByChannel:true})`) 已备好。
|
|
180
|
+
*/
|
|
181
|
+
export const K5_CHANNEL_LOCK = {
|
|
182
|
+
available: true,
|
|
183
|
+
enabled: false,
|
|
184
|
+
/** 盘上真正传了 `serializeByChannel: true` 的调用点数量 (判据从盘上重算, 不许自报) */
|
|
185
|
+
callSites: 0,
|
|
186
|
+
/** 证据出处 (一 channel 一活跃会话身份) */
|
|
187
|
+
evidence: 'web/server.ts:1566-1567 — channel.currentSessionId ⇒ sessionKey = <channelId>:<currentSessionId>',
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* **K5 步骤⑥**: Run 身份的归属 (run-boundary 作用域, 从 K2 的"唯一播种读取"收口过来)。
|
|
191
|
+
* `currentRunId` 的本体从 Pi 实例字段搬进 `actor.state.activeRun` —— 判据 `scanRunBoundaryResidence`
|
|
192
|
+
* 会核对盘上源码里**是否还有** `private currentRunId` 声明 (双向: 声明了却没标未迁 · 没声明却标未迁, 都红)。
|
|
193
|
+
*/
|
|
194
|
+
export const K5_RUN_BOUNDARY = {
|
|
195
|
+
field: 'currentRunId',
|
|
196
|
+
into: 'actor.activeRun',
|
|
197
|
+
migrated: true,
|
|
198
|
+
/** K2 留下的唯一播种读取 (不因本步而增加; 由 K2 的门强制) */
|
|
199
|
+
seedReads: 1,
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
/**
|
|
203
|
+
* **K5 步骤⑧ — 实例侧"绑定前暂存"字段已删除** (data; 判据核对盘上是否真的没有它们)。
|
|
204
|
+
* Pi 出生就带一个**私有 actor** (未注册 ⇒ 谁也拿不到), 工厂知道会话身份时再升级成身份键 actor 并**收养**状态
|
|
205
|
+
* ⇒ 实例侧不再需要暂存字段。删掉它们同时消灭了两类隐患: "两份真相" 与 "兜底分支写错成自递归"。
|
|
206
|
+
*/
|
|
207
|
+
export const K5_FIELD_DELETION = {
|
|
208
|
+
/** 本源里曾经有过的暂存字段全清单 (双向判据的另一半: 少登记一个 ⇒ 红) */
|
|
209
|
+
sourceFields: ['_history', '_channelId', '_agentId', '_goalId', '_runId'],
|
|
210
|
+
deletedStagingFields: ['_history', '_channelId', '_agentId', '_goalId', '_runId'],
|
|
211
|
+
why: 'actor 从出生就在 (私有无身份 / 身份键有身份), 暂存那份多余; 删掉即"删字段"这一步的可见痕迹',
|
|
212
|
+
};
|
|
213
|
+
/** K5 步骤⑦: 一次性 `ExecutionRequest` 的收敛进度。
|
|
214
|
+
* Pi 侧已加唯一入口 (`applyExecutionRequest` 落绑定 · `runExecution` 派发);
|
|
215
|
+
* 入口面 **converted = 1** (web 用户消息路径)。
|
|
216
|
+
* `remaining` 是**派生值** (已投递点数 − converted) —— 位置式形态太多, 行级正则数不准,
|
|
217
|
+
* 与其编一个假精确的门, 不如把"请求式"这半钉死, 剩下用算术表示。
|
|
218
|
+
*/
|
|
219
|
+
export const K5_EXECUTION_REQUEST = {
|
|
220
|
+
methodAdded: true,
|
|
221
|
+
// 2026-10-02 (K8 第二步): 1 → 3 —— routes-tasks.ts / runner-resolver.ts 两处改走 `runExecution`
|
|
222
|
+
// (K5 这张台账记的正是"请求式投递的收敛进度", K8 的迁移会推进它 ⇒ 跨台账必须同步)
|
|
223
|
+
converted: 12, // 2026-10-02: 6 → 10 → 13 → **12** (K8 正刀删掉 `runMessageFromQueue` 简化版路径 ⇒ 随它消失 1 处)
|
|
224
|
+
wiredTotal: 23, // 2026-10-02 正刀: 24 → **23** (删掉的 `runMessageFromQueue` 自己也有一处请求式投递点)
|
|
225
|
+
remaining: 11, // 派生: wiredTotal − converted = 23 − 12 = 11 (与盘上一致)
|
|
226
|
+
};
|
|
227
|
+
/** 入口 → 文件分组 (判据做**双向**校验: 说完成 ⇒ 其文件必须全接完; 说没完成 ⇒ 必须真有文件没接完) */
|
|
228
|
+
export const K5_ENTRY_GROUPS = [
|
|
229
|
+
{ entry: 'web', files: ['web/server.ts', 'web/routes-tasks.ts'], wired: true },
|
|
230
|
+
{ entry: 'CLI', files: ['index.ts', 'cli/interface.ts'], wired: true },
|
|
231
|
+
{ entry: 'P2P 入站', files: ['index.ts'], wired: true },
|
|
232
|
+
{ entry: 'Supervisor/子 Agent', files: ['agents/runner-resolver.ts'], wired: true },
|
|
233
|
+
];
|
|
234
|
+
/** 进度位 —— 门强制与盘上事实同步 (进度只许增; 未建的不许标已建) */
|
|
235
|
+
/**
|
|
236
|
+
* **K5 步骤⑧ 的终态声明** —— "删字段"这一步做完了。
|
|
237
|
+
* 声明**不是自报**: 判据 `scanStep8Completion` 会把三个条件**从盘上重算** (访问器引用数 / 暂存字段声明 / 入口投递),
|
|
238
|
+
* 任何一条不成立 ⇒ 红; 反过来"事实已完成而台账说没完成" ⇒ 也红 (双向, 台账必须与事实同步)。
|
|
239
|
+
*/
|
|
240
|
+
export const K5_STEP8 = {
|
|
241
|
+
claimedComplete: true,
|
|
242
|
+
/** ① 5 个访问器在 pi-sdk 侧 0 引用 (由 scanAccessorSurface 从盘上重算) */
|
|
243
|
+
accessorRefsFrozenAtZero: K5_ACCESSOR_SURFACE.accessorFields.length,
|
|
244
|
+
/** ② 5 个"绑定前暂存"字段的声明不存在 (由 scanStagingFieldDeletion 的双向判据核) */
|
|
245
|
+
stagingFieldsDeleted: K5_FIELD_DELETION.deletedStagingFields.length,
|
|
246
|
+
/** ③ 入口投递 4/4 (由 scanEntryDelivery 从盘上重算) */
|
|
247
|
+
entriesWired: 4,
|
|
248
|
+
entriesTotal: 4,
|
|
249
|
+
why: '⑧ 完成的三个可数条件 —— 声明即需条件成立, 判据从盘上重算; 任一不成立或"事实已完成而台账说没完成"都判红',
|
|
250
|
+
};
|
|
251
|
+
export const K5_PROGRESS = {
|
|
252
|
+
stage: 'field-deletion-complete',
|
|
253
|
+
/** Actor 容器文件路径 (存在性由门真读盘核对) */
|
|
254
|
+
containerPath: 'kernel/channel-actor.ts',
|
|
255
|
+
fieldsMigrated: 4,
|
|
256
|
+
/** 已迁字段名单 —— 门强制 `length === fieldsMigrated` 且每个名字都在 K5_INHERITED_FIELDS 里 */
|
|
257
|
+
migratedFieldNames: ['messageHistory', 'currentChannelId', 'currentAgentId', 'currentGoalId'],
|
|
258
|
+
fieldsTotal: 4,
|
|
259
|
+
entriesWired: 4,
|
|
260
|
+
entriesTotal: 4,
|
|
261
|
+
/** 判据口径用的方法名单 (数据; 见 AGENT_ENTRY_METHODS 的说明) */
|
|
262
|
+
entryMethods: AGENT_ENTRY_METHODS,
|
|
263
|
+
/** 入口分组 (判据做双向校验: 说完成 ⇒ 文件必须全接完) */
|
|
264
|
+
entryGroups: K5_ENTRY_GROUPS,
|
|
265
|
+
/**
|
|
266
|
+
* K5 步骤④ 的细粒度进度: **全部入口面的执行点清单**。
|
|
267
|
+
* 两个数字都由判据 `scanEntryDelivery` 用同一口径**从盘上重算** (剥注释 / 排除 `this.prompt` / 非流式也算), 自报无效。
|
|
268
|
+
*/
|
|
269
|
+
entrySites: [
|
|
270
|
+
// `excludeReceivers` 是**已核实的非执行点 receiver** (台账数据, 冻结; 改动会出现在 diff 里):
|
|
271
|
+
// `this` = CLI readline 提示 (`this.prompt('> ')`); `s` = index.ts 里的 UI 打印助手 (`s.prompt('📩 …')`)
|
|
272
|
+
// 2026-10-02 (K8 第二/三步): 3 处 `promptStream(...)` 改走唯一入口 `runExecution`。
|
|
273
|
+
// 口径说明: `runExecution` 也在方法名单里, 且**助手形态** `requireRunExecution(...)` 也被认作执行点
|
|
274
|
+
// ⇒ 迁移前后 total/wired 都不变 (11/11); 真正在变的是 K8 台账的"通道直呼 prompt"直连数 (10 → 7)。
|
|
275
|
+
{ file: 'web/server.ts', total: 10, wired: 10, excludeReceivers: ['this'] }, // web 用户/中继/任务/心跳 (2026-10-02 正刀: 11 → 10, 简化版路径删除)
|
|
276
|
+
{ file: 'web/routes-tasks.ts', total: 1, wired: 1, excludeReceivers: ['this'] }, // web 任务路由 (K8 后是请求式点; total 不降)
|
|
277
|
+
{ file: 'index.ts', total: 11, wired: 11, excludeReceivers: ['this', 's'] }, // CLI 主入口 + P2P 入站 (含文档摘要/改写)
|
|
278
|
+
{ file: 'cli/interface.ts', total: 0, wired: 0, excludeReceivers: ['this'] }, // readline ⇒ 无执行点
|
|
279
|
+
{ file: 'agents/runner-resolver.ts', total: 1, wired: 1, excludeReceivers: ['this'] }, // 子 Agent / Supervisor 面
|
|
280
|
+
],
|
|
281
|
+
/** K5 第 4 步里的 history **操作**搬迁 (4 个: hydrate/append/compact/persist) */
|
|
282
|
+
historyOpsMigrated: 4,
|
|
283
|
+
historyOpsTotal: 4,
|
|
284
|
+
/** 已搬操作名单 —— 门强制 `length === historyOpsMigrated` 且每个名字都在 HISTORY_OPS 里 */
|
|
285
|
+
historyOpsNames: ['hydrate', 'append', 'compact', 'persist'],
|
|
286
|
+
};
|
|
287
|
+
/** 四个入口 (leo 点名的) —— 全部要进同一 mailbox */
|
|
288
|
+
/**
|
|
289
|
+
* history 写入面的**迁移前实测值** (2026-10-02 grep 计数, 见 `scanHistoryWriteSites`):
|
|
290
|
+
* 判据是"每个模式最多剩 1 处直写 (漏斗自身)" —— 若哪天这两个数字对不上盘上事实, 说明有人绕过漏斗。
|
|
291
|
+
*/
|
|
292
|
+
export const HISTORY_WRITE_SITES = { push: 31, pop: 1, assign: 3 };
|
|
293
|
+
/** history 写入的**唯一漏斗** (三个方法名; 判据要求它们真的存在) */
|
|
294
|
+
/**
|
|
295
|
+
* history 写入的三个唯一漏斗。**可见性也是台账的一部分** —— 判据的语义是"漏斗存在且形态与账一致",
|
|
296
|
+
* 所以把 `replaceHistory` 升成公开 (步骤⑧: 它是唯一的"种历史"入口, 上层/测试必须能调) 也必须现形于此。
|
|
297
|
+
*/
|
|
298
|
+
export const HISTORY_WRITE_FUNNEL = [
|
|
299
|
+
{ name: 'pushHistory', vis: 'private' },
|
|
300
|
+
{ name: 'popHistory', vis: 'private' },
|
|
301
|
+
{ name: 'replaceHistory', vis: 'public', why: '步骤⑧: 唯一的"种历史"入口 (直接赋数组会换掉数组身份 ⇒ 静默丢数据)' },
|
|
302
|
+
];
|
|
303
|
+
export const K5_ENTRIES = ['web/server.ts', 'src/cli', 'P2P 入站', 'Supervisor'];
|
|
304
|
+
/** Goal 绑定必须是**显式操作**, 不许靠裸字段隐式生效 (leo 口径) */
|
|
305
|
+
export const K5_GOAL_BINDING_RULE = '运行中重新绑定 Goal 必须经过显式 Goal Binding 操作 (写 RunContext/Run 记录), 不许靠裸字段隐式生效';
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* K8 台账 (2026-10-02 建) —— **Communication Runtime 收口**
|
|
3
|
+
*
|
|
4
|
+
* 目标形态: `transport → router → channel mailbox`
|
|
5
|
+
* **绝不**: `transport → agent.promptStream()` (通道各自直呼 Agent = 通信层长出 N 条执行路径,
|
|
6
|
+
* 每条的流式/错误/重试/恢复语义都不一样, 没人能一眼看清"一条消息到底怎么被处理")。
|
|
7
|
+
*
|
|
8
|
+
* 本文件是 K8 的**第一步 (台账)**; 判据在 `gate-scan.ts` 的 `countTransportAgentSites` +
|
|
9
|
+
* `scanCommunicationLedger` 里重算比对 —— **台账与盘上事实必须逐字一致**。
|
|
10
|
+
*/
|
|
11
|
+
/** K8 要统一的 8 个事件面 (设计页 §7 原文口径) */
|
|
12
|
+
export const K8_EVENT_FACES = [
|
|
13
|
+
'web-message', // Web 普通消息
|
|
14
|
+
'web-stream', // Web 流式回复
|
|
15
|
+
'web-regen', // Web 重生成
|
|
16
|
+
'cli', // CLI 通道
|
|
17
|
+
'p2p', // P2P 对等消息
|
|
18
|
+
'mobile', // 手机端
|
|
19
|
+
'contacts-reply', // 联系人自动回复
|
|
20
|
+
'external-wake', // 外部唤醒
|
|
21
|
+
'cron', // 定时任务
|
|
22
|
+
'supervisor', // Supervisor 事件
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* 迁移记录 (每题一处, 都要有"为什么这次是零差量/有差量"的判断):
|
|
26
|
+
* · 2026-10-02 第二步: `routes-tasks.ts` / `runner-resolver.ts` 改走 `runExecution`
|
|
27
|
+
* —— routes-tasks 那处**有差量** (隐式绑定 → 显式 `channelId`), 记为待真跑核验。
|
|
28
|
+
* · 2026-10-02 第三步: `web/server.ts` **10 → 7** —— 三处 `promptStream(p, cb, undefined, channelId)`
|
|
29
|
+
* 改走 `runExecution({ input, onStream, channelId })`: 参数与派发**完全一致** ⇒ **按构造零差量** (不新增任何绑定)。
|
|
30
|
+
*/
|
|
31
|
+
export const K8_TRANSPORT_AGENT_SITES = [
|
|
32
|
+
// K8 第五步: 10 → 7 → 3 → 0 (★ server.ts 已零直连)
|
|
33
|
+
{ file: 'src/web/server.ts', count: 0, kind: 'via-actor', faces: ['web-message', 'web-stream', 'web-regen', 'cron', 'supervisor', 'external-wake'], status: 'open', why: 'web 通道 (消息/流式/重生成/cron/Supervisor/外部唤醒)' },
|
|
34
|
+
{ file: 'src/web/routes-tasks.ts', count: 0, kind: 'via-actor', status: 'migrated', evidence: '改经 runExecution({input}) —— 行为等价 (applyExecutionRequest 只覆盖显式给出的字段)', faces: ['contacts-reply'], why: 'task 路由' },
|
|
35
|
+
{ file: 'src/agents/runner-resolver.ts', count: 0, kind: 'via-actor', status: 'migrated', evidence: 'routes-tasks 那处由隐式绑定改为**显式**传 channelId —— **2026-10-02 真跑核验通过** (src/test/k8-routes-tasks-execution.test.ts: 两半 id 一致的自检不变量 + agent 无 prompt 也跑通 + 真变异判红)', faces: ['external-wake'], why: '独立宿主 runner' },
|
|
36
|
+
{ file: 'src/cli-entry.ts', count: 0, kind: 'direct-prompt', status: 'open', faces: ['cli'], why: 'CLI 通道' },
|
|
37
|
+
{ file: 'src/web/mobile-core.ts', count: 0, kind: 'direct-prompt', status: 'open', faces: ['mobile'], why: '手机端通道' }
|
|
38
|
+
];
|
|
39
|
+
/** 各通道自带的出站/重试/恢复状态 (K8 验收: "无各通道自己的重试/任务恢复/outbound 状态")
|
|
40
|
+
*
|
|
41
|
+
* 纪律: **按符号记, 不按行号** —— 行号会漂 (K7 已发生过一次), 门的核法 = "该符号真在该文件里"。
|
|
42
|
+
* 口径 (2026-10-02 逐符号定性, 修正过一次): 每个符号必须带 **scope** —— 因为"文件里出现的状态"
|
|
43
|
+
* 不等于"通道自己的 outbound/重试/恢复"。把 UI 展示状态当收口对象是**伪收口**。
|
|
44
|
+
*/
|
|
45
|
+
export const K8_STATE_SCOPES = ['k8-target', 'ui-state', 'domain-pending', 'infra-retry'];
|
|
46
|
+
const T = (name) => ({ name, scope: 'k8-target' });
|
|
47
|
+
export const K8_PER_CHANNEL_STATE = [
|
|
48
|
+
{ file: 'src/network/p2p-outbox.ts', kind: 'outbox',
|
|
49
|
+
symbols: ['outbox', 'outboxFile', 'outboxStats', 'totalQueued', 'sendOrQueue', 'queueOnly', 'flushAllOutboxes'].map(T),
|
|
50
|
+
why: 'P2P 出站队列 (通道自己的 outbound 状态 + 自己的 flush 时机)' },
|
|
51
|
+
{ file: 'src/web/delivery-ledger.ts', kind: 'delivery-ledger',
|
|
52
|
+
symbols: ['DeliveryLedger', 'DeliveryRecord', 'DeliveryState', 'LedgerStats'].map(T),
|
|
53
|
+
why: '投递台账 (通道自己的投递状态机)' },
|
|
54
|
+
{ file: 'src/web/server-v3-p2p.ts', kind: 'retry',
|
|
55
|
+
symbols: ['v3PendingHistoryGets', 'getV3PendingHistoryGets'].map(T),
|
|
56
|
+
why: 'P2P 侧挂起的 history 取回 (通道自己的待办 + 恢复路径)' },
|
|
57
|
+
{ file: 'src/web/server.ts', kind: 'retry',
|
|
58
|
+
symbols: [
|
|
59
|
+
T('didFixQueue'),
|
|
60
|
+
T('deliveryLedger'), // SSE ping 用的 per-client 连败计数 ⇒ 真出站状态
|
|
61
|
+
{ name: 'didFixTimer', scope: 'k8-target', note: '2s 节流定时器 —— 保留为**调度策略** (执行已交内核邮箱); 它本身仍是通道自己的调度状态' },
|
|
62
|
+
{ name: 'channelRunState', scope: 'k8-target', note: '**下一个(大)目标**: 模块级 `Map<channelId, {running, queue, abortController}>` (24 处用法) —— 通道自己的消息队列 + 串行 flag + abort, 与内核邮箱**功能重复**' },
|
|
63
|
+
{ name: 'messageQueue', scope: 'ui-state', note: '`(global as any)` 上的 **Web UI 通知列表** (pending/已读) ⇒ 展示状态, 不是 outbound/重试/恢复' },
|
|
64
|
+
{ name: 'pendingFriendRequests', scope: 'domain-pending', note: '**好友申请待办** (业务审批数据, 落盘恢复) ⇒ 业务状态, 不是投递状态' },
|
|
65
|
+
{ name: 'PENDING_FRIEND_REQ_FILE', scope: 'domain-pending', note: '同上: 好友申请待办的落盘文件' },
|
|
66
|
+
{ name: 'maxAttempts', scope: 'infra-retry', note: 'HTTP `listen` 的 **EADDRINUSE 重试上限** ⇒ 进程启动基础设施, 不是通道出站' },
|
|
67
|
+
],
|
|
68
|
+
why: 'web 通道内联的真状态: 待修复 channelId 队列(执行已交内核邮箱) + SSE 投递连败计数 + 通道自己的消息队列 channelRunState; 三个符号经定性**不属** K8 收口范围, 逐条写明原因' },
|
|
69
|
+
{ file: 'src/cli-entry.ts', kind: 'resume',
|
|
70
|
+
symbols: [T('resumeId')],
|
|
71
|
+
why: 'CLI 侧会话恢复' },
|
|
72
|
+
{ file: 'src/network/peer-fs.ts', kind: 'outbox',
|
|
73
|
+
symbols: ['OutboxEntry', 'enqueueOutbox', 'readOutbox', 'countOutbox', 'clearOutbox', 'getPeerOutboxPath'].map(T),
|
|
74
|
+
why: '文件传输侧出站 (落盘 outbox, 与 p2p-outbox 又一套)' },
|
|
75
|
+
{ file: 'src/agents/contacts/store.ts', kind: 'none', symbols: [],
|
|
76
|
+
why: '纯存储: **无**自带出站/重试/恢复状态 (显式记 none, 不是"忘了填")' },
|
|
77
|
+
{ file: 'src/agents/contacts/providers.ts', kind: 'retry',
|
|
78
|
+
symbols: ['DeliverResult', 'OTP_MAX_ATTEMPTS', 'smtpDeliver'].map(T),
|
|
79
|
+
why: '联系人通道自己的投递结果与重试上限' },
|
|
80
|
+
{ file: 'src/agents/p2p-chat-tools.ts', kind: 'outbox',
|
|
81
|
+
symbols: ['outboxPath', 'processPendingInbox'].map(T),
|
|
82
|
+
why: 'P2P 聊天工具侧出站 + 待办处理' },
|
|
83
|
+
{ file: 'src/agents/task/task-runner.ts', kind: 'resume',
|
|
84
|
+
symbols: ['ResumeResult', 'resumeTask', 'redelivered', 'refetchDeliveredContent'].map(T),
|
|
85
|
+
why: '任务运行器自带恢复 (含 x402 内容重取/重投递语义)' },
|
|
86
|
+
];
|
|
87
|
+
export const K8_ACCEPTANCE = [
|
|
88
|
+
'通信层**无直接 Agent 调用** (一律 transport → router → channel mailbox)',
|
|
89
|
+
'**无各通道自己的** 重试 / 任务恢复 / outbound 状态 (统一到 router + mailbox)',
|
|
90
|
+
'8 个事件面全部经同一条路由 (Web/CLI/P2P/手机/联系人回复/外部唤醒/cron/Supervisor)',
|
|
91
|
+
'收口后**直连数只许减** (棘轮), 且每减一处都要有"改走 router"的可回滚提交点',
|
|
92
|
+
'失败语义统一: 通道不再各自决定重试/放弃 (避免"有的通道静默丢、有的无限重试")',
|
|
93
|
+
];
|
|
94
|
+
export const K8_PROGRESS = {
|
|
95
|
+
stage: 'ledger-landed',
|
|
96
|
+
directSites: 0, // 12 → 10 (K8 第二步) → 7 (第三步: server.ts 3 处零差量迁移) (2026-10-02: routes-tasks / runner-resolver 两处已走唯一入口; 棘轮只许减)
|
|
97
|
+
perChannelStateFiles: 10,
|
|
98
|
+
// ⚠️ 2026-10-02 **补登**: 上一版漏了两个真状态符号 (`channelRunState` / `didFixTimer`) ⇒ 35 → 37。
|
|
99
|
+
// 这是**纠正漏登**不是范围扩张 (棘轮拦的是"通道自己状态变多", 不是"把已存在的登记上来")。
|
|
100
|
+
// 同一次提交里 `didFixRunning` (通道自己的全局单飞) 被**真删掉** —— 收口本身是减项, 只是它先前没被登记。
|
|
101
|
+
perChannelStateSymbols: 37, // 10 文件 / 37 个状态符号 (含 4 个范围外)
|
|
102
|
+
k8TargetSymbols: 33, // 其中 **33** 个属 K8 收口对象 (棘轮: 只许减)
|
|
103
|
+
runStateSites: 20, // 下一大目标的迁移工作面 (棘轮: 只许减)
|
|
104
|
+
};
|
|
105
|
+
// ════════════════════════════════════════════════════════════════════════════════
|
|
106
|
+
// K8 下一个大目标: `web/server.ts` 的 `channelRunState` (通道自己的消息队列 + 串行 flag + abort)
|
|
107
|
+
//
|
|
108
|
+
// 为什么它是"最大的一刀": 它与内核邮箱**功能重复** —— 通道自己存队列、自己管 running、自己管 abort。
|
|
109
|
+
// 为什么先落台账不直接改: 21 处用法 (剥注释后, 2026-10-02 量) + 三种语义混在一个对象里
|
|
110
|
+
// (调度 / 观测 / 协作续看), 直接改必然把观测与业务语义一起搅进去。
|
|
111
|
+
// ════════════════════════════════════════════════════════════════════════════════
|
|
112
|
+
export const K8_RUNSTATE_ROLES = ['k8-target', 'observational', 'domain-collab'];
|
|
113
|
+
/**
|
|
114
|
+
* `channelRunState` 收口的**总前置** (2026-10-02 量出, 必须写在代码旁边而不是只写在提交里)。
|
|
115
|
+
*
|
|
116
|
+
* 为什么: `running` 同时是**主路径内联跑**与**排队路径**的串行权威。内核邮箱只认识"投给它的任务" ——
|
|
117
|
+
* 若只把排队项投进邮箱而主路径仍内联跑, 邮箱会在内联跑还没结束时**立刻起跑**排队项
|
|
118
|
+
* ⇒ 同一 channel **两条 run 并行** ⇒ 串行性被破坏 (K5 的"同通道串行"承诺失效)。
|
|
119
|
+
* 所以删 `queue`/`running` 的**前置**是: 主路径内联跑自己也进邮箱 (`await getChannelQueue(id).submit(...)`)。
|
|
120
|
+
*
|
|
121
|
+
* **2026-10-02 自纠: 下面这段早些时候写的"实测缺陷"是错的, 按盘上事实改正 (留着错的说法比没有更危险)。**
|
|
122
|
+
* 旧说法 = "主路径 check-then-set 夹在多个 `await` 之间 ⇒ 并发 `/message` 会都通过检查 ⇒ 同通道双开"。
|
|
123
|
+
* **盘上事实不成立**: `if (runState.running)` (server.ts 4839) 与 `running = true` (4853) 之间只有
|
|
124
|
+
* `queue.push` / `broadcastQueueUpdate` / `console.log` / `return` —— **没有 await**; 排空路径那对
|
|
125
|
+
* (`if (runState.running) return;` / `running = true`, 5657/5658) 更是**相邻两行**
|
|
126
|
+
* ⇒ 两处在 Node 单线程模型下都**是原子的**。
|
|
127
|
+
*
|
|
128
|
+
* 真正存在的洞是**同族的另一条** (`handoff 不是授权`): `finishChannelRun` 先**同步**置 `running = false`,
|
|
129
|
+
* 再把下一条**异步**交给邮箱 (`getChannelQueue(channelId).submit`) —— 而该邮箱**与 didFix 等任务共用**
|
|
130
|
+
* (`server.ts` 5790 用同一个 `getChannelQueue(id)`) ⇒ 邮箱里排着前序任务时 handoff 会延后, 这期间新到的
|
|
131
|
+
* `/message` 看到 `running === false` 就**内联起跑**; 等那条排队消息终于被跑到时, 它的防重入守卫
|
|
132
|
+
* `if (runState.running) return;` 直接返回 ⇒ **那条排队消息被静默丢掉** (不是双开, 是丢消息 —— 更糟),
|
|
133
|
+
* 且顺序也被打乱 (后到的先跑)。修法仍是同一个前置: **每条消息 (含主路径) 都投邮箱**, `running`/`queue` 降为观测。
|
|
134
|
+
*/
|
|
135
|
+
export const K8_RUNSTATE_PREREQUISITE = '主路径内联跑必须先进内核邮箱 (`await getChannelQueue(channelId).submit(...)`) 才能删 `queue`/`running`: ' +
|
|
136
|
+
'否则邮箱会与内联跑并行起跑排队项, 同通道双开 (K5 串行承诺失效)。' +
|
|
137
|
+
'**已核实的现网缺陷 (2026-10-02 自纠后)**: handoff 不是授权 —— `finishChannelRun` 同步置 running=false 后异步投邮箱, ' +
|
|
138
|
+
'邮箱与 didFix 共用 ⇒ 前序任务排队时 handoff 延后, 新 `/message` 见 running=false 就内联跑; ' +
|
|
139
|
+
'那条排队消息随后撞上 `if (runState.running) return;` ⇒ **被静默丢掉** (并打乱 FIFO)。' +
|
|
140
|
+
'(旧说法"check-then-set 夹 await ⇒ 并发双开"**不成立**, 已按盘上事实改正。)';
|
|
141
|
+
export const K8_CHANNEL_RUNSTATE = {
|
|
142
|
+
file: 'src/web/server.ts',
|
|
143
|
+
symbol: 'channelRunState',
|
|
144
|
+
sites: 20, // 2026-10-02 收尾后真实值 20 (原 21; 中止位归 actor 时删掉 1 处用法) · 剥注释口径
|
|
145
|
+
fields: [
|
|
146
|
+
{ name: 'running', role: 'observational',
|
|
147
|
+
note: '**2026-10-02 正刀后已降为观测口径**: 串行权威归内核邮箱 (`getChannelQueue(channelId).submit`), `running` 只剩"这一轮是否在飞"的展示/判定用途 (UI 的 `queue_update` · `remoteFollowup` 的 `!matchedRs.running` · `/api/loop/inspect` 文案), 不再参与任何"能不能起跑"的判断' },
|
|
148
|
+
// `queue` 字段 **已删除** (2026-10-02 正刀): 每条消息自己 `getChannelQueue(channelId).submit(...)`,
|
|
149
|
+
// 通道不再持有队列 (随之删掉 `PendingMessage` 接口与 `runMessageFromQueue` 那条简化路径)。
|
|
150
|
+
// `abortController` 字段 **已删除** (2026-10-02 收尾): 中止位归**内核 actor** ——
|
|
151
|
+
// `ChannelActor.beginCancellation()` 每次执行开一个新 controller (与邮箱串行一致) ·
|
|
152
|
+
// `ChannelActor.abort(reason)` 中断它; UI 的终止按钮 (`POST /api/chat/abort`) 走
|
|
153
|
+
// `abortActorsOfChannel(channelId, 'user abort')` (按 channel 扫描注册表, 返回**真的中止了几个**)。
|
|
154
|
+
// ⇒ 通道状态里**不再留第二份**中止位 (该条原先挂在 `role: 'k8-target'` 下, 现已收口)。
|
|
155
|
+
{ name: 'lastSteps', role: 'observational', note: '供 `/api/loop/inspect` 的步骤累积 ⇒ 观测数据, 不是调度状态' },
|
|
156
|
+
{ name: 'lastSummary', role: 'observational', note: '同上: 最近一轮摘要 (检查接口用)' },
|
|
157
|
+
{ name: 'lastFinalReply', role: 'observational', note: '同上: 最近一次最终回复 (检查接口用)' },
|
|
158
|
+
{ name: 'lastTokens', role: 'observational', note: '同上: token 计数 (用量展示)' },
|
|
159
|
+
{ name: 'remoteFollowup', role: 'domain-collab', note: '远端协作续看 (`rounds`/`maxRounds`/`remoteChannelId`) ⇒ **业务协作语义**, 不是通道调度' },
|
|
160
|
+
],
|
|
161
|
+
plan: '按语义分三批, 每批独立提交: ① 队列+单飞 (`queue`/`running`) → 内核邮箱; ② `abort` → `ExecutionRequest.signal` (先定语义); ③ `last*` 观测数据搬出通道对象 (或显式标注"非调度")。',
|
|
162
|
+
prerequisite: K8_RUNSTATE_PREREQUISITE,
|
|
163
|
+
};
|