@zhushanwen/pi-subagent-workflow 8.14.3 → 8.14.4
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/package.json +11 -10
- package/relay/relay.mjs +1 -1
- package/skills/subagent-ext-config/SKILL.md +1 -1
- package/src/host/pi-host.ts +1 -1
- package/src/index.ts +46 -410
- package/src/injectors/workflow-list-injector.ts +6 -1
- package/src/interface/__tests__/tool-subagents.test.ts +494 -0
- package/src/interface/bg-notify-render.ts +4 -27
- package/src/interface/commands.ts +3 -5
- package/src/interface/format.ts +2 -2
- package/src/interface/helpers.ts +86 -28
- package/src/interface/id-preview.ts +12 -0
- package/src/interface/subagent-actions.ts +3 -5
- package/src/interface/subagent-tool-schema.ts +7 -17
- package/src/interface/subagent-tool.ts +5 -13
- package/src/interface/subagents.ts +2 -1
- package/src/interface/tool-result.ts +40 -0
- package/src/interface/tool-shared.ts +109 -0
- package/src/interface/tool-subagents.ts +310 -0
- package/src/interface/tool-workflow-script.ts +5 -9
- package/src/interface/tool-workflow.ts +40 -59
- package/src/jsonl-run-store.ts +65 -5
- package/src/session-lifecycle.ts +184 -40
- package/src/workflow-events.ts +576 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-subagent-workflow",
|
|
3
|
-
"version": "8.14.
|
|
3
|
+
"version": "8.14.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"description": "Unified subagent execution and multi-agent workflow orchestration for Pi — spawned-process agent runtime with sync/background modes, stateful workflow management with persistence, state machine, and execution tracing.",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"src/index.ts",
|
|
26
26
|
"src/jsonl-run-store.ts",
|
|
27
27
|
"src/session-lifecycle.ts",
|
|
28
|
+
"src/workflow-events.ts",
|
|
28
29
|
"src/host/",
|
|
29
30
|
"src/injectors/",
|
|
30
31
|
"src/interface/"
|
|
@@ -54,21 +55,21 @@
|
|
|
54
55
|
]
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
|
-
"@zhushanwen/extension-protocol": "0.
|
|
58
|
-
"@zhushanwen/session-delivery": "0.10.
|
|
59
|
-
"@zhushanwen/pi-ext-guards": "0.4.
|
|
60
|
-
"@zhushanwen/pi-extension-logger": "0.6.
|
|
61
|
-
"@zhushanwen/subagent-core": "0.10.
|
|
62
|
-
"@zhushanwen/pi-subagent-cli": "0.4.
|
|
63
|
-
"@zhushanwen/zcode-subagent-cli": "0.3.
|
|
58
|
+
"@zhushanwen/extension-protocol": "0.12.0",
|
|
59
|
+
"@zhushanwen/session-delivery": "0.10.1",
|
|
60
|
+
"@zhushanwen/pi-ext-guards": "0.4.1",
|
|
61
|
+
"@zhushanwen/pi-extension-logger": "0.6.1",
|
|
62
|
+
"@zhushanwen/subagent-core": "0.10.5",
|
|
63
|
+
"@zhushanwen/pi-subagent-cli": "0.4.1",
|
|
64
|
+
"@zhushanwen/zcode-subagent-cli": "0.3.2"
|
|
64
65
|
},
|
|
65
66
|
"peerDependencies": {
|
|
66
67
|
"@earendil-works/pi-ai": "^0.84.4",
|
|
67
68
|
"@earendil-works/pi-coding-agent": "^0.84.4",
|
|
68
69
|
"@earendil-works/pi-tui": "^0.84.4",
|
|
69
70
|
"typebox": "*",
|
|
70
|
-
"@zhushanwen/pi-pending-notifications": "0.7.
|
|
71
|
-
"@zhushanwen/pi-structured-output": "5.1.
|
|
71
|
+
"@zhushanwen/pi-pending-notifications": "0.7.5",
|
|
72
|
+
"@zhushanwen/pi-structured-output": "5.1.8"
|
|
72
73
|
},
|
|
73
74
|
"peerDependenciesMeta": {
|
|
74
75
|
"@earendil-works/pi-coding-agent": {
|
package/relay/relay.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* subagent relay 代理 CLI
|
|
3
|
+
* subagent relay 代理 CLI。
|
|
4
4
|
*
|
|
5
5
|
* 角色:双面字节泵——extension 眼中的 pi 子进程(stdio 与 spawn 真实 pi 字节级同构:
|
|
6
6
|
* 逐行 JSONL RPC / get_state 握手由 extension 侧 session-runner 原样驱动,代理只搬运字节)
|
|
@@ -86,7 +86,7 @@ config.json 位于 pi agent 目录下的 `subagents/config.json`,随环境不
|
|
|
86
86
|
|
|
87
87
|
1. **新建 session**(必须——当前 session 不重读配置)。
|
|
88
88
|
2. 让主 agent 派一个 subagent(例:用 `subagents` 工具发个简单任务)。
|
|
89
|
-
3. taiji
|
|
89
|
+
3. taiji composer **任务托盘**(task tray)的 Subagents 面板看该项最左的引擎 icon(pi / zcode)——这是统一验证面。
|
|
90
90
|
4. journal 落点 `~/.taiji-dev/engines/<engineId>/` **仅适用非 pi 引擎**(zcode 分支建 journal);pi 分支不建 journal,pi 任务以 icon 为验证面。
|
|
91
91
|
|
|
92
92
|
## 常见错误排查
|
package/src/host/pi-host.ts
CHANGED
|
@@ -43,7 +43,7 @@ import { toErrorMessage } from "@zhushanwen/pi-ext-guards";
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* core 包(@zhushanwen/subagent-core)agents/ 资产进 pi 发现面的注入根(C5⑥,
|
|
46
|
-
* convergence §5.4 检查点 2
|
|
46
|
+
* convergence §5.4 检查点 2)。
|
|
47
47
|
*
|
|
48
48
|
* 解析锚点 = `@zhushanwen/subagent-core/workflows/README.md`:`./workflows/*` 子入口
|
|
49
49
|
* 在 workspace TS 直引与 npm dist 两种发布形态下同径(publishConfig 保留该子入口),
|
package/src/index.ts
CHANGED
|
@@ -6,19 +6,24 @@
|
|
|
6
6
|
* + messageRenderer(subagent-bg-notify)+ pi.__workflowRun + session 事件。
|
|
7
7
|
*
|
|
8
8
|
* 包内结构(执行运行时已迁 packages/subagent-core,本包只留注册面与宿主适配):
|
|
9
|
-
* interface/
|
|
10
|
-
* host/
|
|
11
|
-
* injectors/
|
|
9
|
+
* interface/ → 注册胶水(tools / commands / TUI 渲染 / GUI mappers)
|
|
10
|
+
* host/ → pi 宿主端口实现(HostServices / NotifyDomain 的 pi 侧兑现)
|
|
11
|
+
* injectors/ → 提示注入器(engine-awareness / model-list / resource-list …)
|
|
12
12
|
* session-lifecycle.ts → 会话生命周期装配 seam(测试可注入 fake 依赖)
|
|
13
|
+
* workflow-events.ts → workflow 域事件族装配 seam(7 个 pi.on handler + deps 装配)
|
|
14
|
+
*
|
|
15
|
+
* 本文件 = 组合根:只留注册(tools / commands / renderer / pi.__workflowRun / 进程级
|
|
16
|
+
* 信号 hook)与装配接线(core 端口 / 注入器 / 两个 seam 调用)。
|
|
13
17
|
*
|
|
14
18
|
* 架构导航见 docs/extensions/subagents/architecture.md。
|
|
15
19
|
*
|
|
16
20
|
* 设计基线:D-004(旧包不动)/ ADR-025(进程内执行)/ D-8(pi.__workflowRun 签名)。
|
|
17
21
|
*/
|
|
18
22
|
|
|
19
|
-
import type { ExtensionAPI
|
|
23
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
20
24
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
21
25
|
import { getLogger, setPiHandle } from "@zhushanwen/pi-extension-logger";
|
|
26
|
+
import { toErrorMessage } from "@zhushanwen/pi-ext-guards";
|
|
22
27
|
|
|
23
28
|
// ═══ core 宿主端口接线(subagent-core 包抽离 u0-wire;实现见 src/host/pi-host.ts) ═══
|
|
24
29
|
import { configureCore } from "@zhushanwen/subagent-core";
|
|
@@ -28,7 +33,6 @@ import { createPiHostServices, createPiNotifyDomainPorts } from "./host/pi-host.
|
|
|
28
33
|
// [u7a D5] 壳层在途上报出口:core 状态迁移 → 本出口 → select 通道(marker 帧)→ runtime。
|
|
29
34
|
import { createInFlightReporter } from "./host/inflight-reporter.ts";
|
|
30
35
|
|
|
31
|
-
import { bestEffort } from "@zhushanwen/subagent-core";
|
|
32
36
|
// ═══ 经 core barrel 消费执行域(执行运行时住 packages/subagent-core) ═══
|
|
33
37
|
// [U7] 引擎列表状态文件(registry → engines.json,GUI 引擎选择器数据源)
|
|
34
38
|
import { syncEnginesFile } from "@zhushanwen/subagent-core";
|
|
@@ -38,10 +42,8 @@ import { syncEnginesFile } from "@zhushanwen/subagent-core";
|
|
|
38
42
|
// [P3 引擎接线] 组合根登记 'zcode' 引擎(D8 薄壳:vendored 定位 cli descriptor;
|
|
39
43
|
// engineDataDir 默认走 common/data-dir SSOT)
|
|
40
44
|
import { registerZcodeEngine } from "@zhushanwen/subagent-core";
|
|
41
|
-
import { getModelConfigService } from "@zhushanwen/subagent-core";
|
|
42
|
-
import { getBoundNotifyLedger } from "@zhushanwen/subagent-core";
|
|
43
|
-
import { getSubagentService } from "@zhushanwen/subagent-core";
|
|
44
45
|
import { killAllSpawnedChildren } from "@zhushanwen/subagent-core";
|
|
46
|
+
import { runAndWait, type WorkflowRunResult } from "@zhushanwen/subagent-core";
|
|
45
47
|
// [engine-awareness U3/D7-④] per-turn 引擎检测编排 + before_agent_start 链尾接线
|
|
46
48
|
import { setupEngineAwarenessInjector } from "./injectors/engine-awareness.ts";
|
|
47
49
|
import { setupModelListInjector } from "./injectors/model-list-injector.ts";
|
|
@@ -49,33 +51,15 @@ import { setupSubagentListInjector } from "./injectors/subagent-list-injector.ts
|
|
|
49
51
|
import { setupWorkflowListInjector } from "./injectors/workflow-list-injector.ts";
|
|
50
52
|
import { renderBgNotifyMessage } from "./interface/bg-notify-render.ts";
|
|
51
53
|
import { registerWorkflowsCommand } from "./interface/commands.ts";
|
|
52
|
-
import { toGuiCtx } from "./interface/gui-mappers.ts";
|
|
53
|
-
import { notifyDone, trackNotifiedRunId } from "./interface/helpers.ts";
|
|
54
54
|
import { registerSubagentTool } from "./interface/subagent-tool.ts";
|
|
55
55
|
// ═══ interface/ 层(tools/commands/tui 合并) ═══
|
|
56
56
|
import { registerSubagentsCommand } from "./interface/subagents.ts";
|
|
57
|
+
import { registerSubagentsTool } from "./interface/tool-subagents.ts";
|
|
57
58
|
import { registerWorkflowTool } from "./interface/tool-workflow.ts";
|
|
58
59
|
import { registerWorkflowScriptTool } from "./interface/tool-workflow-script.ts";
|
|
59
|
-
// ═══
|
|
60
|
-
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
63
|
-
evictDoneRunsBeyondCap,
|
|
64
|
-
MAX_RETAINED_DONE_RUNS,
|
|
65
|
-
scheduleTimeBudget,
|
|
66
|
-
terminateRunningRuns,
|
|
67
|
-
} from "@zhushanwen/subagent-core";
|
|
68
|
-
import type { WorkflowRun } from "@zhushanwen/subagent-core";
|
|
69
|
-
import { WorkerHostImpl } from "@zhushanwen/subagent-core";
|
|
70
|
-
import { WorkflowScriptRegistryImpl } from "@zhushanwen/subagent-core";
|
|
71
|
-
// ═══ session 生命周期装配 seam(bootstrap seam,设计 §3.1/D1) ═══
|
|
72
|
-
import {
|
|
73
|
-
getOrCreateDialogQueue,
|
|
74
|
-
setupSessionLifecycle,
|
|
75
|
-
type SessionLifecycleDeps,
|
|
76
|
-
type SessionLifecycleResult,
|
|
77
|
-
} from "./session-lifecycle.ts";
|
|
78
|
-
import { toErrorMessage } from "@zhushanwen/pi-ext-guards";
|
|
60
|
+
// ═══ workflow 域事件族装配 seam(7 个 pi.on handler + makeDeps/getWorkflowDeps/
|
|
61
|
+
// lazyDeps;与 session-lifecycle.ts 同构,D2 原样搬移 + lazyDeps 样板收敛) ═══
|
|
62
|
+
import { setupWorkflowDomain } from "./workflow-events.ts";
|
|
79
63
|
|
|
80
64
|
// ── pi.__workflowRun 类型扩展(D-8 签名) ─────────────────
|
|
81
65
|
|
|
@@ -166,6 +150,14 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
|
|
|
166
150
|
// 用户体验拍板 2026-08-25:taiji 打开后激活任意 session 的第一时间(含 TUI 等价
|
|
167
151
|
// 场景)GUI 引擎选择器就该有数据;session_start 处保留幂等重写兜底 jiti 双路径/
|
|
168
152
|
// 模块重载场景的刷新)。
|
|
153
|
+
// [skill-reload D9] oncePerProcess 不提权(用户裁决):守卫 Map 是 ext-guards
|
|
154
|
+
// 模块级状态,jiti reload 后归零 → 进程级维护跨 reload 幂等重跑属预期,不为此
|
|
155
|
+
// 提权 globalThis。factory 期引擎重注册的危险面已由 D2b 消除——
|
|
156
|
+
// registerEngineDescriptor 稳定标识等价则保留单例不 dispose(幂等重注册,杀令 B
|
|
157
|
+
// 拆除),本调用点的 discovery 面与上方 registerZcodeEngine 都经此入口;标识变化
|
|
158
|
+
// (引擎包真升级)仍走现状 dispose。恢复误杀面由 session_start(reason==='reload')
|
|
159
|
+
// 门控消除(D4,session-lifecycle.ts recoverCrashedRuns 守卫调用点的
|
|
160
|
+
// [skill-reload D4] 注释有对应登记)。
|
|
169
161
|
syncEnginesFile(getAgentDir());
|
|
170
162
|
|
|
171
163
|
// ════════════════════════════════════════════════════════════
|
|
@@ -188,159 +180,15 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
|
|
|
188
180
|
setupWorkflowListInjector(pi);
|
|
189
181
|
setupModelListInjector(pi);
|
|
190
182
|
|
|
191
|
-
// [主 session 文件解析随迁] cachedMainSessionFile 缓存与 resolveMainSessionFileById
|
|
192
|
-
// 随 session_start 装配块整体迁入 session-lifecycle.ts(唯一消费方是随迁块;
|
|
193
|
-
// SubagentService 的 getMainSessionFile getter 由 createOrReuseServices 提供)。
|
|
194
|
-
|
|
195
|
-
// resources_discover:不再注册 handler(v4 决策:不再注入额外 skill 目录,
|
|
196
|
-
// ADR-031 废弃 discovery.json)。pi 核心 auto-discovery 已覆盖 .agents/skills
|
|
197
|
-
// 等标准目录,子 session 的 --skill 由 agent({skill}) 调用方显式传入,无需
|
|
198
|
-
// extension 额外补充。
|
|
199
|
-
|
|
200
|
-
// ════════════════════════════════════════════════════════════
|
|
201
|
-
// workflow 域:tools + command + pi.__workflowRun + state
|
|
202
|
-
// ════════════════════════════════════════════════════════════
|
|
203
|
-
const lsRef = { lastSessionId: "" };
|
|
204
|
-
const notifiedRunIds = new Set<string>();
|
|
205
|
-
const guard = { isProcessing: false };
|
|
206
|
-
|
|
207
|
-
// Infra 实例(per-factory 单例,跨 session 复用)
|
|
208
|
-
const workerHost = new WorkerHostImpl();
|
|
209
|
-
const registry = new WorkflowScriptRegistryImpl();
|
|
210
|
-
|
|
211
|
-
// SAR 改为 per-session 构造(需要 ctxModel 填底 D-008 + subagentService 委托目标),
|
|
212
|
-
// 构造点随 session_start 装配块迁入 session-lifecycle.ts,经 SessionLifecycleResult
|
|
213
|
-
// 传回(见 setupSessionLifecycle)。
|
|
214
|
-
|
|
215
|
-
// per-session 状态(session_start 时重建)。value = SessionLifecycleResult
|
|
216
|
-
// (setupSessionLifecycle 装配结果,ctx 必有)。
|
|
217
|
-
const sessionState = new Map<string, SessionLifecycleResult>();
|
|
218
|
-
|
|
219
|
-
function log(
|
|
220
|
-
level: "debug" | "info" | "warn" | "error",
|
|
221
|
-
component: string,
|
|
222
|
-
message: string,
|
|
223
|
-
data?: unknown,
|
|
224
|
-
): void {
|
|
225
|
-
try {
|
|
226
|
-
pi.appendEntry("workflow:log", {
|
|
227
|
-
timestamp: Date.now(),
|
|
228
|
-
level,
|
|
229
|
-
component,
|
|
230
|
-
message,
|
|
231
|
-
data,
|
|
232
|
-
});
|
|
233
|
-
} catch (err) {
|
|
234
|
-
void err;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// [workflow state 目录解析随迁] resolveSessionDir 随 session_start 装配块整体迁入
|
|
239
|
-
// session-lifecycle.ts(唯一消费方是随迁块)。
|
|
240
|
-
|
|
241
|
-
/** 组合根侧生产 deps 工厂:SessionLifecycleDeps 全部成员有生产默认实现(住
|
|
242
|
-
* session-lifecycle.ts——createOrReuseServices 单例语义 / WorktreeManager 每次
|
|
243
|
-
* 扫描新建 / JsonlRunStore per-session 新建),此处无本地构造可注入;工厂形态
|
|
244
|
-
* 保留为 index 侧注入点(测试或后续演进可在此覆盖)。
|
|
245
|
-
* 测试注入路径:不挂载 index.ts,直接调 setupSessionLifecycle(pi, ctx, fakeDeps)。 */
|
|
246
|
-
function makeLifecycleDeps(): SessionLifecycleDeps {
|
|
247
|
-
return {};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function makeDeps(
|
|
251
|
-
state: Pick<SessionLifecycleResult, "store" | "runs" | "sessionDir" | "runner">,
|
|
252
|
-
sessionCtx?: ExtensionContext,
|
|
253
|
-
) {
|
|
254
|
-
const deps: LauncherDeps = {
|
|
255
|
-
store: state.store,
|
|
256
|
-
workerHost,
|
|
257
|
-
runner: state.runner,
|
|
258
|
-
runs: state.runs,
|
|
259
|
-
registry,
|
|
260
|
-
// onRunDone 是全部 done 路径的单点汇聚(abortRun + error-recovery),顺序固化为
|
|
261
|
-
// notify → track → evict:notifyDone 先发完整聚合通知(淘汰后聚合根仍在闭包参数
|
|
262
|
-
// run 引用上不受影响),trackNotifiedRunId 有界化去重窗口,最后裁剪 done run 内存。
|
|
263
|
-
// 本轮 run 的 completedAt 在 transition("done") 时同步设为当前时刻=全局最新,
|
|
264
|
-
// 恒在保留端——结构性保证其不被自身触发的裁剪淘汰,无需 protectRunId。
|
|
265
|
-
onRunDone: (run: WorkflowRun) => {
|
|
266
|
-
notifyDone(pi, run.runId, run, notifiedRunIds, toGuiCtx(sessionCtx));
|
|
267
|
-
trackNotifiedRunId(notifiedRunIds, run.runId);
|
|
268
|
-
const evicted = evictDoneRunsBeyondCap(state.runs, MAX_RETAINED_DONE_RUNS);
|
|
269
|
-
if (evicted > 0) {
|
|
270
|
-
logger.debug("[subagent-workflow] evicted done runs beyond cap", {
|
|
271
|
-
evicted,
|
|
272
|
-
keep: MAX_RETAINED_DONE_RUNS,
|
|
273
|
-
sessionId: lsRef.lastSessionId,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
eventBus: pi.events,
|
|
278
|
-
scheduleTimeBudget: (runId: string, budgetTimeMs: number) =>
|
|
279
|
-
scheduleTimeBudget(runId, deps, budgetTimeMs),
|
|
280
|
-
onWorkflowCall: (name: string, args: Record<string, unknown>, parentRun: WorkflowRun) =>
|
|
281
|
-
executeNestedWorkflow(name, args, parentRun, deps),
|
|
282
|
-
// [H2 W3] workflow agent() 统一派发入口(设计 §3.5):pump 侧 dispatchAgentCall
|
|
283
|
-
// 经此转调 SubagentService.executeWorkflowAgent——真实 record(origin:"workflow"
|
|
284
|
-
// + parentRunId)进 store、共享池/守护/journal 归 service 编排;parentRunId 由
|
|
285
|
-
// pump 补 run.runId。service 单例在 session_start 后必在(run 只能于 session 内
|
|
286
|
-
// 派发);null 时抛错由 pump 的 dispatchCall catch 兜底回发 failed result。
|
|
287
|
-
workflowAgentDispatch: (opts, parentRunId, signal) => {
|
|
288
|
-
const service = getSubagentService();
|
|
289
|
-
if (!service) {
|
|
290
|
-
throw new Error("workflow agent dispatch unavailable: subagent service not initialized");
|
|
291
|
-
}
|
|
292
|
-
return service.executeWorkflowAgent(opts, parentRunId, signal);
|
|
293
|
-
},
|
|
294
|
-
log,
|
|
295
|
-
};
|
|
296
|
-
return deps;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function isScriptRunning(name: string): boolean {
|
|
300
|
-
for (const state of sessionState.values()) {
|
|
301
|
-
for (const run of state.runs.values()) {
|
|
302
|
-
if (run.spec.scriptName === name && run.state.status === "running") return true;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
183
|
// ════════════════════════════════════════════════════════════
|
|
309
|
-
//
|
|
184
|
+
// workflow 域:per-factory 状态 + 7 个 session/model 事件 + deps 装配
|
|
310
185
|
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
// ════════════════════════════════════════════════════════════
|
|
316
|
-
pi.on("session_start", async (_event: SessionStartEvent, ctx: ExtensionContext) => {
|
|
317
|
-
lsRef.lastSessionId = ctx.sessionManager.getSessionId();
|
|
318
|
-
// [u7a D5] 初始上报(count=当下绝对计数):触发时点 = extension 加载完成 / session
|
|
319
|
-
// 就绪(factory 无 ctx/ui,session_start 是最早带 ctx 的钩子——plugin-bridge 同款
|
|
320
|
-
// 事实)。fire-and-forget 在 await 装配链之前发起,不阻塞也不被阻塞。
|
|
321
|
-
inflightReporter.attachSession(ctx);
|
|
322
|
-
const result = await setupSessionLifecycle(pi, ctx, makeLifecycleDeps());
|
|
323
|
-
sessionState.set(result.sessionId, result);
|
|
324
|
-
});
|
|
325
|
-
|
|
186
|
+
// 状态创建(lsRef / notifiedRunIds / workerHost / registry / sessionState)、log、
|
|
187
|
+
// makeLifecycleDeps / makeDeps、7 个 pi.on handler、getWorkflowDeps 守卫 +
|
|
188
|
+
// lazyDeps 已收编 workflow-events.ts(事件族 seam,与 session-lifecycle.ts 同构;
|
|
189
|
+
// 7 个 handler 的注册相对顺序原样保留)。此处仅接线。
|
|
326
190
|
// ════════════════════════════════════════════════════════════
|
|
327
|
-
|
|
328
|
-
// 验证——检测 ledger/ack entry 被 compaction 清除时按内存态补写(notify-ledger
|
|
329
|
-
// compactionCheck;未清除则 no-op)。内存态在 compaction 后仍活着,作为补写源;
|
|
330
|
-
// 重启后的权威仍是两列 entry 差集(内存不承担销账职责)。
|
|
331
|
-
// ═══════════════════════════════════════════════════════
|
|
332
|
-
pi.on("session_compact", (_event: SessionCompactEvent, _ctx: ExtensionContext) => {
|
|
333
|
-
try {
|
|
334
|
-
const rewritten = getBoundNotifyLedger()?.compactionCheck() ?? 0;
|
|
335
|
-
if (rewritten > 0) {
|
|
336
|
-
logger.warn(`[subagents] notify ledger entries lost to compaction; rewrote ${rewritten} from memory`);
|
|
337
|
-
}
|
|
338
|
-
} catch (err) {
|
|
339
|
-
logger.warn("[subagents] notify ledger compactionCheck failed", {
|
|
340
|
-
reason: toErrorMessage(err),
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
});
|
|
191
|
+
const workflow = setupWorkflowDomain(pi, { inflightReporter });
|
|
344
192
|
|
|
345
193
|
// ════════════════════════════════════════════════════════════
|
|
346
194
|
// [U7 + engine-awareness U3] before_agent_start:引擎感知注入(链尾注册,D7——
|
|
@@ -350,148 +198,19 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
|
|
|
350
198
|
// sessionState 存取器注入。编排/渲染/链尾依据的完整注释随迁至该函数。
|
|
351
199
|
// ════════════════════════════════════════════════════════════
|
|
352
200
|
setupEngineAwarenessInjector(pi, {
|
|
353
|
-
getLastEngine: (sid) => sessionState.get(sid)?.lastEngine,
|
|
201
|
+
getLastEngine: (sid) => workflow.state.sessionState.get(sid)?.lastEngine,
|
|
354
202
|
setLastEngine: (sid, engine) => {
|
|
355
|
-
const state = sessionState.get(sid);
|
|
203
|
+
const state = workflow.state.sessionState.get(sid);
|
|
356
204
|
if (state) state.lastEngine = engine;
|
|
357
205
|
},
|
|
358
206
|
});
|
|
359
207
|
|
|
360
|
-
// ════════════════════════════════════════════════════════════
|
|
361
|
-
// model_select:用户切换 model 时刷新缓存
|
|
362
|
-
// ════════════════════════════════════════════════════════════
|
|
363
|
-
pi.on("model_select", (event, ctx: ExtensionContext) => {
|
|
364
|
-
const service = getModelConfigService();
|
|
365
|
-
if (service && typeof service.setCtxModel === "function") {
|
|
366
|
-
service.setCtxModel(event.model);
|
|
367
|
-
}
|
|
368
|
-
// H1: 同步刷新所有 session 的 SAR ctxModel(旧实现只在 session_start 固化)
|
|
369
|
-
const sid = ctx.sessionManager.getSessionId();
|
|
370
|
-
const state = sessionState.get(sid);
|
|
371
|
-
if (state) {
|
|
372
|
-
state.runner.updateCtxModel(event.model);
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
// ════════════════════════════════════════════════════════════
|
|
377
|
-
// session_tree:切分支前终止所有 running run(一次性生命周期——切走即作废)
|
|
378
|
-
// ════════════════════════════════════════════════════════════
|
|
379
|
-
pi.on("session_tree", async (_event: SessionTreeEvent, ctx: ExtensionContext) => {
|
|
380
|
-
const sessionId = ctx.sessionManager.getSessionId();
|
|
381
|
-
lsRef.lastSessionId = sessionId;
|
|
382
|
-
|
|
383
|
-
const state = sessionState.get(sessionId);
|
|
384
|
-
if (state) {
|
|
385
|
-
// 一次性生命周期(D-2):running run 转 done,failed 落盘(helper 内部自过滤
|
|
386
|
-
// running,单 run 失败不中断其余)。此处不再挂起待恢复。
|
|
387
|
-
try {
|
|
388
|
-
await terminateRunningRuns(makeDeps(state, ctx), "Session switched: run terminated");
|
|
389
|
-
} catch (err) {
|
|
390
|
-
bestEffort(err, "terminateRunningRuns (session_tree handler)");
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
// ════════════════════════════════════════════════════════════
|
|
396
|
-
// SP-4: session_before_fork(/fork)/ session_before_switch(/new)级联关闭
|
|
397
|
-
// ════════════════════════════════════════════════════════════
|
|
398
|
-
// 主 session /fork 或 /new 时,清理旧 record(disposeAllRecords:CAS 转终态 +
|
|
399
|
-
// archive + worktree 清理)。before 事件在 session 替换前触发,确保旧 session 的
|
|
400
|
-
// subagent 在新 session 创建前被清理(随后的 session_shutdown → dispose 收割子进程)。
|
|
401
|
-
//
|
|
402
|
-
// [M2 修复] 旧实现把 /new 级联挂在 session_before_tree 上——SDK 中该事件只由
|
|
403
|
-
// AgentSession.navigateTree()(/tree 同 session 分支切换)触发,/new 走
|
|
404
|
-
// session_before_switch(reason:"new") + session_shutdown(reason:"new"),从不触发
|
|
405
|
-
// before_tree。后果双向:/new 级联是死代码;普通 /tree 分支导航反而误杀全部活跃
|
|
406
|
-
// subagent。现 /new 改挂 session_before_switch(reason==="new"),before_tree handler
|
|
407
|
-
// 移除(/tree 是同 session 内导航,record/子进程归属不变,无级联关闭诉求)。
|
|
408
|
-
pi.on("session_before_fork", (_event, _ctx) => {
|
|
409
|
-
const service = getSubagentService();
|
|
410
|
-
if (service) {
|
|
411
|
-
const count = service.onParentFork();
|
|
412
|
-
if (count > 0) {
|
|
413
|
-
logger.warn(`[subagents] /fork 级联关闭 ${count} 个 subagent`);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
pi.on("session_before_switch", (event, _ctx) => {
|
|
419
|
-
// /new(reason:"new")创建全新 session → 级联关闭旧 record。
|
|
420
|
-
// reason:"resume"(/resume /import 回到已有 session)不级联:record 按 rootSessionId
|
|
421
|
-
// 归属隔离,跨 session 读写由 store 过滤守卫,无需销毁。
|
|
422
|
-
if (event.reason !== "new") return;
|
|
423
|
-
const service = getSubagentService();
|
|
424
|
-
if (service) {
|
|
425
|
-
const count = service.onParentNew();
|
|
426
|
-
if (count > 0) {
|
|
427
|
-
logger.warn(`[subagents] /new 级联关闭 ${count} 个 subagent`);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
// ════════════════════════════════════════════════════════════
|
|
433
|
-
// session_shutdown:dispose subagents + terminate workflows + store 收尾 + cleanup
|
|
434
|
-
//
|
|
435
|
-
// store 收尾:每 session 的 JsonlRunStore 在 terminateRunningRuns 之后 dispose(刷
|
|
436
|
-
// pending 去抖批 + await in-flight 链,见 W2C5)。R3 声明:SIGTERM/SIGINT 走下方
|
|
437
|
-
// process handler 不触发本路径,pending 去抖丢失等价崩溃链(重启后 kill-9 恢复
|
|
438
|
-
// 收编 running 残留——终态/创建均冷路径已落盘,丢的只有 ≤saveDebounceMs 的
|
|
439
|
-
// running 尾巴,ES1 已接受);不做 best-effort SIGTERM dispose(需同步 IO 改造,
|
|
440
|
-
// 超出 wave 边界)。
|
|
441
|
-
// ════════════════════════════════════════════════════════════
|
|
442
|
-
pi.on("session_shutdown", async (_event: SessionShutdownEvent, _ctx: ExtensionContext) => {
|
|
443
|
-
// ── subagents 域:dispose SubagentService ──
|
|
444
|
-
getSubagentService()?.dispose();
|
|
445
|
-
|
|
446
|
-
// [u7a D5] 在途上报通道随 session 终结:摘 ctx + 停重试(session 已死,重试直至
|
|
447
|
-
// 成功的语义只对活 session 成立;进程级出口监听保留——后续 /new 重新 attach)。
|
|
448
|
-
inflightReporter.detachSession();
|
|
449
|
-
|
|
450
|
-
// ── workflow 域:terminate 所有 running run + store 收尾 + 清理 temp files ──
|
|
451
|
-
// H-5: 遍历所有 sessionState 条目清理(而不只 lastSessionId——
|
|
452
|
-
// 防御 session 切换但 session_tree 未先触发导致 lastSessionId 指向已删除 session 的情况)。
|
|
453
|
-
for (const [sessionId, state] of sessionState) {
|
|
454
|
-
// 编排顺序(W2C5):terminate(await,failed 落盘——重启后 kill-9 恢复不误判)
|
|
455
|
-
// → store.dispose(await,刷 pending 去抖批 + await in-flight 链,关「shutdown
|
|
456
|
-
// 时刻 pending 去抖写丢失」窗口)→ delete。terminate 的 running 过滤在 helper
|
|
457
|
-
// 内部(单 run 失败不中断其余);外层 try/catch 兜底防单 session 异常中断后续
|
|
458
|
-
// session 条目的 dispose + delete(对齐原 allSettled 的不中断语义)。
|
|
459
|
-
try {
|
|
460
|
-
await terminateRunningRuns(makeDeps(state, _ctx), "Session shutdown: run terminated");
|
|
461
|
-
} catch (err) {
|
|
462
|
-
bestEffort(err, "terminateRunningRuns (session_shutdown handler)");
|
|
463
|
-
}
|
|
464
|
-
// dispose 自身恒 resolve,catch 兜底防御——handler 内抛错会中断后续 session
|
|
465
|
-
// 条目清理。不留静默吞错(错误必须可操作):debug 留痕带 sessionId/sessionDir,
|
|
466
|
-
// 排查「shutdown 后 run 状态不落盘」类问题时有迹可循。
|
|
467
|
-
await state.store.dispose().catch((err: unknown) => {
|
|
468
|
-
logger.debug(
|
|
469
|
-
`[subagent-workflow] session_shutdown store.dispose failed (sessionId=${sessionId}, sessionDir=${state.sessionDir})`,
|
|
470
|
-
{ reason: toErrorMessage(err) },
|
|
471
|
-
);
|
|
472
|
-
});
|
|
473
|
-
sessionState.delete(sessionId);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// M2: 清理 dialog queue 运行时状态(queue/current/processing)。
|
|
477
|
-
// [#10] rejectAll() settle 所有 pending dialog Promise(防闭包泄漏:未 settle 的
|
|
478
|
-
// Promise 持有 resolve/reject 闭包及 handler 上下文,session 退出后仍挂在全球队列上),
|
|
479
|
-
// 并内部重置 queue/current/processing(原子操作,无 footgun)。
|
|
480
|
-
// 单 session 假设(M-2,同 lastSessionId):rejectAll() 清空进程级单例的所有 pending,
|
|
481
|
-
// 依赖 Pi 单进程单 session 串行保证——不会误清其他 session。多 session 并发的迁移策略
|
|
482
|
-
// 见 DialogGlobalQueue 类注释(rejectAllForSession)。
|
|
483
|
-
// channel registry 不清:跨 session 持久是有意设计(ask-user 扩展注册的 channel handler
|
|
484
|
-
// 在 /new /resume /fork 时不丢失注册)。
|
|
485
|
-
const dialogQueue = getOrCreateDialogQueue();
|
|
486
|
-
dialogQueue.rejectAll();
|
|
487
|
-
});
|
|
488
|
-
|
|
489
208
|
// ════════════════════════════════════════════════════════════
|
|
490
209
|
// [V2 决策 7 防线 i] process 级 shutdown hook(显式收割三道防线之一)
|
|
491
210
|
//
|
|
492
|
-
// 上方 session_shutdown(pi async hook)在进程被 SIGTERM/
|
|
493
|
-
//
|
|
494
|
-
// SIGTERM 全部 activation)。
|
|
211
|
+
// 上方 session_shutdown(pi async hook,workflow-events.ts)在进程被 SIGTERM/
|
|
212
|
+
// SIGINT 强杀或崩溃时不触发,此处 process.on 兜底确保 sync 子进程被收割
|
|
213
|
+
// (防线 i:shutdown 时显式 SIGTERM 全部 activation)。
|
|
495
214
|
//
|
|
496
215
|
// - SIGTERM:pi 各 mode(rpc/interactive/print)自带 SIGTERM handler 负责退出编排,
|
|
497
216
|
// 本 extension 的 handler 只做收割 + 设 exitCode(不 re-raise、不抢 pi 的退出语义;
|
|
@@ -507,7 +226,7 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
|
|
|
507
226
|
// (历史接线点随协议化重构消失,仅余自持单测)。当前的双写者防护由
|
|
508
227
|
// subagent-service 的 resumesInFlight 集合守卫承担。
|
|
509
228
|
// 防线 ii(启动孤儿扫描):未接线,骨架已随 L2 死代码清扫删除(当前 piped stdio
|
|
510
|
-
// 下 stdin-EOF
|
|
229
|
+
// 下 stdin-EOF 自灭链覆盖崩溃路径)。
|
|
511
230
|
// ════════════════════════════════════════════════════════════
|
|
512
231
|
process.on("SIGTERM", () => {
|
|
513
232
|
reapSpawnedChildrenOnShutdown();
|
|
@@ -527,32 +246,6 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
|
|
|
527
246
|
process.on("SIGINT", sigintHandler);
|
|
528
247
|
process.on("beforeExit", reapSpawnedChildrenOnShutdown);
|
|
529
248
|
|
|
530
|
-
// ════════════════════════════════════════════════════════════
|
|
531
|
-
// workflow deps 守卫(单一出口)
|
|
532
|
-
//
|
|
533
|
-
// [守卫合一] 原 pi.__workflowRun 内联守卫 + getDeps 守卫两份重复(state 缺失 /
|
|
534
|
-
// storeHealthy=false),合并为单一 getWorkflowDeps:返回 discriminated union,
|
|
535
|
-
// 两个消费点各自决定失败形态——pi.__workflowRun(D-8 API)返回错误对象(不
|
|
536
|
-
// throw,保住调用方 Promise 契约),getDeps(3 个 tool 的 lazy deps 源)throw
|
|
537
|
-
// (pi tool 框架将其转译为 tool 错误结果)。错误消息逐字保留(crash-recovery
|
|
538
|
-
// 测试锁 "store unavailable" / "loadAll failed" 子串)。
|
|
539
|
-
// ════════════════════════════════════════════════════════════
|
|
540
|
-
type WorkflowDepsResolution =
|
|
541
|
-
| { ok: true; deps: LauncherDeps }
|
|
542
|
-
| { ok: false; reason: string };
|
|
543
|
-
|
|
544
|
-
const getWorkflowDeps = (sessionId: string): WorkflowDepsResolution => {
|
|
545
|
-
const state = sessionState.get(sessionId);
|
|
546
|
-
if (!state) {
|
|
547
|
-
return { ok: false, reason: "Session not initialized" };
|
|
548
|
-
}
|
|
549
|
-
// MF-1: store 不健康时 fail-fast,避免 store.save 再次失败导致 run 状态不落地。
|
|
550
|
-
if (!state.storeHealthy) {
|
|
551
|
-
return { ok: false, reason: "Workflow store unavailable (loadAll failed in session_start)" };
|
|
552
|
-
}
|
|
553
|
-
return { ok: true, deps: makeDeps(state, state.ctx) };
|
|
554
|
-
};
|
|
555
|
-
|
|
556
249
|
// ════════════════════════════════════════════════════════════
|
|
557
250
|
// pi.__workflowRun(D-8 签名)
|
|
558
251
|
// ════════════════════════════════════════════════════════════
|
|
@@ -565,7 +258,7 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
|
|
|
565
258
|
// 注意:lastSessionId 是单值假设——Pi 当前保证单 session 串行(一次只一个活跃 session)。
|
|
566
259
|
// 若未来 Pi 支持多 session 并发,此处需改为从 ctx.sessionManager.getSessionId() 显式传入。
|
|
567
260
|
// M-2 已记录此假设。
|
|
568
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
261
|
+
const resolved = workflow.getWorkflowDeps(workflow.state.lsRef.lastSessionId);
|
|
569
262
|
if (!resolved.ok) {
|
|
570
263
|
return {
|
|
571
264
|
status: "done",
|
|
@@ -584,82 +277,25 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
|
|
|
584
277
|
};
|
|
585
278
|
|
|
586
279
|
// ════════════════════════════════════════════════════════════
|
|
587
|
-
// Tools(3
|
|
280
|
+
// Tools(3 个)+ Commands(2 个)—— 注册面
|
|
588
281
|
//
|
|
589
|
-
// lazyDeps
|
|
590
|
-
//
|
|
591
|
-
//
|
|
282
|
+
// lazyDeps / isScriptRunning / registry 由 workflow-events.ts 装配结果提供。
|
|
283
|
+
// guard:workflow tool 与 subagents(批量派发入口)共用同一 guard——两者是同一条
|
|
284
|
+
// runWorkflow 管道的入口,单守卫防双 guard 语义漂移(D3 复用裁决)。
|
|
592
285
|
// ════════════════════════════════════════════════════════════
|
|
593
|
-
const
|
|
594
|
-
get store() {
|
|
595
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
596
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
597
|
-
return resolved.deps.store;
|
|
598
|
-
},
|
|
599
|
-
get runs() {
|
|
600
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
601
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
602
|
-
return resolved.deps.runs;
|
|
603
|
-
},
|
|
604
|
-
get registry() {
|
|
605
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
606
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
607
|
-
return resolved.deps.registry;
|
|
608
|
-
},
|
|
609
|
-
get onRunDone() {
|
|
610
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
611
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
612
|
-
return resolved.deps.onRunDone;
|
|
613
|
-
},
|
|
614
|
-
get eventBus() {
|
|
615
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
616
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
617
|
-
return resolved.deps.eventBus;
|
|
618
|
-
},
|
|
619
|
-
get workerHost() {
|
|
620
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
621
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
622
|
-
return resolved.deps.workerHost;
|
|
623
|
-
},
|
|
624
|
-
get runner() {
|
|
625
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
626
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
627
|
-
return resolved.deps.runner;
|
|
628
|
-
},
|
|
629
|
-
// scheduleTimeBudget / onWorkflowCall 不可缺席(ports.ts D-12 regression fix):
|
|
630
|
-
// rebuildRuntime 重排 run 级墙钟预算计时器、worker 脚本嵌套 workflow() 调用都经
|
|
631
|
-
// 这两个成员消费——lazyDeps 缺席会让消费点拿到 undefined(可选属性静默放行),
|
|
632
|
-
// 带时间预算的 run 命中一次错误重试后计时器静默失效。转发形态与其余成员一致。
|
|
633
|
-
get scheduleTimeBudget() {
|
|
634
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
635
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
636
|
-
return resolved.deps.scheduleTimeBudget;
|
|
637
|
-
},
|
|
638
|
-
get onWorkflowCall() {
|
|
639
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
640
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
641
|
-
return resolved.deps.onWorkflowCall;
|
|
642
|
-
},
|
|
643
|
-
get log() {
|
|
644
|
-
const resolved = getWorkflowDeps(lsRef.lastSessionId);
|
|
645
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
646
|
-
return resolved.deps.log;
|
|
647
|
-
},
|
|
648
|
-
};
|
|
286
|
+
const guard = { isProcessing: false };
|
|
649
287
|
|
|
650
|
-
registerWorkflowTool(pi, lazyDeps, guard);
|
|
651
|
-
|
|
288
|
+
registerWorkflowTool(pi, workflow.lazyDeps, guard);
|
|
289
|
+
registerSubagentsTool(pi, workflow.lazyDeps, guard);
|
|
290
|
+
registerWorkflowScriptTool(pi, workflow.state.registry, workflow.isScriptRunning);
|
|
652
291
|
|
|
653
|
-
// ════════════════════════════════════════════════════════════
|
|
654
|
-
// Commands(2 个)
|
|
655
|
-
// ════════════════════════════════════════════════════════════
|
|
656
292
|
registerWorkflowsCommand(
|
|
657
293
|
pi,
|
|
658
294
|
() => {
|
|
659
|
-
const state = sessionState.get(lsRef.lastSessionId);
|
|
295
|
+
const state = workflow.state.sessionState.get(workflow.state.lsRef.lastSessionId);
|
|
660
296
|
return state?.runs ?? new Map();
|
|
661
297
|
},
|
|
662
|
-
lazyDeps,
|
|
298
|
+
workflow.lazyDeps,
|
|
663
299
|
);
|
|
664
300
|
}
|
|
665
301
|
|
|
@@ -669,7 +305,7 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
|
|
|
669
305
|
|
|
670
306
|
// channel registry 经 channel-registry-access.ts 公开访问(跨扩展 API)。
|
|
671
307
|
// dialog queue 单例随 session_start 装配域迁居 session-lifecycle.ts
|
|
672
|
-
// (getOrCreateDialogQueue;消费方 = 该文件 createOrReuseServices +
|
|
308
|
+
// (getOrCreateDialogQueue;消费方 = 该文件 createOrReuseServices + workflow-events.ts
|
|
673
309
|
// session_shutdown),Symbol key 单定义点随迁,避免双份定义漂移。
|
|
674
310
|
|
|
675
311
|
// 跨扩展 channel handler 注册入口已收口到 core 深路径
|