@zhushanwen/subagent-core 0.2.0 → 0.4.0
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/README.md +45 -2
- package/agents/analyst.md +60 -0
- package/agents/coder.md +69 -0
- package/agents/debugger.md +66 -0
- package/agents/doc-reviewer.md +49 -0
- package/agents/explorer.md +63 -0
- package/agents/general-purpose.md +32 -0
- package/agents/orchestrator.md +61 -0
- package/agents/planner.md +53 -0
- package/agents/researcher.md +67 -0
- package/agents/reviewer.md +73 -0
- package/dist/chunk-4KN4TTG7.js +240 -0
- package/dist/chunk-APZY4IME.js +27 -0
- package/dist/chunk-X7SZ5HLQ.js +66 -0
- package/dist/execution/engine/engines/zcode/constants.cjs +65 -0
- package/dist/execution/engine/engines/zcode/constants.d.cts +93 -1
- package/dist/execution/engine/engines/zcode/constants.d.ts +93 -1
- package/dist/execution/engine/engines/zcode/constants.js +43 -1
- package/dist/execution/engine/engines/zcode/reader.d.cts +1 -1
- package/dist/execution/engine/engines/zcode/reader.d.ts +1 -1
- package/dist/execution/engine/engines/zcode/reader.js +4 -234
- package/dist/execution/engine/paths.js +7 -19
- package/dist/index.cjs +14709 -100
- package/dist/index.d.cts +4053 -200
- package/dist/index.d.ts +4053 -200
- package/dist/index.js +14358 -102
- package/dist/{types-BxyAidGf.d.cts → types-DpUO16pj.d.cts} +464 -1
- package/dist/{types-BxyAidGf.d.ts → types-DpUO16pj.d.ts} +464 -1
- package/package.json +6 -3
- package/src/__tests__/review-fix-loop-script.test.ts +90 -2
- package/src/__tests__/review-fix-loop-utils.test.ts +21 -0
- package/src/__tests__/smoke.test.ts +9 -2
- package/src/execution/__tests__/agent-profile.test.ts +232 -0
- package/src/execution/__tests__/agents-assembly.test.ts +219 -0
- package/src/execution/__tests__/chat-engine-routing.test.ts +71 -0
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +1 -0
- package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
- package/src/execution/__tests__/delivery-methods.test.ts +2 -0
- package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
- package/src/execution/__tests__/dialog-queue.test.ts +199 -1
- package/src/execution/__tests__/epipe-fallback.test.ts +2 -0
- package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
- package/src/execution/__tests__/finalize-record.test.ts +106 -0
- package/src/execution/__tests__/gc-timer.test.ts +2 -0
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +2 -0
- package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +378 -0
- package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
- package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
- package/src/execution/__tests__/notify-ledger.test.ts +294 -0
- package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
- package/src/execution/__tests__/record-store.test.ts +83 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +4 -0
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +1 -0
- package/src/execution/__tests__/run-spawn-edges.test.ts +407 -17
- package/src/execution/__tests__/run-spawn-integration.test.ts +1 -1
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +12 -4
- package/src/execution/__tests__/service-kill-escalation.test.ts +89 -0
- package/src/execution/__tests__/session-pending.test.ts +228 -0
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
- package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
- package/src/execution/__tests__/session-runner-epipe.test.ts +1 -0
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +226 -0
- package/src/execution/__tests__/settled-watchdog.test.ts +289 -0
- package/src/execution/__tests__/spawned-children.test.ts +153 -10
- package/src/execution/__tests__/subagent-actions-core.test.ts +997 -0
- package/src/execution/__tests__/subagent-service-message-close.test.ts +71 -10
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +266 -0
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +3 -0
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +316 -0
- package/src/execution/__tests__/timeout-integration.test.ts +7 -7
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +95 -1
- package/src/execution/__tests__/ui-request-queue.test.ts +25 -0
- package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
- package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
- package/src/execution/agent-registry.ts +225 -1
- package/src/execution/agents-assembly.ts +88 -0
- package/src/execution/concurrency-pool.ts +71 -9
- package/src/execution/dialog-queue.ts +101 -3
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +49 -2
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +87 -1
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +26 -0
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +83 -1
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +131 -0
- package/src/execution/engine/__tests__/registry.test.ts +90 -1
- package/src/execution/engine/engine-discovery.ts +12 -16
- package/src/execution/engine/engines/pi/task-spec-mapper.ts +3 -3
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +274 -0
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +472 -0
- package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +387 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +780 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +815 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +462 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +119 -1
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +136 -6
- package/src/execution/engine/engines/zcode/appserver-home.ts +442 -0
- package/src/execution/engine/engines/zcode/appserver-probe.ts +141 -0
- package/src/execution/engine/engines/zcode/connection.ts +585 -0
- package/src/execution/engine/engines/zcode/constants.ts +129 -0
- package/src/execution/engine/engines/zcode/golden-sample.ts +54 -8
- package/src/execution/engine/engines/zcode/preparer.ts +25 -22
- package/src/execution/engine/engines/zcode/session-channel.ts +656 -0
- package/src/execution/engine/engines/zcode/zcode-engine.ts +909 -41
- package/src/execution/engine/host-task-spec.ts +3 -3
- package/src/execution/engine/port.ts +41 -1
- package/src/execution/engine/registry.ts +53 -0
- package/src/execution/finalize-record.ts +63 -5
- package/src/execution/get-state-handshake.ts +85 -10
- package/src/execution/lifecycle-manager.ts +27 -3
- package/src/execution/manifest-store.ts +53 -62
- package/src/execution/notifier.ts +10 -10
- package/src/execution/notify-ledger.ts +117 -16
- package/src/execution/record-entry.ts +8 -2
- package/src/execution/record-store.ts +29 -15
- package/src/execution/session-pending.ts +213 -62
- package/src/execution/session-runner.ts +974 -128
- package/src/execution/sessions-index.ts +10 -55
- package/src/execution/settled-watchdog.ts +99 -0
- package/src/execution/subagent-actions-core.ts +686 -0
- package/src/execution/subagent-service.ts +396 -49
- package/src/execution/ui-request-handler-factory.ts +27 -4
- package/src/execution/worktree-git-ops.ts +397 -0
- package/src/execution/worktree-manager.ts +72 -10
- package/src/execution/worktree-registry.ts +6 -12
- package/src/index.ts +413 -6
- package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -4
- package/src/orchestration/__tests__/args-meta.test.ts +358 -0
- package/src/orchestration/__tests__/error-recovery-handlers.test.ts +1 -8
- package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +382 -0
- package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
- package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
- package/src/orchestration/__tests__/file-run-store.test.ts +389 -0
- package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
- package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
- package/src/orchestration/__tests__/lifecycle.test.ts +77 -0
- package/src/orchestration/__tests__/run-snapshot.test.ts +353 -0
- package/src/orchestration/__tests__/script-generate.test.ts +314 -0
- package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
- package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
- package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
- package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
- package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
- package/src/orchestration/agent-opts-resolver.ts +7 -7
- package/src/orchestration/args-meta.ts +198 -0
- package/src/orchestration/error-recovery.ts +416 -146
- package/src/orchestration/execute-agent-call.ts +9 -9
- package/src/orchestration/file-run-store.ts +327 -0
- package/src/orchestration/launcher.ts +35 -26
- package/src/orchestration/lifecycle.ts +355 -79
- package/src/orchestration/models/agent-call.ts +7 -7
- package/src/orchestration/models/budget.ts +5 -5
- package/src/orchestration/models/run-runtime.ts +13 -13
- package/src/orchestration/models/trace.ts +8 -8
- package/src/orchestration/models/workflow-run.ts +27 -27
- package/src/orchestration/models/workflow-script.ts +4 -4
- package/src/orchestration/run-snapshot.ts +266 -0
- package/src/orchestration/script-generate.ts +154 -0
- package/src/orchestration/script-lint.ts +33 -33
- package/src/orchestration/worker-handle.ts +10 -10
- package/src/orchestration/worker-host.ts +10 -10
- package/src/orchestration/worker-script-builder.ts +401 -346
- package/src/orchestration/workflow-files.ts +37 -11
- package/src/orchestration/workflow-run-summary.ts +69 -0
- package/src/orchestration/workflow-script-registry-impl.ts +31 -9
- package/src/shared/__tests__/agent-ref.test.ts +209 -2
- package/src/shared/__tests__/atomic-write.test.ts +267 -0
- package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
- package/src/shared/__tests__/injection-render.test.ts +518 -0
- package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
- package/src/shared/__tests__/resource-discovery.test.ts +3 -2
- package/src/shared/agent-ref.ts +143 -2
- package/src/shared/atomic-write.ts +320 -0
- package/src/shared/bounded-serialize.ts +154 -0
- package/src/shared/injection-render.ts +279 -0
- package/src/shared/meta-parser.ts +41 -1
- package/src/shared/resource-discovery.ts +104 -37
- package/src/shared/resource-meta.ts +14 -0
- package/src/shared/xml-injection.ts +9 -9
- package/workflows/README.md +9 -9
- package/workflows/chain.js +4 -2
- package/workflows/map-reduce.js +5 -3
- package/workflows/parallel.js +5 -3
- package/workflows/review-fix-loop-utils.cjs +14 -3
- package/workflows/review-fix-loop.js +18 -7
- package/workflows/scatter-gather.js +4 -2
- package/dist/chunk-3VOERJPJ.js +0 -22
|
@@ -21,10 +21,16 @@
|
|
|
21
21
|
// D5);同一边沿的多条 pending 合并为一条注入
|
|
22
22
|
// ③ ack:回执判定成功(主 session 出现 notifyId 匹配的 subagent-bg-notify
|
|
23
23
|
// custom_message entry)后 appendEntry("subagent-bg-notify-ack")
|
|
24
|
-
// ④ replay:重启恢复扫描 ledger/ack
|
|
24
|
+
// ④ replay:重启恢复扫描 ledger/ack/abandoned entry 差集重放;重放按 notifyId 幂等去重
|
|
25
25
|
// (details 携带 notifyId,重复条目可识别)
|
|
26
26
|
//
|
|
27
|
-
//
|
|
27
|
+
// 止损上半场(T4③/PS-6):④只保证「送达保证」下半场,回执确认不可达时(如送达
|
|
28
|
+
// entry 被 compaction 清除)attempts 无上限 = 同一条通知每 120s 重复注入并
|
|
29
|
+
// triggerTurn 无限唤醒 LLM。收敛:重投达 NOTIFY_REDELIVERY_MAX_ATTEMPTS 仍无回执 →
|
|
30
|
+
// 写 abandoned 终态 entry 放弃(warn 含 subagent 标识与 subagents action:"list"
|
|
31
|
+
// 手动核对指引),放弃号跨重启不复活(恢复扫描视同已销账)。
|
|
32
|
+
//
|
|
33
|
+
// 通道分工(D4):ledger/ack/abandoned 用 plain appendEntry(type=custom 不进 LLM 上下文——
|
|
28
34
|
// session-manager sessionEntryToContextMessages 对 custom 返回 []);送达消息用
|
|
29
35
|
// pi.sendMessage({triggerTurn:true})(custom_message 进上下文)。两通道不得混用。
|
|
30
36
|
//
|
|
@@ -49,6 +55,11 @@ const logger = getLogger("subagents");
|
|
|
49
55
|
export const NOTIFY_LEDGER_CUSTOM_TYPE = "subagent-bg-notify-ledger";
|
|
50
56
|
/** 销账 entry customType(plain custom entry,不进 LLM 上下文)。 */
|
|
51
57
|
export const NOTIFY_ACK_CUSTOM_TYPE = "subagent-bg-notify-ack";
|
|
58
|
+
/**
|
|
59
|
+
* [T4③/PS-6] 放弃终态 entry customType(plain custom entry,不进 LLM 上下文)。
|
|
60
|
+
* 与 ack 同形同域:恢复/compaction 扫描把它视同已销账——放弃条目跨重启不复活。
|
|
61
|
+
*/
|
|
62
|
+
export const NOTIFY_ABANDONED_CUSTOM_TYPE = "subagent-bg-notify-abandoned";
|
|
52
63
|
|
|
53
64
|
/**
|
|
54
65
|
* 送达消息的 customType。notifier.ts / bg-notify-render / index.ts(messageRenderer
|
|
@@ -65,6 +76,14 @@ export const NOTIFY_CUSTOM_TYPE = "subagent-bg-notify";
|
|
|
65
76
|
*/
|
|
66
77
|
export const NOTIFY_WATCHDOG_MS = 120_000;
|
|
67
78
|
|
|
79
|
+
/**
|
|
80
|
+
* [T4③/PS-6] 单条通知投递尝试上限(attempts 字段含首次投递):看门狗把 sent 超期
|
|
81
|
+
* 条目转回 pending 前,attempts 已达本上限 → 不再重投,转放弃终态(abandoned entry
|
|
82
|
+
* + warn 恢复指引)。被否语义:「重投无限但幂等」——幂等只防重复入账,不防重复
|
|
83
|
+
* 投递唤醒 LLM(设计 docs/design/subagent-core-unbounded-wait-audit.md §7.2 T4③)。
|
|
84
|
+
*/
|
|
85
|
+
export const NOTIFY_REDELIVERY_MAX_ATTEMPTS = 5;
|
|
86
|
+
|
|
68
87
|
/**
|
|
69
88
|
* U4 投递计数分桶(设计 §5 U4:分桶口径与 §2.2 三条丢失路径一一对应,回归时定位到
|
|
70
89
|
* 具体环节):
|
|
@@ -103,16 +122,22 @@ export interface NotifyAckEntryData {
|
|
|
103
122
|
notifyId: string;
|
|
104
123
|
}
|
|
105
124
|
|
|
125
|
+
/** [T4③/PS-6] abandoned entry 的 data schema(v1,与 ack 同形——终态标记只需身份键)。 */
|
|
126
|
+
export interface NotifyAbandonedEntryData {
|
|
127
|
+
v: 1;
|
|
128
|
+
notifyId: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
106
131
|
/** ledger 依赖的宿主最小接口(index.ts session_start 装配;解耦便于测试)。 */
|
|
107
132
|
export interface NotifyLedgerHost {
|
|
108
|
-
/** 写 plain custom entry(ledger/ack 通道;pi.appendEntry)。 */
|
|
133
|
+
/** 写 plain custom entry(ledger/ack/abandoned 通道;pi.appendEntry)。 */
|
|
109
134
|
appendLedgerEntry(customType: string, data: unknown): void;
|
|
110
135
|
/** 读当前 session 全部 entry(回执扫描 + 恢复扫描;ctx.sessionManager.getEntries())。 */
|
|
111
136
|
readSessionEntries(): readonly unknown[];
|
|
112
137
|
/** 主 agent 是否空闲(发送前二次复查用;ctx.isIdle())。 */
|
|
113
138
|
isIdle(): boolean;
|
|
114
139
|
/** 订阅 settled 边沿(pi.on("agent_settled")——无退订语义:createExtensionAPI.on
|
|
115
|
-
* 只 push 进 extension.handlers、无 off(pi 0.84.
|
|
140
|
+
* 只 push 进 extension.handlers、无 off(pi 0.84.4 loader.js:209-214),由 ledger
|
|
116
141
|
* disposed 标志包装)。注意与 ctx.events.on(channel) 消歧:后者走 eventBus、有
|
|
117
142
|
* trackEventBusSubscription 退订通路(loader.js:174、:338),语义不同。 */
|
|
118
143
|
onAgentSettled(handler: () => void): void;
|
|
@@ -128,7 +153,7 @@ interface NotifyLedgerItem {
|
|
|
128
153
|
recordedAt: number;
|
|
129
154
|
/** 最近一次投递受理时刻;undefined = 尚未投递(pending,等下一边沿)。 */
|
|
130
155
|
sentAt: number | undefined;
|
|
131
|
-
/**
|
|
156
|
+
/** 投递尝试次数(含首次;达 NOTIFY_REDELIVERY_MAX_ATTEMPTS 后超期即放弃,诊断用)。 */
|
|
132
157
|
attempts: number;
|
|
133
158
|
}
|
|
134
159
|
|
|
@@ -141,16 +166,24 @@ export interface NotifyLedger {
|
|
|
141
166
|
* 同批 pending 合并单条送达(triggerTurn 直达)。 */
|
|
142
167
|
attemptDeliver(): void;
|
|
143
168
|
/** ③ 回执销账:扫 session entries,出现 notifyId 匹配的送达 custom_message entry
|
|
144
|
-
* → appendEntry(ack) + 摘账(内存态不承担销账职责,权威 =
|
|
169
|
+
* → appendEntry(ack) + 摘账(内存态不承担销账职责,权威 = 多列 entry 差集)。 */
|
|
145
170
|
checkReceipts(): void;
|
|
146
|
-
/** ④ 重启恢复:扫 ledger/ack
|
|
171
|
+
/** ④ 重启恢复:扫 ledger/ack/abandoned entry 差集,未销账且未放弃号重新入账并投递
|
|
172
|
+
* (已销账零重发;已放弃号不复活——[T4③/PS-6] 止损终态跨重启生效)。
|
|
147
173
|
* @returns 重放条数。 */
|
|
148
174
|
recoverFromSession(): number;
|
|
149
|
-
/** compaction 降级(P-B4 未验证):检测 ledger/ack entry 被清除 → 按内存态补写。
|
|
175
|
+
/** compaction 降级(P-B4 未验证):检测 ledger/ack/abandoned entry 被清除 → 按内存态补写。
|
|
150
176
|
* @returns 补写条数。 */
|
|
151
177
|
compactionCheck(): number;
|
|
152
178
|
/** 诊断/测试:pending(已记账未投递)条数。 */
|
|
153
179
|
pendingCount(): number;
|
|
180
|
+
/**
|
|
181
|
+
* [T4④/PS-5] pending 只读快照(notifyId/content/record,副本非活引用):
|
|
182
|
+
* 供 SubagentService.dispose 在「shutdown flush 被 isIdle 门拦」时把未投递
|
|
183
|
+
* pending 复写落盘(同一 ledger entry 通道,notifyId 幂等)供重启 replay。
|
|
184
|
+
* 消费侧配合面仅此只读方法——不改变 attemptDeliver/abandon 既有语义。
|
|
185
|
+
*/
|
|
186
|
+
pendingEntries(): ReadonlyArray<{ notifyId: string; content: string; record: object }>;
|
|
154
187
|
/** 诊断/测试:已投递待回执条数。 */
|
|
155
188
|
waitingReceiptCount(): number;
|
|
156
189
|
/** U4 诊断:三桶计数快照(副本;增量同时经 extensionLogger 通道落日志)。 */
|
|
@@ -166,13 +199,15 @@ function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
|
166
199
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
167
200
|
}
|
|
168
201
|
|
|
169
|
-
/** 扫 ledger/ack
|
|
202
|
+
/** 扫 ledger/ack/abandoned 三列 plain custom entry(恢复 / compaction 检查共用)。 */
|
|
170
203
|
function scanSessionLedgerEntries(entries: readonly unknown[]): {
|
|
171
204
|
ledger: Map<string, NotifyLedgerEntryData>;
|
|
172
205
|
acked: Set<string>;
|
|
206
|
+
abandoned: Set<string>;
|
|
173
207
|
} {
|
|
174
208
|
const ledger = new Map<string, NotifyLedgerEntryData>();
|
|
175
209
|
const acked = new Set<string>();
|
|
210
|
+
const abandoned = new Set<string>();
|
|
176
211
|
for (const entry of entries) {
|
|
177
212
|
if (!isPlainObject(entry) || entry["type"] !== "custom") continue;
|
|
178
213
|
const customType = entry["customType"];
|
|
@@ -189,9 +224,11 @@ function scanSessionLedgerEntries(entries: readonly unknown[]): {
|
|
|
189
224
|
}
|
|
190
225
|
} else if (customType === NOTIFY_ACK_CUSTOM_TYPE) {
|
|
191
226
|
acked.add(notifyId);
|
|
227
|
+
} else if (customType === NOTIFY_ABANDONED_CUSTOM_TYPE) {
|
|
228
|
+
abandoned.add(notifyId);
|
|
192
229
|
}
|
|
193
230
|
}
|
|
194
|
-
return { ledger, acked };
|
|
231
|
+
return { ledger, acked, abandoned };
|
|
195
232
|
}
|
|
196
233
|
|
|
197
234
|
/** 收集 wanted 集合中已送达(custom_message entry 出现)的 notifyId。
|
|
@@ -232,6 +269,11 @@ export function createNotifyLedger(
|
|
|
232
269
|
const items = new Map<string, NotifyLedgerItem>();
|
|
233
270
|
/** 已销账内存索引(notifyId 幂等判重 + compaction 补写源;权威 = ack entry 列)。 */
|
|
234
271
|
const ackedIds = new Set<string>();
|
|
272
|
+
/**
|
|
273
|
+
* [T4③/PS-6] 已放弃终态内存索引(幂等判重 + compaction 补写源;权威 = abandoned
|
|
274
|
+
* entry 列)。与 ackedIds 同型:放弃是终态,同 notifyId 绝不再入账/重投/复活。
|
|
275
|
+
*/
|
|
276
|
+
const abandonedIds = new Set<string>();
|
|
235
277
|
/** U4 投递计数三桶(诊断快照源;增量经 emitBucketLog 落 extensionLogger)。 */
|
|
236
278
|
const buckets: NotifyDeliveryBucketMetrics = {
|
|
237
279
|
settleRejected: 0,
|
|
@@ -244,7 +286,8 @@ export function createNotifyLedger(
|
|
|
244
286
|
const api: NotifyLedger = {
|
|
245
287
|
record(notifyId, content, record): boolean {
|
|
246
288
|
if (disposed) return false;
|
|
247
|
-
|
|
289
|
+
// 幂等去重:在账(pending/sent)/ 已销账 / 已放弃([T4③] 终态绝不重发)→ false
|
|
290
|
+
if (items.has(notifyId) || ackedIds.has(notifyId) || abandonedIds.has(notifyId)) return false;
|
|
248
291
|
host.appendLedgerEntry(NOTIFY_LEDGER_CUSTOM_TYPE, {
|
|
249
292
|
v: 1,
|
|
250
293
|
notifyId,
|
|
@@ -297,9 +340,13 @@ export function createNotifyLedger(
|
|
|
297
340
|
if (disposed) return 0;
|
|
298
341
|
const state = scanSessionLedgerEntries(host.readSessionEntries());
|
|
299
342
|
for (const notifyId of state.acked) ackedIds.add(notifyId);
|
|
343
|
+
// [T4③/PS-6] 放弃终态跨重启生效:abandoned entry 视同已销账,下方差集重放跳过
|
|
344
|
+
// ——放弃号重启后不复活不重投(止损语义不以进程边界失效)。
|
|
345
|
+
for (const notifyId of state.abandoned) abandonedIds.add(notifyId);
|
|
300
346
|
let replayed = 0;
|
|
301
347
|
for (const entry of state.ledger.values()) {
|
|
302
348
|
if (state.acked.has(entry.notifyId)) continue; // 已销账零重发
|
|
349
|
+
if (abandonedIds.has(entry.notifyId)) continue; // 已放弃不复活(止损终态)
|
|
303
350
|
if (items.has(entry.notifyId) || ackedIds.has(entry.notifyId)) continue; // 幂等
|
|
304
351
|
items.set(entry.notifyId, {
|
|
305
352
|
...entry,
|
|
@@ -345,6 +392,14 @@ export function createNotifyLedger(
|
|
|
345
392
|
rewritten += 1;
|
|
346
393
|
}
|
|
347
394
|
}
|
|
395
|
+
// [T4③/PS-6] 放弃终态同享补写:内存是权威,compaction 清掉 abandoned entry 后
|
|
396
|
+
// 不补写会让放弃号在下次重启按差集复活(重投重启)。
|
|
397
|
+
for (const notifyId of abandonedIds) {
|
|
398
|
+
if (!state.abandoned.has(notifyId)) {
|
|
399
|
+
host.appendLedgerEntry(NOTIFY_ABANDONED_CUSTOM_TYPE, { v: 1, notifyId } satisfies NotifyAbandonedEntryData);
|
|
400
|
+
rewritten += 1;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
348
403
|
return rewritten;
|
|
349
404
|
},
|
|
350
405
|
|
|
@@ -356,6 +411,16 @@ export function createNotifyLedger(
|
|
|
356
411
|
return n;
|
|
357
412
|
},
|
|
358
413
|
|
|
414
|
+
pendingEntries(): ReadonlyArray<{ notifyId: string; content: string; record: object }> {
|
|
415
|
+
const out: Array<{ notifyId: string; content: string; record: object }> = [];
|
|
416
|
+
for (const item of items.values()) {
|
|
417
|
+
if (item.sentAt !== undefined) continue;
|
|
418
|
+
// 逐条浅拷贝:消费方(dispose 落盘复写)不得持有内部可变态。
|
|
419
|
+
out.push({ notifyId: item.notifyId, content: item.content, record: { ...item.record } });
|
|
420
|
+
}
|
|
421
|
+
return out;
|
|
422
|
+
},
|
|
423
|
+
|
|
359
424
|
waitingReceiptCount(): number {
|
|
360
425
|
let n = 0;
|
|
361
426
|
for (const item of items.values()) {
|
|
@@ -408,6 +473,35 @@ export function createNotifyLedger(
|
|
|
408
473
|
};
|
|
409
474
|
}
|
|
410
475
|
|
|
476
|
+
/** [T4③/PS-6] 放弃终态:appendEntry(abandoned) 落盘终态标记(重启恢复不复活)+
|
|
477
|
+
* warn 恢复指引。放弃后账面摘除(pending/waiting 计数归零、看门狗可停),同
|
|
478
|
+
* notifyId 被 record 幂等拒绝——「确认不可达」的止损上半场;通知内容本身仍可
|
|
479
|
+
* 经 subagents action:"list" 手动核对(账本 entry 与 result 落盘不受影响)。
|
|
480
|
+
* 放弃不计入 watchdogReplays 桶(该桶口径 = 实际发生重投的条数)。 */
|
|
481
|
+
function abandonItem(item: NotifyLedgerItem): void {
|
|
482
|
+
host.appendLedgerEntry(NOTIFY_ABANDONED_CUSTOM_TYPE, { v: 1, notifyId: item.notifyId } satisfies NotifyAbandonedEntryData);
|
|
483
|
+
items.delete(item.notifyId);
|
|
484
|
+
abandonedIds.add(item.notifyId);
|
|
485
|
+
// 消息含 subagent 标识与恢复指引(S-E 验收面);notifyId/attempts 等动态值按
|
|
486
|
+
// D4 约定放 data 参数(msg 近固定 key,限流命中面)。
|
|
487
|
+
logger.warn(
|
|
488
|
+
`Subagent "${itemLabel(item)}" notification abandoned - no receipt after ` +
|
|
489
|
+
`${NOTIFY_REDELIVERY_MAX_ATTEMPTS} delivery attempts; verify manually via subagents action:"list"`,
|
|
490
|
+
{ notifyId: item.notifyId, attempts: item.attempts },
|
|
491
|
+
);
|
|
492
|
+
maybeStopWatchdog();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** 放弃 warn 的 subagent 标识:优先 record.agent(人可读);record 形态异常时
|
|
496
|
+
* (ledger 对 record 不透明,见 NotifyLedgerEntryData)退回 notifyId(仍可检索)。 */
|
|
497
|
+
function itemLabel(item: NotifyLedgerItem): string {
|
|
498
|
+
if (isPlainObject(item.record)) {
|
|
499
|
+
const agent = item.record["agent"];
|
|
500
|
+
if (typeof agent === "string" && agent.length > 0) return agent;
|
|
501
|
+
}
|
|
502
|
+
return item.notifyId;
|
|
503
|
+
}
|
|
504
|
+
|
|
411
505
|
function ensureWatchdog(): void {
|
|
412
506
|
if (watchdogTimer !== undefined || disposed) return;
|
|
413
507
|
// 看门狗(D5 ②兜底触发面):回执检查 + 超时重投 + pending 补投。
|
|
@@ -418,12 +512,19 @@ export function createNotifyLedger(
|
|
|
418
512
|
// attemptDeliver 即重投——正常时序 settled 边沿早已销账,只有消息真丢失才到这
|
|
419
513
|
const cutoff = Date.now() - NOTIFY_WATCHDOG_MS;
|
|
420
514
|
let timedOut = 0;
|
|
515
|
+
// [T4③/PS-6] 止损:attempts 已达上限仍超期 = 确认不可达 → 放弃转终态,
|
|
516
|
+
// 不再重投(同一条完成通知不无限重复注入唤醒 LLM)。
|
|
517
|
+
const givenUp: NotifyLedgerItem[] = [];
|
|
421
518
|
for (const item of items.values()) {
|
|
422
|
-
if (item.sentAt
|
|
423
|
-
|
|
424
|
-
|
|
519
|
+
if (item.sentAt === undefined || item.sentAt > cutoff) continue;
|
|
520
|
+
if (item.attempts >= NOTIFY_REDELIVERY_MAX_ATTEMPTS) {
|
|
521
|
+
givenUp.push(item);
|
|
522
|
+
continue;
|
|
425
523
|
}
|
|
524
|
+
item.sentAt = undefined;
|
|
525
|
+
timedOut += 1;
|
|
426
526
|
}
|
|
527
|
+
for (const item of givenUp) abandonItem(item);
|
|
427
528
|
if (timedOut > 0) {
|
|
428
529
|
// U4 ①watchdogReplays 桶:busy 窗口滞留兜底的重投条数(同一消息反复超时累计)
|
|
429
530
|
buckets.watchdogReplays += timedOut;
|
|
@@ -467,7 +568,7 @@ export function createNotifyLedger(
|
|
|
467
568
|
// (P-B0 相邻机制已源码级锚定,agent-session.js:327-331,PS-07)。
|
|
468
569
|
// [MF-5] registerSettledListener=false(bind 路径)时跳过注册:pi.on("agent_settled")
|
|
469
570
|
// 无退订语义——createExtensionAPI.on 只 push 进 extension.handlers、无 off
|
|
470
|
-
//(pi 0.84.
|
|
571
|
+
//(pi 0.84.4 dist/core/extensions/loader.js:209-214)。注意消歧两类订阅面:此处的
|
|
471
572
|
// pi.on 是生命周期事件订阅(无退订);ctx.events.on(channel) 走 eventBus、有
|
|
472
573
|
// trackEventBusSubscription 退订通路(loader.js:174、:338)——勿按后者的可退订
|
|
473
574
|
// 语义「修复」此处。per-bind 注册会随 session 切换累积死 handler
|
|
@@ -543,7 +644,7 @@ function settledEdgeDispatch(): void {
|
|
|
543
644
|
/**
|
|
544
645
|
* session_start 装配(index.ts):构造新 ledger 并绑定为模块级单例(notifier.notify
|
|
545
646
|
* 经 getBoundNotifyLedger 消费)。重复 bind(/resume /fork /new 的 session_start)
|
|
546
|
-
* 替换旧实例——内存态清零符合「内存不承担销账职责」,权威 = entry
|
|
647
|
+
* 替换旧实例——内存态清零符合「内存不承担销账职责」,权威 = entry 多列差集
|
|
547
648
|
* (调用方随后 recoverFromSession 重建)。
|
|
548
649
|
*
|
|
549
650
|
* [MF-5] 监听单例化:首次 bind 经当次 host 注册模块级单例 handler
|
|
@@ -21,7 +21,9 @@ import type {
|
|
|
21
21
|
} from "./types.ts";
|
|
22
22
|
|
|
23
23
|
/** 自描述 record entry 的 customType。写点字面量与本常量的等值由
|
|
24
|
-
* __tests__/record-store.test.ts 断言钉住(消费方引用本常量,勿用裸字符串)。
|
|
24
|
+
* __tests__/record-store.test.ts 断言钉住(消费方引用本常量,勿用裸字符串)。
|
|
25
|
+
*
|
|
26
|
+
* @experimental execution 运行时面(U10① D6):一个 minor 周期内允许签名微调。 */
|
|
25
27
|
export const SUBAGENT_RECORD_CUSTOM_TYPE = "subagent-record";
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -34,6 +36,8 @@ export const SUBAGENT_RECORD_CUSTOM_TYPE = "subagent-record";
|
|
|
34
36
|
* - worktreeHandle:不可 JSON 序列化的运行时句柄(布尔投影 worktree 保留)。
|
|
35
37
|
*
|
|
36
38
|
* undefined 字段经 JSON.stringify 自然缺省(与 SubagentRecord 重建侧语义一致)。
|
|
39
|
+
*
|
|
40
|
+
* @experimental execution 运行时面(U10① D6):一个 minor 周期内允许签名微调。
|
|
37
41
|
*/
|
|
38
42
|
export interface SubagentRecordEntryData {
|
|
39
43
|
/** schema 版本(W16 起 v1)。消费方按 v 判别解析,不认识的版本跳过而非猜测。 */
|
|
@@ -95,7 +99,9 @@ export interface SubagentRecordEntryData {
|
|
|
95
99
|
engineHandle?: { sessionRef: Record<string, string>; journalPath?: string; poolKey: string };
|
|
96
100
|
}
|
|
97
101
|
|
|
98
|
-
/** SubagentRecord → 自描述 entry data(快照投影,不 mutate 源)。
|
|
102
|
+
/** SubagentRecord → 自描述 entry data(快照投影,不 mutate 源)。
|
|
103
|
+
*
|
|
104
|
+
* @experimental execution 运行时面(U10① D6):一个 minor 周期内允许签名微调。 */
|
|
99
105
|
export function toSubagentRecordEntry(record: SubagentRecord): SubagentRecordEntryData {
|
|
100
106
|
return {
|
|
101
107
|
v: 1,
|
|
@@ -352,6 +352,12 @@ export type RecordStorePi = {
|
|
|
352
352
|
* 读时从 sessions/*.jsonl 重建([perf] light 头部扫描 + per-file 缓存)。
|
|
353
353
|
*
|
|
354
354
|
* 任何 mutate → notifyChange()(仅通知监听器;磁盘缓存靠 stat 戳自校验,不清空)。
|
|
355
|
+
*
|
|
356
|
+
* record 状态查询面(U10① D6):按状态枚举 listRunning/collectRecords(statusFilter)、
|
|
357
|
+
* 按 id 查询 getMutable/findLightById/getFullRecord——方法签名即导出形态,本类零改动。
|
|
358
|
+
*
|
|
359
|
+
* @experimental execution 运行时面(设计 docs/design/subagent-core-sink-design.md §3.3 D6):
|
|
360
|
+
* 一个 minor 周期内允许签名微调,稳定后转常规 semver 承诺。
|
|
355
361
|
*/
|
|
356
362
|
export class RecordStore {
|
|
357
363
|
private readonly records = new Map<string, ExecutionRecord>();
|
|
@@ -698,11 +704,11 @@ export class RecordStore {
|
|
|
698
704
|
...(chatMode
|
|
699
705
|
? { resumable: true }
|
|
700
706
|
: {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
707
|
+
status: "closed" as const,
|
|
708
|
+
closedReason: "gc" as const,
|
|
709
|
+
endedAt: Date.now(),
|
|
710
|
+
error: "orphan recovery: no child session file (spawn interrupted or file removed externally)",
|
|
711
|
+
}),
|
|
706
712
|
});
|
|
707
713
|
}
|
|
708
714
|
|
|
@@ -740,9 +746,19 @@ export class RecordStore {
|
|
|
740
746
|
this.indexHigherVersion = false;
|
|
741
747
|
}
|
|
742
748
|
|
|
743
|
-
/**
|
|
749
|
+
/**
|
|
750
|
+
* /resume /fork /new 后复活(dispose 的逆操作)。
|
|
751
|
+
*
|
|
752
|
+
* [PS-10/T6④] 同步复位 orphanJudged 防重缓存:resumable 形态(IO-error 保守分支 /
|
|
753
|
+
* chatMode 分流)没有 .finalized sidecar 锚,重判资格完全由本缓存承载——dispose 时
|
|
754
|
+
* 有 clear(session 结束),但 revive 此前不复位,导致「同进程内曾经的 IO 失败记录
|
|
755
|
+
* 永久停留 resumable」,与本文件 recoverOrphanRecords 注释承诺的「IO 恢复后重开可重判」
|
|
756
|
+
* 不符。/new 复活正是「重开」语义:IO 已恢复的记录下次 recoverOrphanRecords 重新判定
|
|
757
|
+
* 收敛终态;仍不可读的记录重判再落一次 resumable entry(幂等,末条语义不变)。
|
|
758
|
+
*/
|
|
744
759
|
revive(): void {
|
|
745
760
|
this._disposed = false;
|
|
761
|
+
this.orphanJudged.clear();
|
|
746
762
|
}
|
|
747
763
|
|
|
748
764
|
// ── 内部 ──────────────────────────────────────────────────
|
|
@@ -971,7 +987,10 @@ export class RecordStore {
|
|
|
971
987
|
})
|
|
972
988
|
.catch((err: unknown) => {
|
|
973
989
|
this.indexDirty = true; // 失败恢复 dirty,下轮过窗重试
|
|
974
|
-
|
|
990
|
+
// [PS-14/T7③] 升 warn:索引反复写失败(权限/磁盘满)曾仅 debug 级,排障时
|
|
991
|
+
// 无线索。失败会跨轮重试(dirty 恢复),warn 每次过窗写失败都会出现——
|
|
992
|
+
// 正是「反复写失败需要可见」的信号面。
|
|
993
|
+
logger.warn("[subagents] sessions-index write failed", {
|
|
975
994
|
detail: { dir: encDir, error: err instanceof Error ? err.message : String(err) },
|
|
976
995
|
});
|
|
977
996
|
});
|
|
@@ -1171,14 +1190,9 @@ export class RecordStore {
|
|
|
1171
1190
|
return rec;
|
|
1172
1191
|
}
|
|
1173
1192
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
if (entry) {
|
|
1178
|
-
if (!entry.negative) this.idToFile.delete(entry.light.id);
|
|
1179
|
-
this.fileCache.delete(file);
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1193
|
+
// [PS-15/T7⑤] 「从缓存与索引移除单文件」的旧私有方法已整体删除:全仓无调用方
|
|
1194
|
+
// 的死代码(设计 §4.3 PS-15 实锤,顺手清理,无行为影响)。「文件删除时移除缓存」
|
|
1195
|
+
// 的职责实际由 reconstructAll 的消失文件修剪路径承担。
|
|
1182
1196
|
|
|
1183
1197
|
/** 排序比较器:status priority(running<failed<cancelled<done)+ startedAt desc。 */
|
|
1184
1198
|
private static compareRecords(a: SubagentRecord, b: SubagentRecord): number {
|