@zhushanwen/pi-structured-output 6.0.0 → 6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-structured-output",
3
- "version": "6.0.0",
3
+ "version": "6.1.1",
4
4
  "description": "Structured output tool for Pi — workflow schema enforcement via pi's parameter layer; interactive mode validates self-reported schemas with Ajv",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -25,11 +25,11 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "ajv": "^8.17.0",
28
- "@zhushanwen/pi-ext-guards": "0.4.2"
28
+ "@zhushanwen/pi-ext-guards": "0.4.4"
29
29
  },
30
30
  "peerDependencies": {
31
- "@earendil-works/pi-coding-agent": "^0.84.4",
32
- "typebox": "*"
31
+ "@earendil-works/pi-coding-agent": "^1.0.0",
32
+ "typebox": "1.3.27"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^24.0.0",
package/src/index.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  * turn_end 时检查模型是否调用了 structured-output 工具,没调/失败则 steer 注入
12
12
  * 重试提醒(软闸门,最多重试 2 次)。同一状态机(WorkflowGate)上的硬杀闸门在
13
13
  * 同签名校验失败达 3 次时 terminal:写日志(stderr + session JSONL 双通道,含恢复
14
- * 指引)后 ctx.abort() + ctx.shutdown() 优雅终止子进程 + 15s 兜底硬退 timer——
14
+ * 指引)后 ctx.abort() + ctx.shutdown() 优雅终止子进程(无兜底硬退 timer,ADR-0122)——
15
15
  * terminal 后软闸门不再 steer(同一份 terminal 事实,无跨对象接线;G2 与模型
16
16
  * 配合度无关)。装配与 listener 明细见 workflow-hook.setupWorkflowHook。
17
17
  *
package/src/loop-gate.ts CHANGED
@@ -34,9 +34,9 @@
34
34
  * - 连续同签名失败达 MAX_CONSECUTIVE_FAILURES(3)→ terminal 态:
35
35
  * 写日志(stderr + appendEntry 双通道,含 §5.2 形态 b 恢复指引)后
36
36
  * ctx.abort()(停当前 turn,截断 token 燃烧窗口)+ ctx.shutdown() 优雅终止
37
- * 子进程(RPC mode 在 agent_settled 后 exit),并武装 15s 兜底硬退 timer
38
- * (R3 F-2 bounded teardown,覆盖 pi 挂死不 settle 的异常态;副作用序列见
39
- * runTerminalTeardown,装配层在 newlyTerminal 时调用)。
37
+ * 子进程(RPC mode 在 agent_settled 后 exit)——副作用序列见 runTerminalTeardown,
38
+ * 装配层在 newlyTerminal 时调用。无兜底硬退 timer(ADR-0122 信任边界内不设防:
39
+ * pi 挂死不 settle 的处置 = 父进程既有失败路径 + 用户重启应用,不建墙钟兜底)。
40
40
  * - 成功调用清零(模型走通即无循环)。
41
41
  *
42
42
  * steer 侧转移(onToolExecEnd 的记账段 + onTurnEnd,转移表锁定于 retry-state.test.ts):
@@ -460,70 +460,6 @@ export class WorkflowGate {
460
460
  /** terminal 日志的 appendEntry customType(session.jsonl 持久化,不进 LLM 上下文)。 */
461
461
  export const GATE_ENTRY_TYPE = "structured-output:gate";
462
462
 
463
- /** 每秒毫秒数(teardown 日志 ms→s 换算用,no-magic-numbers)。 */
464
- const MS_PER_SECOND = 1000;
465
-
466
- /**
467
- * terminal 后 bounded teardown 的兜底硬退窗口(ms)。
468
- *
469
- * 任务原案 10s(SIGTERM 前优雅窗口)+ 5s(SIGKILL 前余量)的信号分级在扩展上下文
470
- * 不可达(pi 0.84.1 ExtensionContext 无子进程句柄/信号能力,见 armForceExitTeardown
471
- * 注释的核实记录),合并为单步 process.exit 硬退窗口。
472
- *
473
- * 数据完整性权衡(照任务要求写明):session flush 在 shutdown 请求时已尽力——RPC
474
- * mode 于 agent_settled 后 exit,session entry 逐条 append 落盘(writeTerminatedLog
475
- * 的 entry 在 terminal 当下已写入);15s 优雅窗口远超正常 flush 需求(abort+shutdown
476
- * 后正常退出秒级完成),兜底只覆盖「pi 挂死不 settle」的异常态——此时宁可硬退
477
- *(父进程 SW 侧走「子进程结束未产出 structured-output」失败路径,stderr 已留原因)
478
- * 也不无限烧 token。
479
- *
480
- * 安全域:15_000 为字面量常量,处于 Node setTimeout 安全域内(非有限/超 2^31-1
481
- * 会塌缩为 1ms 立即触发——若未来 delay 来源动态化,需引入安全域校验,参照
482
- * packages/subagent-core/src/shared/timer-delay.ts 的 assertSafeTimerDelay)。
483
- */
484
- export const TEARDOWN_FORCE_EXIT_MS = 15_000;
485
-
486
- /**
487
- * 兜底硬退 exit code:非零且 < 128(SW 侧 session-runner 的 SIGNAL_EXIT_CODE_THRESHOLD)
488
- * → 父进程按「子进程自身报错」记录(stderr 缓冲已先行写明原因),不会与信号终止混淆。
489
- */
490
- const TEARDOWN_EXIT_CODE = 1;
491
-
492
- // one-shot timer 不属 C-ext-06 §7.5 的「跨 session 存活进程级单例」范畴:
493
- // terminal 一次性武装、随 process.exit 消亡;jiti 双实例下双 timer 各自
494
- // process.exit 进程级幂等,模块级 let 即可。
495
- let teardownTimer: ReturnType<typeof setTimeout> | undefined;
496
-
497
- /**
498
- * 武装 terminal 后的 bounded teardown 兜底:TEARDOWN_FORCE_EXIT_MS 后进程仍未退出
499
- *(pi 挂死未 settle)则 process.exit 硬退。
500
- *
501
- * 方案依据(pi 0.84.1 实装核实,node_modules/@earendil-works/pi-coding-agent
502
- * dist/core/extensions/types.d.ts + dist/core/extensions/runner.js):
503
- * ExtensionContext 对外仅 shutdown()(优雅:置 shutdownRequested,agent_settled 后
504
- * exit)与 abort()(中止当前 agent 操作),无子进程句柄/信号 API;扩展与 pi 子进程
505
- * 同进程(loader 进程内加载),「向子进程发 SIGTERM/SIGKILL」在语义上不成立——扩展
506
- * 能做的最大硬杀就是对自身 process.exit。故采用任务预设的兜底形态:abort+shutdown
507
- * 优雅退出为主,定时 process.exit 兜底。
508
- *
509
- * timer 卫生:delay 为字面量常量、处 setTimeout 安全域(见 TEARDOWN_FORCE_EXIT_MS 注释)+
510
- * unref(不阻止 pi 在窗口内自然退出;自然退出时本 timer 随进程消亡不再开火)+
511
- * terminal 路径幂等 clearTimeout(重复武装不叠加多个兜底 timer)。
512
- */
513
- export function armForceExitTeardown(): void {
514
- if (teardownTimer !== undefined) clearTimeout(teardownTimer);
515
- const timer = setTimeout(() => {
516
- process.stderr.write(
517
- `[structured-output gate] graceful shutdown did not complete within ${TEARDOWN_FORCE_EXIT_MS / MS_PER_SECOND}s; `
518
- + "force-exiting (session flush was best-effort at shutdown request).\n",
519
- );
520
- process.exit(TEARDOWN_EXIT_CODE);
521
- }, TEARDOWN_FORCE_EXIT_MS);
522
- // unref:窗口内 pi 自然退出时不被本 timer 拖住(timer 随进程消亡,不再开火)
523
- timer.unref();
524
- teardownTimer = timer;
525
- }
526
-
527
463
  /**
528
464
  * terminal 态日志(§5.2 形态 b):
529
465
  * - stderr:子进程 stderr 直出(taiji runtime 的 pi-*.jsonl tee / 本地探针可见)
@@ -561,21 +497,20 @@ function writeTerminatedLog(pi: PiAPI, gate: WorkflowGate): void {
561
497
  * terminal 触发时的副作用链(装配层在 gate.onToolExecEnd 返回 newlyTerminal 时调用,
562
498
  * 见 workflow-hook.setupWorkflowHook)。
563
499
  *
564
- * 时序(R3 F-2 bounded teardown):写日志(stderr + appendEntry 双通道,R3 F-3)→
565
- * ctx.abort()(中止当前 agent 操作——截断「shutdown 请求后当前 turn 的 bash/read/
566
- * 流式继续跑、模型继续烧 token」的窗口,~25s 实测窗口在 abort 后即止)→
567
- * ctx.shutdown()(RPC mode 置 shutdownRequested,agent_settled 后进程 exit(0),
568
- * 父进程走「子进程结束但未产出 structured-output」的既有失败路径)→
569
- * armForceExitTeardown()(15s 兜底硬退,覆盖 pi 挂死不 settle 的异常态;方案依据
570
- * 见该函数注释)。
500
+ * 时序:写日志(stderr + appendEntry 双通道,R3 F-3)→ ctx.abort()(中止当前
501
+ * agent 操作——截断「shutdown 请求后当前 turn 的 bash/read/流式继续跑、模型继续烧
502
+ * token」的窗口)→ ctx.shutdown()(RPC mode 置 shutdownRequested,agent_settled 后
503
+ * 进程 exit(0),父进程走「子进程结束但未产出 structured-output」的既有失败路径)。
504
+ * 无兜底硬退 timer(ADR-0122 信任边界内不设防):pi 挂死不 settle 时 shutdown 不返回
505
+ * 的处置 = 父进程既有失败路径 + 用户重启应用,不建墙钟兜底。
571
506
  */
572
507
  export function runTerminalTeardown(pi: PiAPI, gate: WorkflowGate, ctx: ExtensionContext): void {
573
508
  writeTerminatedLog(pi, gate);
574
509
  // stale ctx 防御(crash-resilience D1):abort/shutdown 均在 pi assertActive 面
575
510
  // (PS-30,runner.js createContext)——session 替换窗口触发 terminal 时无人接的
576
511
  // 同步 throw 会经 async handler 变 rejected Promise 杀 pi 进程。stale 静默跳过
577
- // 优雅退出(此时进程的存在意义已随 session 替换消失),armForceExitTeardown 的
578
- // 15s 硬退兜底保持武装——自清理语义不丢。非 stale 错误原样上抛(守卫不吞真实 bug)。
512
+ // 优雅退出(此时进程的存在意义已随 session 替换消失)。非 stale 错误原样上抛
513
+ //(守卫不吞真实 bug)。
579
514
  guardStaleCtx(() => {
580
515
  ctx.abort();
581
516
  ctx.shutdown();
@@ -584,9 +519,8 @@ export function runTerminalTeardown(pi: PiAPI, gate: WorkflowGate, ctx: Extensio
584
519
  onStale: (error) => {
585
520
  process.stderr.write(
586
521
  `[structured-output gate] terminal teardown skipped (stale ctx, session replaced): `
587
- + `${toErrorMessage(error)}; force-exit timer stays armed.\n`,
522
+ + `${toErrorMessage(error)}\n`,
588
523
  );
589
524
  },
590
525
  });
591
- armForceExitTeardown();
592
526
  }
@@ -4,7 +4,7 @@
4
4
  * 装配形态(workflow 模式,见 src/index.ts):
5
5
  * - tool_execution_end:唯一 listener——喂 loop-gate 的 WorkflowGate(合一状态机:
6
6
  * steer 记账段 + 硬杀计数段,先记账后计数),newlyTerminal 时触发硬杀副作用链
7
- * (runTerminalTeardown:日志 → abort+shutdown → 15s 兜底硬退)。
7
+ * (runTerminalTeardown:日志 → abort+shutdown 优雅终止)。
8
8
  * - turn_end:唯一 listener——模型未成功产出 structured-output 时以 steer 方式注入
9
9
  * 重试提醒(软闸门),提示词不伪装用户气泡,对话流用户内容 100% 来自用户输入;
10
10
  * 最多重试 MAX_HOOK_RETRIES 次,防止无限循环。
@@ -206,7 +206,7 @@ export function setupWorkflowHook(pi: PiAPI, schemaJson: string): void {
206
206
  // 审查项#8:await 发送结果——发送失败(如 compaction 中开轮抛错 / 扩展已
207
207
  // 被 assertActive 拒绝)不扣减重试预算(不调 onTurnEnd),否则 fire-and-forget
208
208
  // 丢一份 steer + 白扣一次预算,两次即永久哑火。
209
- // pi 0.84.4 实装(loader.js):extension 侧 sendMessage 同步转发且吞掉异步
209
+ // pi 实装(loader.js,1.0.0 复核同构):extension 侧 sendMessage 同步转发且吞掉异步
210
210
  // rejection(bindCore .catch(emitError) 转事件)返回 void——await 对 undefined
211
211
  // 立即解析;此处的 try/catch 兜住同步 throw(assertActive)与未来 pi 返回真
212
212
  // Promise 的形态。