@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 +4 -4
- package/src/index.ts +1 -1
- package/src/loop-gate.ts +12 -78
- package/src/workflow-hook.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-structured-output",
|
|
3
|
-
"version": "6.
|
|
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.
|
|
28
|
+
"@zhushanwen/pi-ext-guards": "0.4.4"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
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()
|
|
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
|
|
38
|
-
*
|
|
39
|
-
*
|
|
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
|
-
*
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
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
|
|
578
|
-
|
|
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)}
|
|
522
|
+
+ `${toErrorMessage(error)}\n`,
|
|
588
523
|
);
|
|
589
524
|
},
|
|
590
525
|
});
|
|
591
|
-
armForceExitTeardown();
|
|
592
526
|
}
|
package/src/workflow-hook.ts
CHANGED
|
@@ -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
|
|
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
|
|
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 的形态。
|