@zhushanwen/subagent-core 0.8.0 → 0.9.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/dist/{chunk-OL5BK4VN.js → chunk-LZJOEDGQ.js} +455 -203
- package/dist/engine-discovery-scan-Da4Dkzsi.d.cts +818 -0
- package/dist/engine-discovery-scan-Da4Dkzsi.d.ts +818 -0
- package/dist/execution/engine/engine-discovery-scan.cjs +216 -155
- package/dist/execution/engine/engine-discovery-scan.d.cts +1 -2
- package/dist/execution/engine/engine-discovery-scan.d.ts +1 -2
- package/dist/execution/engine/engine-discovery-scan.js +1 -1
- package/dist/execution/relay-env.js +5 -4
- package/dist/index.cjs +7686 -5894
- package/dist/index.d.cts +1876 -1360
- package/dist/index.d.ts +1876 -1360
- package/dist/index.js +7048 -5493
- package/dist.bundle/index.cjs +7747 -5957
- package/package.json +4 -4
- package/src/__tests__/manifest-store.test.ts +16 -15
- package/src/__tests__/robustness-medium-batch1.test.ts +10 -14
- package/src/__tests__/robustness-medium-batch2.test.ts +7 -2
- package/src/execution/__tests__/alive-store.test.ts +117 -1
- package/src/execution/__tests__/batch-finalized.test.ts +206 -0
- package/src/execution/__tests__/chat-engine-routing.test.ts +117 -1
- package/src/execution/__tests__/{cold-resurrect.test.ts → cold-lookup.test.ts} +133 -33
- package/src/execution/__tests__/collect-coordinator-service.test.ts +7 -5
- package/src/execution/__tests__/conversation-continuation.test.ts +1452 -0
- package/src/execution/__tests__/delivery-methods.test.ts +90 -176
- package/src/execution/__tests__/dialog-queue.test.ts +70 -3
- package/src/execution/__tests__/dispose-manifest-recovery.test.ts +475 -0
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +3 -3
- package/src/execution/__tests__/execute-nesting.test.ts +8 -5
- package/src/execution/__tests__/execution-record.test.ts +8 -148
- package/src/execution/__tests__/finalize-record.test.ts +295 -129
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +21 -29
- package/src/execution/__tests__/helpers/fake-engine-port.ts +13 -68
- package/src/execution/__tests__/helpers/legacy-sidecar.ts +33 -0
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +9 -5
- package/src/execution/__tests__/idle-gc.test.ts +60 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +3 -254
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +43 -22
- package/src/execution/__tests__/model-config-service.test.ts +111 -0
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +10 -6
- package/src/execution/__tests__/notify-ledger.test.ts +42 -1
- package/src/execution/__tests__/rebuild-indexes.test.ts +329 -0
- package/src/execution/__tests__/record-binding.test.ts +613 -0
- package/src/execution/__tests__/record-origin.test.ts +318 -0
- package/src/execution/__tests__/record-store-intent-api.test.ts +600 -0
- package/src/execution/__tests__/record-store-orphan-revive.test.ts +71 -3
- package/src/execution/__tests__/record-store.test.ts +72 -54
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +8 -5
- package/src/execution/__tests__/round-supervisor-workflow-origin.test.ts +205 -0
- package/src/execution/__tests__/run-orchestration-write-lease.test.ts +293 -0
- package/src/execution/__tests__/run-orchestration.test.ts +199 -0
- package/src/execution/__tests__/session-file-gc.test.ts +35 -4
- package/src/execution/__tests__/session-reconstructor.test.ts +78 -0
- package/src/execution/__tests__/state-marker.test.ts +288 -0
- package/src/execution/__tests__/stream-sink-retirement.test.ts +8 -5
- package/src/execution/__tests__/subagent-actions-core.test.ts +67 -16
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +0 -1
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -11
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +17 -11
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +130 -53
- package/src/execution/__tests__/subagent-service.test.ts +17 -215
- package/src/execution/__tests__/subprocess-agent-runner-no-progress-killall.test.ts +138 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +105 -660
- package/src/execution/__tests__/sync-collect-recovery.test.ts +23 -16
- package/src/execution/__tests__/terminal-write-latency.test.ts +79 -0
- package/src/execution/__tests__/workflow-agent-dispatch.test.ts +782 -0
- package/src/execution/alive-store.ts +37 -28
- package/src/execution/{cold-resurrect.ts → cold-lookup.ts} +48 -45
- package/src/execution/config.ts +1 -1
- package/src/execution/conversation-continuation.ts +541 -0
- package/src/execution/dialog-queue.ts +60 -12
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +16 -1
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +17 -51
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +0 -5
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +5 -9
- package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +0 -3
- package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -3
- package/src/execution/engine/__tests__/model-prompt.test.ts +0 -3
- package/src/execution/engine/__tests__/registry.test.ts +0 -1
- package/src/execution/engine/__tests__/routing.test.ts +0 -1
- package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +0 -3
- package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +0 -3
- package/src/execution/engine/client/__tests__/engine-client-reap.test.ts +212 -0
- package/src/execution/engine/client/__tests__/mirror.test.ts +73 -1
- package/src/execution/engine/client/__tests__/protocol-closure.test.ts +2 -9
- package/src/execution/engine/client/__tests__/remote-engine.test.ts +46 -12
- package/src/execution/engine/client/engine-client.ts +81 -25
- package/src/execution/engine/client/mirror.ts +9 -0
- package/src/execution/engine/client/remote-engine.ts +52 -34
- package/src/execution/engine/client/reverse-router.ts +6 -27
- package/src/execution/engine/common/__tests__/run-signals.test.ts +79 -0
- package/src/execution/engine/common/capability-gate.ts +27 -0
- package/src/execution/engine/common/data-dir.ts +3 -2
- package/src/execution/engine/common/errors.ts +1 -1
- package/src/execution/engine/common/journal-wiring.ts +18 -9
- package/src/execution/engine/common/run-signals.ts +83 -0
- package/src/execution/engine/d8-compat.ts +1 -1
- package/src/execution/engine/host/__tests__/host-bridge.test.ts +3 -18
- package/src/execution/engine/host/host-bridge.ts +19 -41
- package/src/execution/engine/host/pi-host-binding.ts +5 -5
- package/src/execution/engine/port.ts +34 -54
- package/src/execution/engine/types.ts +11 -14
- package/src/execution/execution-record.ts +15 -47
- package/src/execution/finalize-record.ts +94 -197
- package/src/execution/idle-gc.ts +9 -3
- package/src/execution/lifecycle-manager.ts +25 -361
- package/src/execution/lifecycle-predicates.ts +22 -19
- package/src/execution/manifest-store.ts +40 -42
- package/src/execution/model-config-service.ts +1 -1
- package/src/execution/notifier.ts +39 -1
- package/src/execution/notify-host.ts +13 -4
- package/src/execution/record-entry.ts +20 -2
- package/src/execution/record-store.ts +925 -160
- package/src/execution/relay-env.ts +8 -3
- package/src/execution/round-supervisor/domain.ts +17 -5
- package/src/execution/round-supervisor/service-binding.test.ts +89 -53
- package/src/execution/round-supervisor/service-binding.ts +71 -21
- package/src/execution/round-supervisor/supervisor.ts +23 -9
- package/src/execution/service/record-access.ts +547 -0
- package/src/execution/service/record-lifecycle.ts +542 -0
- package/src/execution/service/run-orchestration.ts +1647 -0
- package/src/execution/service/service-bootstrap.ts +111 -0
- package/src/execution/service/service-constants.ts +27 -0
- package/src/execution/service/session-baselines.ts +401 -0
- package/src/execution/service/sync-collect-domain.ts +442 -0
- package/src/execution/service/workflow-dispatch.ts +567 -0
- package/src/execution/session-file-gc.ts +11 -8
- package/src/execution/session-reconstructor.ts +44 -1
- package/src/execution/sessions-index.ts +20 -3
- package/src/execution/settled-watchdog.ts +86 -43
- package/src/execution/state-marker.ts +516 -0
- package/src/execution/stream-sink.ts +1 -1
- package/src/execution/subagent-actions-core.ts +53 -31
- package/src/execution/subagent-service.ts +643 -2980
- package/src/execution/subprocess-agent-runner.ts +46 -323
- package/src/execution/types.ts +60 -43
- package/src/execution/ui-request-handler-factory.ts +5 -0
- package/src/execution/worktree-manager.ts +1 -1
- package/src/execution/worktree-registry.ts +1 -1
- package/src/index.ts +6 -5
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -3
- package/src/orchestration/__tests__/agent-call-stream.test.ts +24 -49
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +4 -4
- package/src/orchestration/__tests__/file-run-store.test.ts +5 -7
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +2 -3
- package/src/orchestration/__tests__/run-snapshot.test.ts +27 -43
- package/src/orchestration/__tests__/worker-message-pump-workflow-dispatch.test.ts +274 -0
- package/src/orchestration/execute-agent-call.ts +1 -1
- package/src/orchestration/file-run-store.ts +1 -1
- package/src/orchestration/models/ports.ts +20 -13
- package/src/orchestration/models/types.ts +11 -13
- package/src/orchestration/run-snapshot.ts +18 -22
- package/src/orchestration/worker-message-pump.ts +33 -57
- package/src/shared/atomic-write.ts +10 -8
- package/dist/chunk-T2SYBW3I.js +0 -24
- package/dist/engine-discovery-scan-BXLA8y-z.d.cts +0 -1624
- package/dist/engine-discovery-scan-BXLA8y-z.d.ts +0 -1624
- package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +0 -255
- package/src/execution/__tests__/finalized-marker.test.ts +0 -104
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -488
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +0 -75
- package/src/execution/__tests__/tombstone-store.test.ts +0 -73
- package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +0 -230
- package/src/execution/finalized-marker.ts +0 -70
- package/src/execution/tombstone-store.ts +0 -72
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
ExecutionMode,
|
|
26
26
|
ExecutionStatus,
|
|
27
27
|
InternalToolCall,
|
|
28
|
+
RecordOrigin,
|
|
28
29
|
Turn,
|
|
29
30
|
} from "./types.ts";
|
|
30
31
|
import { extractLabelFromArgs } from "./execution-record.ts";
|
|
@@ -111,6 +112,14 @@ export interface SubagentIdentityData {
|
|
|
111
112
|
worktree?: boolean;
|
|
112
113
|
/** @deprecated 兼容旧文件:旧 identity entry 写的是 parentSessionId,读取时 fallback 到 rootSessionId。 */
|
|
113
114
|
parentSessionId?: string;
|
|
115
|
+
/**
|
|
116
|
+
* 来源身份(H2 S3 修复)。旧文件缺失 → undefined(= "tool" 语义,缺省零迁移)。
|
|
117
|
+
* standalone 形态(identity entry 链)的 workflow record 经本字段在重建面保留
|
|
118
|
+
* origin 过滤语义;读取侧经 normalizeReconOrigin 守卫归一(非法值 → undefined)。
|
|
119
|
+
*/
|
|
120
|
+
origin?: RecordOrigin;
|
|
121
|
+
/** origin="workflow" 时所属 workflow run id。旧文件缺失 → undefined。 */
|
|
122
|
+
parentRunId?: string;
|
|
114
123
|
}
|
|
115
124
|
|
|
116
125
|
/** SDK jsonl entry(getEntries() 返回,header 已排除)。 */
|
|
@@ -157,6 +166,14 @@ export interface ReconstructedRecord {
|
|
|
157
166
|
chatMode?: boolean;
|
|
158
167
|
/** [review round2] worktree 隔离标志(见 SubagentIdentityData.worktree)。 */
|
|
159
168
|
worktree?: boolean;
|
|
169
|
+
/**
|
|
170
|
+
* 来源身份(H2 S3 修复,来自 identity custom entry 经守卫归一;旧文件 undefined =
|
|
171
|
+
* "tool" 语义)。漏本字段则全量重建面(getFullRecord → reconstructFromFile)丢
|
|
172
|
+
* origin,workflow record 逃过 D1 投影过滤。
|
|
173
|
+
*/
|
|
174
|
+
origin: RecordOrigin | undefined;
|
|
175
|
+
/** origin="workflow" 时所属 workflow run id(守卫归一后;缺省 undefined)。 */
|
|
176
|
+
parentRunId: string | undefined;
|
|
160
177
|
/**
|
|
161
178
|
* 对话轮次计数(非 identity entry 字段,reconstructFromFile 不填)。
|
|
162
179
|
* V2 idle record 的 round 只在内存维护(doFinalizeRoundToIdle 递增),磁盘重建不恢复。
|
|
@@ -259,6 +276,21 @@ function isIdentityData(data: unknown): data is SubagentIdentityData {
|
|
|
259
276
|
typeof d.task === "string" &&
|
|
260
277
|
typeof d.startedAt === "number"
|
|
261
278
|
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 来源域守卫归一(H2 S3 修复):origin 非法值/缺省 → undefined(= "tool" 语义)。
|
|
283
|
+
* 字面量守卫与 record-store.readEntryOriginFields(主 entry 重建侧)逐字对齐——
|
|
284
|
+
* identity entry 的 data 经 JSON.parse 后 origin 运行时值不可信,TS 类型只是断言,
|
|
285
|
+
* 消费投影点必须守卫。
|
|
286
|
+
*/
|
|
287
|
+
function normalizeReconOrigin(v: unknown): RecordOrigin | undefined {
|
|
288
|
+
return v === "workflow" || v === "tool" ? v : undefined;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** parentRunId 安全读取(非 string → undefined),与 normalizeReconOrigin 同消费点。 */
|
|
292
|
+
function normalizeReconParentRunId(v: unknown): string | undefined {
|
|
293
|
+
return typeof v === "string" ? v : undefined;
|
|
262
294
|
}/**
|
|
263
295
|
* 待匹配的 toolCall(assistant 发起,等 toolResult 回填)。
|
|
264
296
|
* 记录在 assistant Turn 上,toolResult 到达时按 toolCallId 找到并填充。
|
|
@@ -485,6 +517,9 @@ function buildReconstructedRecord(
|
|
|
485
517
|
const rootSessionId = identity.rootSessionId ?? identity.parentSessionId;
|
|
486
518
|
// slug 兜底:旧 identity entry 无 slug 字段 → 空串(与 SubagentRecord.slug: string 契约一致)。
|
|
487
519
|
const slug = identity.slug ?? "";
|
|
520
|
+
// 来源域归一(H2 S3):非法值/缺省 → undefined = "tool" 语义(守卫见 normalizeReconOrigin)。
|
|
521
|
+
const origin = normalizeReconOrigin(identity.origin);
|
|
522
|
+
const parentRunId = normalizeReconParentRunId(identity.parentRunId);
|
|
488
523
|
return {
|
|
489
524
|
...identity,
|
|
490
525
|
slug,
|
|
@@ -492,6 +527,8 @@ function buildReconstructedRecord(
|
|
|
492
527
|
parentRecordId: identity.parentRecordId,
|
|
493
528
|
depth: identity.depth ?? 0,
|
|
494
529
|
forkDepth: identity.forkDepth,
|
|
530
|
+
origin,
|
|
531
|
+
parentRunId,
|
|
495
532
|
sessionFile,
|
|
496
533
|
status,
|
|
497
534
|
closedReason,
|
|
@@ -587,6 +624,10 @@ export interface IdentityHeaderRecon {
|
|
|
587
624
|
chatMode?: boolean;
|
|
588
625
|
/** [review round2] worktree 隔离标志(见 SubagentIdentityData.worktree)。 */
|
|
589
626
|
worktree?: boolean;
|
|
627
|
+
/** 来源身份(H2 S3 修复,守卫归一后;缺省 undefined = "tool" 语义)。light 列表投影用。 */
|
|
628
|
+
origin: RecordOrigin | undefined;
|
|
629
|
+
/** origin="workflow" 时所属 workflow run id(守卫归一后;缺省 undefined)。 */
|
|
630
|
+
parentRunId: string | undefined;
|
|
590
631
|
model: string;
|
|
591
632
|
thinkingLevel: string | undefined;
|
|
592
633
|
sessionFile: string;
|
|
@@ -743,7 +784,7 @@ function absorbScannedEntry(state: LightIdentityScan, entry: JsonlEntry): boolea
|
|
|
743
784
|
return false;
|
|
744
785
|
}
|
|
745
786
|
|
|
746
|
-
/** 扫描态 → IdentityHeaderRecon(归一化与全量 recon 同源:rootSessionId fallback 旧字段、slug
|
|
787
|
+
/** 扫描态 → IdentityHeaderRecon(归一化与全量 recon 同源:rootSessionId fallback 旧字段、slug 兜底空串、origin/parentRunId 守卫归一)。 */
|
|
747
788
|
function toIdentityRecon(
|
|
748
789
|
state: LightIdentityScan,
|
|
749
790
|
identity: SubagentIdentityData,
|
|
@@ -763,6 +804,8 @@ function toIdentityRecon(
|
|
|
763
804
|
forkDepth: identity.forkDepth,
|
|
764
805
|
chatMode: identity.chatMode,
|
|
765
806
|
worktree: identity.worktree,
|
|
807
|
+
origin: normalizeReconOrigin(identity.origin),
|
|
808
|
+
parentRunId: normalizeReconParentRunId(identity.parentRunId),
|
|
766
809
|
model: state.model,
|
|
767
810
|
thinkingLevel: state.thinkingLevel,
|
|
768
811
|
sessionFile,
|
|
@@ -26,7 +26,7 @@ import * as path from "node:path";
|
|
|
26
26
|
import { getLogger } from "../core/logger.ts";
|
|
27
27
|
import { writeAtomicFile } from "../shared/atomic-write.ts";
|
|
28
28
|
|
|
29
|
-
import type { ExecutionMode } from "./types.ts";
|
|
29
|
+
import type { ExecutionMode, RecordOrigin } from "./types.ts";
|
|
30
30
|
|
|
31
31
|
const logger = getLogger("subagents");
|
|
32
32
|
|
|
@@ -74,6 +74,14 @@ export interface SessionsIndexEntry {
|
|
|
74
74
|
/** 空串合法:尾部探测(readIdentityTail)拿不到 model 时的合法结果,不当损坏。 */
|
|
75
75
|
model: string;
|
|
76
76
|
thinkingLevel: string | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* 来源身份(H2 S3 修复:light→索引→重建往返闭合)。undefined 表示缺失
|
|
79
|
+
* (旧索引文件/origin 缺省 = "tool" 语义),可选字段不升 INDEX_VERSION——
|
|
80
|
+
* 对齐 rootSessionId/parentRecordId 先例(宽容守卫 + 序列化自然缺省)。
|
|
81
|
+
*/
|
|
82
|
+
origin?: RecordOrigin;
|
|
83
|
+
/** origin="workflow" 时所属 workflow run id(undefined = 缺失/tool 来源)。 */
|
|
84
|
+
parentRunId?: string;
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
/**
|
|
@@ -133,11 +141,19 @@ function hasSessionDescFields(v: Record<string, unknown>): boolean {
|
|
|
133
141
|
return typeof v.task === "string" && typeof v.slug === "string" && typeof v.startedAt === "number";
|
|
134
142
|
}
|
|
135
143
|
|
|
136
|
-
/** 可选 string 字段组(rootSessionId/parentRecordId:undefined 表示缺失,合法)。 */
|
|
144
|
+
/** 可选 string 字段组(rootSessionId/parentRecordId/parentRunId:undefined 表示缺失,合法)。 */
|
|
137
145
|
function hasOptionalStringFields(v: Record<string, unknown>): boolean {
|
|
138
146
|
return (
|
|
139
147
|
(v.rootSessionId === undefined || typeof v.rootSessionId === "string") &&
|
|
140
|
-
(v.parentRecordId === undefined || typeof v.parentRecordId === "string")
|
|
148
|
+
(v.parentRecordId === undefined || typeof v.parentRecordId === "string") &&
|
|
149
|
+
(v.parentRunId === undefined || typeof v.parentRunId === "string")
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** 来源域字段组(origin 守卫宽容:undefined 缺省合法,字面量白名单对齐 readEntryOriginFields)。 */
|
|
154
|
+
function hasOriginFields(v: Record<string, unknown>): boolean {
|
|
155
|
+
return (
|
|
156
|
+
v.origin === undefined || v.origin === "workflow" || v.origin === "tool"
|
|
141
157
|
);
|
|
142
158
|
}
|
|
143
159
|
|
|
@@ -158,6 +174,7 @@ function isPositiveIndexEntry(raw: unknown): raw is SessionsIndexEntry {
|
|
|
158
174
|
hasStampAndIdentityFields(v) &&
|
|
159
175
|
hasSessionDescFields(v) &&
|
|
160
176
|
hasOptionalStringFields(v) &&
|
|
177
|
+
hasOriginFields(v) &&
|
|
161
178
|
hasModelFields(v)
|
|
162
179
|
);
|
|
163
180
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// 中段(prompt → agent_end):工作段,输出即进展——无进展检测
|
|
9
9
|
// (armMidRoundNoProgress,锚点:prompt 发出时 arm;有效协议事件行刷新;
|
|
10
10
|
// 连续静默 SETTLED_MID_ROUND_NO_PROGRESS_MS=30min → kill + 该轮失败终态化)。
|
|
11
|
-
//
|
|
11
|
+
// 对齐既有 keep-alive 无进展检测先例的 30min 量级(同构思路:
|
|
12
12
|
// 「仍在推进」由 stdout 有效事件定义而非总时长,修复旧 10min 全程固定窗对
|
|
13
13
|
// >10min 合法 chatMode 单轮的误杀)。
|
|
14
14
|
//
|
|
@@ -31,14 +31,22 @@
|
|
|
31
31
|
// spawn watchdog 默认关」的三无窗口,warn 明示后果),未设/非法 = 默认 600s。
|
|
32
32
|
// 中段阈值 v1 不开 env(减法:与 keep-alive 先例同为纯常量)。
|
|
33
33
|
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
34
|
+
// [M3 耦合登记] 本原语自 M3 起被 workflow 域复用([H2 W2/W4] 起挂载点 = subagent-service.ts
|
|
35
|
+
// armWorkflowNoProgressWatchdog(runWorkflowEngineTask 内 arm;原 SAR.run per-call 守护
|
|
36
|
+
// 随 W4 掏空归位),armMidRoundNoProgress 同一入口)——故 env ≤0 的「关闭两段」不再是
|
|
37
|
+
// chat 域局部行为:workflow 域 G1 熔断(静默楔死 run 的 30min 无进展回收)一并失效。
|
|
38
|
+
// warn 文案已明示该连带后果(env 开关语义本身不动:设计明文「中段阈值 v1 不开 env」,
|
|
39
|
+
// 有界兜底的处置必须可解释,规则 19)。
|
|
40
|
+
//
|
|
41
|
+
// 挂载点(同一原语——同一组常量 + 同一组挂载/交棒/清除 helper;[H1 U6] 收敛为单挂载):
|
|
42
|
+
// - 会话形态轮:subagent-service.ts kickOffChatRound(轮开跑 arm 中段;[H1 U6] 旧
|
|
43
|
+
// 热路径 deliverChatMessage 第二挂载点随 interact 面退役)
|
|
44
|
+
// - workflow 域:subagent-service.ts armWorkflowNoProgressWatchdog([H2 W2];原
|
|
45
|
+
// SAR.run per-call 守护随 [H2 W4] 掏空归位,M3 起复用)
|
|
46
|
+
// 事件侧接线([H1 U6] 刷新源 = run 事件通道既有事件;交棒 = run 应答驱动):
|
|
47
|
+
// - 有效协议事件行 → refreshMidRoundNoProgress / refreshFromProtocolEvent(中段刷新)
|
|
48
|
+
// - run 应答收敛 → noteRoundSettledFromProtocol(交棒收尾段,Continuation onRunSettled)
|
|
49
|
+
// - 轮终簿记 / close / 终态化 → disarmRoundFromProtocol / disarmSettledWatchdog(两段一并清)
|
|
42
50
|
//
|
|
43
51
|
// 与 lifecycle-manager 的 idle timer 互补:idle timer 管 settled 已到达后的空闲
|
|
44
52
|
// 回收,本原语管 settled 永不到达的 wedged——两条正交通道不互相替代。
|
|
@@ -53,11 +61,11 @@ import { assertSafeTimerDelay } from "../shared/timer-delay.ts";
|
|
|
53
61
|
|
|
54
62
|
const logger = getLogger("subagents");
|
|
55
63
|
|
|
56
|
-
/** 时间单位换算常量(命名后供 watchdog
|
|
64
|
+
/** 时间单位换算常量(命名后供 watchdog 常量组合,消除裸乘法字面量)。 */
|
|
57
65
|
const MS_PER_SECOND = 1000;
|
|
58
66
|
const SECONDS_PER_MINUTE = 60;
|
|
59
67
|
|
|
60
|
-
/**
|
|
68
|
+
/** 中段无进展阈值(分钟):对齐既有 keep-alive 无进展检测先例的 30min 量级。 */
|
|
61
69
|
const MID_ROUND_MINUTES = 30;
|
|
62
70
|
|
|
63
71
|
/**
|
|
@@ -73,7 +81,7 @@ export const SETTLED_WATCHDOG_TIMEOUT_MS = 600_000;
|
|
|
73
81
|
/**
|
|
74
82
|
* 中段(prompt → agent_end)无进展检测的连续静默阈值(30min)。
|
|
75
83
|
*
|
|
76
|
-
*
|
|
84
|
+
* 对齐既有 keep-alive 无进展检测先例(30min 无进展检测,量级同源)。
|
|
77
85
|
* 刷新面严格限定 stdout pump 解析出的**有效协议事件行**(message_* 与 tool_* 与 turn_end
|
|
78
86
|
* 等 SdkEvent)——LC-9 的 invalid 行(非法 JSON / 缺 type 字段的调试噪音)不刷新,
|
|
79
87
|
* 防调试输出续命。v1 不开 env(设计 §6-D9 定案:与 keep-alive 同为纯常量)。
|
|
@@ -81,14 +89,41 @@ export const SETTLED_WATCHDOG_TIMEOUT_MS = 600_000;
|
|
|
81
89
|
*/
|
|
82
90
|
export const SETTLED_MID_ROUND_NO_PROGRESS_MS = MID_ROUND_MINUTES * SECONDS_PER_MINUTE * MS_PER_SECOND;
|
|
83
91
|
|
|
92
|
+
/**
|
|
93
|
+
* [M6,仅测试可达] 中段窗长覆盖值(秒级窗真跑全链测试的注入口)。
|
|
94
|
+
*
|
|
95
|
+
* 生产恒 undefined → 生效值 = SETTLED_MID_ROUND_NO_PROGRESS_MS(30min,默认值由单测
|
|
96
|
+
* 断言守护)。形态对齐既有测试钩子先例 `_resetSettledWatchdogsForTest`(模块级状态 +
|
|
97
|
+
* `_` 前缀导出),不新造第二套模式;`_resetSettledWatchdogsForTest` 一并复位,防用例间
|
|
98
|
+
* 污染。刻意不做 env:设计明文「中段阈值 v1 不开 env」(每个 knob 都是误配置通道,
|
|
99
|
+
* 用户调短会误杀正常长任务)。
|
|
100
|
+
*/
|
|
101
|
+
let midRoundWindowOverrideMs: number | undefined;
|
|
102
|
+
|
|
103
|
+
/** 中段生效窗长:测试注入优先,否则常量默认(生产恒 30min)。arm 与 refresh 共用单一读取点。 */
|
|
104
|
+
export function getMidRoundNoProgressWindowMs(): number {
|
|
105
|
+
return midRoundWindowOverrideMs ?? SETTLED_MID_ROUND_NO_PROGRESS_MS;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* [仅测试] 覆盖中段无进展窗长(传 undefined 复位)。生产路径零调用——生产生效值恒为
|
|
110
|
+
* SETTLED_MID_ROUND_NO_PROGRESS_MS。
|
|
111
|
+
*/
|
|
112
|
+
export function _setMidRoundNoProgressWindowMsForTest(ms: number | undefined): void {
|
|
113
|
+
midRoundWindowOverrideMs = ms;
|
|
114
|
+
}
|
|
115
|
+
|
|
84
116
|
/**
|
|
85
117
|
* 收尾段定值的用户覆盖 env(规则 19:用户显式指定才生效)。
|
|
86
118
|
*
|
|
87
119
|
* >0 覆盖收尾段(SETTLED_WATCHDOG_TIMEOUT_MS 默认);≤0 关闭两段(回到三无窗口,
|
|
88
|
-
* warn
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
120
|
+
* warn 明示后果——[M3 耦合登记] 自 M3 起该 no-op 同时关掉 workflow 域 G1 熔断:
|
|
121
|
+
* subagent-service.ts armWorkflowNoProgressWatchdog(原 SAR.run per-call 守护,[H2 W4]
|
|
122
|
+
* 掏空归位)的 no-progress 守护经 armMidRoundNoProgress 同一入口挂载,watchdog 不 arm
|
|
123
|
+
* 则 workflow 域静默楔死同样无独立回收计时);未设 = 默认 600s;非数字 = 非法回落
|
|
124
|
+
* 默认 + warn 留痕(对齐 XYZ_SUBAGENT_IDLE_TIMEOUT_MS 的 LC-7 教训:非法回落必须
|
|
125
|
+
* 可见)。前缀用 XYZ_SUBAGENT_*(ENV_WHITELIST_PREFIXES 白名单,PI_ 前缀在桌面
|
|
126
|
+
* spawn 链被静默丢弃——同 XYZ_SUBAGENT_IDLE_TIMEOUT_MS 改名教训)。
|
|
92
127
|
*/
|
|
93
128
|
export const SETTLED_WATCHDOG_ENV = "XYZ_SUBAGENT_SETTLED_WATCHDOG_MS";
|
|
94
129
|
|
|
@@ -134,9 +169,13 @@ function resolveSettledWatchdogEnv(): { disabled: boolean; overrideMs?: number }
|
|
|
134
169
|
if (parsed <= 0) {
|
|
135
170
|
logger.warn(
|
|
136
171
|
`[settled-watchdog] ${SETTLED_WATCHDOG_ENV}=${parsed} disables BOTH watchdog phases (mid-round ` +
|
|
137
|
-
`no-progress + settled phase limit).
|
|
138
|
-
`agent_settled never arriving) has NO independent recovery timer — the process
|
|
139
|
-
`host exits (the "three-no-window" shape)
|
|
172
|
+
`no-progress + settled phase limit) for ALL domains. Consequences: (1) a wedged chatMode round ` +
|
|
173
|
+
`(no agent_end, or agent_settled never arriving) has NO independent recovery timer — the process ` +
|
|
174
|
+
`leaks until the host exits (the "three-no-window" shape); (2) the workflow-domain no-progress ` +
|
|
175
|
+
`fuse (M3: workflow dispatch arms through this same primitive — ` +
|
|
176
|
+
`SubagentService.runWorkflowEngineTask, see [H2 W4]) is silently disabled too — a wedged workflow ` +
|
|
177
|
+
`run then stalls with no terminal notification even though the chat-domain docs only describe (1). ` +
|
|
178
|
+
`Recovery: unset the env or set a positive ms value.`,
|
|
140
179
|
);
|
|
141
180
|
envCache.disabled = true;
|
|
142
181
|
return envCache;
|
|
@@ -180,7 +219,8 @@ function clearEntry(recordId: string): void {
|
|
|
180
219
|
* - 到期行为由调用方注入(kill 进程 + 该轮失败终态化)——本模块不持有
|
|
181
220
|
* ChildProcess / record,与 lifecycle-manager 同构(副作用能力由调用方注入,
|
|
182
221
|
* 本模块只管 timer 生命周期,可独立编译 + 单测)。
|
|
183
|
-
* - env 显式关闭(≤0)时 no-op(解析时已 warn
|
|
222
|
+
* - env 显式关闭(≤0)时 no-op(解析时已 warn 明示后果——含 workflow 域熔断连带的
|
|
223
|
+
* 明示文案;M3 复用本入口,故本 no-op 不是 chat 域局部行为)。
|
|
184
224
|
*/
|
|
185
225
|
export function armMidRoundNoProgress(
|
|
186
226
|
recordId: string,
|
|
@@ -191,15 +231,17 @@ export function armMidRoundNoProgress(
|
|
|
191
231
|
): void {
|
|
192
232
|
if (isSettledWatchdogDisabled()) return;
|
|
193
233
|
clearEntry(recordId);
|
|
194
|
-
//
|
|
195
|
-
|
|
234
|
+
// 生产恒为常量(安全域内);测试注入的秒级窗同样过校验——入口统一校验防未来值改
|
|
235
|
+
// 可配置时静默引入 1ms 溢出语义反转。
|
|
236
|
+
const windowMs = getMidRoundNoProgressWindowMs();
|
|
237
|
+
assertSafeTimerDelay(windowMs, "settled watchdog (mid-round)");
|
|
196
238
|
const timer = setTimeout(() => {
|
|
197
239
|
armedEntries.delete(recordId);
|
|
198
240
|
logger.debug(
|
|
199
|
-
`[settled-watchdog] mid-round fired for ${recordId} after ${
|
|
241
|
+
`[settled-watchdog] mid-round fired for ${recordId} after ${windowMs}ms without a valid protocol event`,
|
|
200
242
|
);
|
|
201
|
-
handlers.onMidTimeout({ phase: "mid-round", waitedMs:
|
|
202
|
-
},
|
|
243
|
+
handlers.onMidTimeout({ phase: "mid-round", waitedMs: windowMs });
|
|
244
|
+
}, windowMs);
|
|
203
245
|
timer.unref();
|
|
204
246
|
armedEntries.set(recordId, {
|
|
205
247
|
timer,
|
|
@@ -263,15 +305,16 @@ export function refreshMidRoundNoProgress(recordId: string): void {
|
|
|
263
305
|
const entry = armedEntries.get(recordId);
|
|
264
306
|
if (!entry || entry.phase !== "mid-round") return;
|
|
265
307
|
clearTimeout(entry.timer);
|
|
266
|
-
|
|
308
|
+
const windowMs = getMidRoundNoProgressWindowMs();
|
|
309
|
+
assertSafeTimerDelay(windowMs, "settled watchdog (mid-round refresh)");
|
|
267
310
|
const onMidTimeout = entry.onMidTimeout;
|
|
268
311
|
const timer = setTimeout(() => {
|
|
269
312
|
armedEntries.delete(recordId);
|
|
270
313
|
logger.debug(
|
|
271
|
-
`[settled-watchdog] mid-round fired for ${recordId} after ${
|
|
314
|
+
`[settled-watchdog] mid-round fired for ${recordId} after ${windowMs}ms without a valid protocol event`,
|
|
272
315
|
);
|
|
273
|
-
onMidTimeout?.({ phase: "mid-round", waitedMs:
|
|
274
|
-
},
|
|
316
|
+
onMidTimeout?.({ phase: "mid-round", waitedMs: windowMs });
|
|
317
|
+
}, windowMs);
|
|
275
318
|
timer.unref();
|
|
276
319
|
entry.timer = timer;
|
|
277
320
|
}
|
|
@@ -299,8 +342,9 @@ export function disarmSettledWatchdog(recordId: string): void {
|
|
|
299
342
|
}
|
|
300
343
|
|
|
301
344
|
/**
|
|
302
|
-
* 清空全部 armed entry + env
|
|
303
|
-
* _resetLifecycleState 先例。清缓存使测试内 vi.stubEnv 改 env
|
|
345
|
+
* 清空全部 armed entry + env 解析缓存 + 中段窗测试覆盖(测试隔离用,beforeEach/afterEach
|
|
346
|
+
* 调;命名对齐 _resetLifecycleState 先例。清缓存使测试内 vi.stubEnv 改 env 后重新解析
|
|
347
|
+
* 生效;复位 midRoundWindowOverrideMs 防 M6 秒级窗污染其他用例)。
|
|
304
348
|
*/
|
|
305
349
|
export function _resetSettledWatchdogsForTest(): void {
|
|
306
350
|
for (const entry of armedEntries.values()) {
|
|
@@ -308,42 +352,41 @@ export function _resetSettledWatchdogsForTest(): void {
|
|
|
308
352
|
}
|
|
309
353
|
armedEntries.clear();
|
|
310
354
|
envCache = undefined;
|
|
355
|
+
midRoundWindowOverrideMs = undefined;
|
|
311
356
|
}
|
|
312
357
|
|
|
313
|
-
// ── [W4] 协议事件面接线 API
|
|
358
|
+
// ── [W4] 协议事件面接线 API ────────────────────────────────────────────
|
|
314
359
|
//
|
|
315
360
|
// 设计权威源:docs/design/chat-domain-v1x-liveness-governance.md §3.2 D2 前置 1 +
|
|
316
361
|
// D5「settled-watchdog 生产接线重接」:两段守护的 refresh 源随 chat 域 cli 化改挂
|
|
317
|
-
// 协议事件流(arm 点 =
|
|
318
|
-
// 事件;kill/终态 =
|
|
319
|
-
//
|
|
320
|
-
// 其替换目标:W3 删旧接线时改调下面三个命名入口,语义逐一同构)。
|
|
362
|
+
// 协议事件流(arm 点 = 轮开跑;中段刷新源 = host/streamDelta 与 run 事件通道
|
|
363
|
+
// 事件;kill/终态 = 既有杀链)。旧 inproc stdout-pump 接线(session-runner.ts)已随
|
|
364
|
+
// W3 删件移除,下面三个命名入口是协议事件面的唯一驱动入口。
|
|
321
365
|
//
|
|
322
|
-
//
|
|
323
|
-
// W3 负责删旧,两套接线并存期由原语幂等吸收重复调用):
|
|
366
|
+
// 命名入口与既有原语的映射(刻意薄委托、零新语义):
|
|
324
367
|
// refreshFromProtocolEvent ↔ refreshMidRoundNoProgress(协议事件行到达)
|
|
325
|
-
// noteRoundSettledFromProtocol ↔ handoverMidRoundToSettled
|
|
368
|
+
// noteRoundSettledFromProtocol ↔ handoverMidRoundToSettled(轮次收敛交棒
|
|
326
369
|
// settled 相位 = 轮收敛,中段让位收尾段)
|
|
327
370
|
// disarmRoundFromProtocol ↔ disarmSettledWatchdog(idle 相位 / close / 终态)
|
|
328
371
|
//
|
|
329
372
|
// 监督器域 resume 轮的 refresh 覆盖:resume 轮的 arm 点在 subagent-service
|
|
330
373
|
// deliverChatMessage(interact 返回点,冷热路径同点),其轮内协议事件(streamDelta /
|
|
331
|
-
//
|
|
374
|
+
// 协议事件行)到达时经 refreshFromProtocolEvent 刷新——W3 接线后两域轮(chat 轮
|
|
332
375
|
// 与监督器域 resume 轮)共用同一事件面。
|
|
333
376
|
|
|
334
|
-
/** [W4 协议事件面] 协议事件行(host/streamDelta /
|
|
377
|
+
/** [W4 协议事件面] 协议事件行(host/streamDelta / run 事件通道)到达:刷新
|
|
335
378
|
* 中段无进展计时。未挂载 / 已交棒(收尾段不刷新)/ 已 fire 时幂等 no-op。 */
|
|
336
379
|
export function refreshFromProtocolEvent(recordId: string): void {
|
|
337
380
|
refreshMidRoundNoProgress(recordId);
|
|
338
381
|
}
|
|
339
382
|
|
|
340
|
-
/** [W4
|
|
383
|
+
/** [W4] 轮次收敛(run 应答 settle,[H1 U6] run 应答驱动交棒):中段让位收尾段
|
|
341
384
|
* (两段独立计时,交棒语义见 handoverMidRoundToSettled)。未挂载时幂等 no-op。 */
|
|
342
385
|
export function noteRoundSettledFromProtocol(recordId: string): void {
|
|
343
386
|
handoverMidRoundToSettled(recordId);
|
|
344
387
|
}
|
|
345
388
|
|
|
346
|
-
/** [W4
|
|
389
|
+
/** [W4] 轮终簿记 / close / 终态化处置:两段一并清。
|
|
347
390
|
* 不存在 armed entry 时幂等 no-op。 */
|
|
348
391
|
export function disarmRoundFromProtocol(recordId: string): void {
|
|
349
392
|
disarmSettledWatchdog(recordId);
|