@tekmidian/pai 1.0.3 → 1.0.5

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.
Files changed (147) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/{auto-route-B7Yu6Uam.mjs → auto-route-DUYr57rs.mjs} +2 -2
  3. package/dist/auto-route-DWYK_0i0.mjs +11 -0
  4. package/dist/budget-DX-VIWGf.mjs +15 -0
  5. package/dist/{caller-identity-yLFb7CKq.mjs → caller-identity-Bim8JT-D.mjs} +3 -3
  6. package/dist/{capacity-DEveQdFV.mjs → capacity-CsZMkVyg.mjs} +7 -4
  7. package/dist/{chain-7n9Gx56r.mjs → chain-Dzz6xcKH.mjs} +5 -5
  8. package/dist/cli/index.mjs +114 -208
  9. package/dist/{cockpit-actions-BBHeyEZI.mjs → cockpit-actions-BTA1njMO.mjs} +31 -25
  10. package/dist/{cockpit-actions-DR9X514g.mjs → cockpit-actions-BgsiNcUY.mjs} +18 -18
  11. package/dist/cockpit-memory-DIrNs4xr.mjs +20 -0
  12. package/dist/config/defaults/config.yaml +9 -0
  13. package/dist/{config-XpZWOYOG.mjs → config-CST2uq1v.mjs} +29 -4
  14. package/dist/{config-Bu1_vbY8.mjs → config-DYbyEe4-.mjs} +2 -2
  15. package/dist/{config-DtENJ6cL.mjs → config-Dv3t7xnT.mjs} +1 -1
  16. package/dist/config-h5qK3fst.mjs +4 -0
  17. package/dist/{controls-CPoeudou.mjs → controls-OiVVbe-f.mjs} +18 -16
  18. package/dist/daemon/index.mjs +38 -37
  19. package/dist/{daemon-DPAo6WTa.mjs → daemon-C1dycZ3W.mjs} +35 -29
  20. package/dist/daemon-ieo0lnpq.mjs +46 -0
  21. package/dist/daemon-mcp/index.mjs +35 -35
  22. package/dist/detect-C0ZBXqDL.mjs +10 -0
  23. package/dist/{detect-9CZNO0yf.mjs → detect-CHuxFrLy.mjs} +2 -2
  24. package/dist/{embed-pass-DIWjA68P.mjs → embed-pass-DtVF2fOV.mjs} +4 -4
  25. package/dist/{embedding-gate-Cnk4YCyX.mjs → embedding-gate-B98PK_wz.mjs} +2 -2
  26. package/dist/{embedding-gate-6rZrWRXa.mjs → embedding-gate-CTAk8STe.mjs} +2 -2
  27. package/dist/{endreason-C577xIQz.mjs → endreason-BqyyU7BH.mjs} +20 -6
  28. package/dist/{factory-DjAAsqr_.mjs → factory-BV0MQA3T.mjs} +4 -4
  29. package/dist/{factory-Bsib-qDG.mjs → factory-DeASMV7Z.mjs} +4 -4
  30. package/dist/{fallback-DFlItw1Z.mjs → fallback-Bj2cyVk_.mjs} +230 -8
  31. package/dist/{health-Dnym-qcT.mjs → health-kufVIf6i.mjs} +4 -4
  32. package/dist/{helpers-BD6V-p9w.mjs → helpers-CvbYEKoD.mjs} +2 -2
  33. package/dist/{helpers-CMGjy_0N.mjs → helpers-WV8tdQHM.mjs} +5 -5
  34. package/dist/hooks/block-sleep-poll.mjs +1 -1
  35. package/dist/hooks/capture-all-events.mjs +21 -2
  36. package/dist/hooks/capture-tool-output.mjs +21 -2
  37. package/dist/hooks/load-project-context.mjs +7 -7
  38. package/dist/hooks/local-model-hint.mjs +1 -1
  39. package/dist/hooks/route-agents-to-worker.mjs +1 -1
  40. package/dist/hooks/security-validator.mjs +2 -2
  41. package/dist/hooks/worker-edit-repair.mjs +2 -2
  42. package/dist/hooks/worker-guard.mjs +8 -8
  43. package/dist/hooks/worker-note.mjs +2 -2
  44. package/dist/hooks/worker-pool-host.mjs +158 -55
  45. package/dist/hooks/worker-proxy.mjs +1 -1
  46. package/dist/hooks/worker-status-line.mjs +50 -22
  47. package/dist/hooks/worker-supervision.mjs +1 -1
  48. package/dist/hooks/worker-tool-name-repair.mjs +2 -2
  49. package/dist/index.mjs +8 -8
  50. package/dist/{indexer-backend-Cll7le4P.mjs → indexer-backend-B2vtFl38.mjs} +37 -234
  51. package/dist/{ipc-client-CaiGJnSM.mjs → ipc-client-CnYEF31R.mjs} +1 -1
  52. package/dist/{ipc-client-ZRmDKd5Z.mjs → ipc-client-DN7EvoVo.mjs} +1 -1
  53. package/dist/kill-tree-CXZ0WfVf.mjs +10 -0
  54. package/dist/{kill-tree-EWYaEk5v.mjs → kill-tree-CcpsRLCZ.mjs} +7 -7
  55. package/dist/{launch-Jh5NVeNe.mjs → launch-DpQPZGsT.mjs} +3 -3
  56. package/dist/{launch-CXVlZjHt.mjs → launch-UPQBoW2q.mjs} +3 -3
  57. package/dist/{local-candidate-DLdwQt0s.mjs → local-candidate-Cp_bw4kK.mjs} +3 -3
  58. package/dist/{local-instances-ZeRkkuS5.mjs → local-instances-B-c-CxzP.mjs} +4 -4
  59. package/dist/{local-instances-DOxSgT_L.mjs → local-instances-bdCNNSsL.mjs} +2 -2
  60. package/dist/{local-tiers-BE4jGaMa.mjs → local-tiers-BtdEEe5n.mjs} +2 -2
  61. package/dist/{local-tiers-DNMI5W86.mjs → local-tiers-OBqAAfOg.mjs} +3 -3
  62. package/dist/{main-config-ops-BEMpuhDz.mjs → main-config-ops-BYsK3BxM.mjs} +3 -3
  63. package/dist/{main-resolver-3Ns1HPmS.mjs → main-resolver-CAC3xfZM.mjs} +17 -17
  64. package/dist/{model-inventory-BFVSNFT3.mjs → model-inventory-BuTpi9a6.mjs} +13 -13
  65. package/dist/{models-add-C1QIc1DA.mjs → models-add-CJx2YAwh.mjs} +37 -36
  66. package/dist/operator-Bpxq_WB5.mjs +7 -0
  67. package/dist/{operator-CyuB3aVw.mjs → operator-DrwWItVt.mjs} +2 -2
  68. package/dist/{pai-marker-BcZyJguM.mjs → pai-marker-DLKJ9Yr5.mjs} +2 -2
  69. package/dist/{pane-audit-DKQt71s4.mjs → pane-audit-wv9UkyCe.mjs} +27 -9
  70. package/dist/{paths-CFHTloSe.mjs → paths-DBd78I7Z.mjs} +3 -3
  71. package/dist/{paths-CHifubDh.mjs → paths-DS5VQKoR.mjs} +3 -3
  72. package/dist/{planner-BmFACdLa.mjs → planner-K0mFrCpL.mjs} +28 -28
  73. package/dist/{pool-Dh-q3iJi.mjs → pool-BZ94nFuq.mjs} +2 -2
  74. package/dist/{pool-cli-BggU8GRp.mjs → pool-cli-B1eKySeN.mjs} +5 -5
  75. package/dist/{pool-cli-DQ6OzRy2.mjs → pool-cli-BjYQDfx6.mjs} +6 -6
  76. package/dist/{postgres-C6ti4haA.mjs → postgres-C-T5OWer.mjs} +19 -9
  77. package/dist/{postgres-BdBNkL_l.mjs → postgres-CBHY5PIi.mjs} +3 -3
  78. package/dist/{project-setup-jvfONJTQ.mjs → project-setup-pIPHZaXf.mjs} +26 -246
  79. package/dist/{providers-CblTFrhz.mjs → providers-Dzx5GAUI.mjs} +6 -6
  80. package/dist/providers-QJzpaNhX.mjs +16 -0
  81. package/dist/{query-feedback-BRfPtqki.mjs → query-feedback-DN52N9C5.mjs} +1 -1
  82. package/dist/{registrable-DpyTtajg.mjs → registrable-CXBBpUcz.mjs} +4 -4
  83. package/dist/registrable-Drn8wIDO.mjs +9 -0
  84. package/dist/render-C4KR8qQ8.mjs +13 -0
  85. package/dist/{render-BYmQkuLd.mjs → render-Cz3BWIBO.mjs} +8 -8
  86. package/dist/root-Bfb1zgwE.mjs +36 -0
  87. package/dist/{root-Xglu0OPD.mjs → root-bG48WVpW.mjs} +10 -10
  88. package/dist/route-table-ZVMNBsy7.mjs +15 -0
  89. package/dist/{routing-BAAST9vT.mjs → routing-BlD_tKjW.mjs} +136 -18
  90. package/dist/{routing-CENt4HH1.mjs → routing-D27jxj9x.mjs} +13 -13
  91. package/dist/{run-CSC7mgPf.mjs → run-AZVwceiU.mjs} +49 -27
  92. package/dist/{scan-C_Y5W9py.mjs → scan-C3uotg_f.mjs} +5 -5
  93. package/dist/{scope-D-h6F6DN.mjs → scope-D8HK6vU7.mjs} +4 -4
  94. package/dist/{session-8iqQDkqb.mjs → session-BAfypQb9.mjs} +26 -26
  95. package/dist/session-BcnhepLZ.mjs +35 -0
  96. package/dist/{session-scan-W-gbwJHM.mjs → session-scan-BmnjSReu.mjs} +5 -5
  97. package/dist/session-scan-CqUQdCac.mjs +9 -0
  98. package/dist/session-usage-oycSdiD3.mjs +337 -0
  99. package/dist/{sources-ESUkZaWv.mjs → sources-CiE3rZek.mjs} +1 -1
  100. package/dist/{status-ggJ7lgVg.mjs → status-COtzewKV.mjs} +3 -3
  101. package/dist/{status-Dm4dYciN.mjs → status-D_SWmOlH.mjs} +4 -4
  102. package/dist/{tool-errors-CJWODvlo.mjs → tool-errors-B2LnIqR0.mjs} +6 -6
  103. package/dist/{tool-errors-7E13StWd.mjs → tool-errors-DXJ70-sr.mjs} +2 -2
  104. package/dist/{viewer-mDXUNHNy.mjs → viewer-BgGRqXfD.mjs} +14 -14
  105. package/dist/viewer-CbGN2Ri4.mjs +22 -0
  106. package/dist/{wait-ByV0OyMZ.mjs → wait-ChpHKJZV.mjs} +3 -3
  107. package/dist/worker-config-D1KVAMp4.mjs +42 -0
  108. package/dist/{worker-config-CJhEMk19.mjs → worker-config-DgdT0iTB.mjs} +443 -40
  109. package/dist/workers-config-BhZBrR-Q.mjs +4 -0
  110. package/dist/{worktree-CRs2gyg0.mjs → worktree-BY--Rgmy.mjs} +5 -5
  111. package/dist/{zettelkasten-B_rRRRvD.mjs → zettelkasten-BHVx5zj-.mjs} +4 -4
  112. package/dist/{zettelkasten-BzMm8REH.mjs → zettelkasten-LoH9RBiN.mjs} +2 -2
  113. package/docs/cockpit.md +1 -0
  114. package/docs/commands/README.md +1 -0
  115. package/docs/commands/config.md +20 -0
  116. package/gemini-extension.json +1 -1
  117. package/package.json +1 -1
  118. package/pai-plugin.json +1 -1
  119. package/plugins/context-preservation/plugin.json +1 -1
  120. package/plugins/core/plugin.json +1 -1
  121. package/plugins/creative/plugin.json +1 -1
  122. package/plugins/observability/plugin.json +1 -1
  123. package/plugins/productivity/plugin.json +1 -1
  124. package/plugins/semantic-search/plugin.json +1 -1
  125. package/plugins/ui/plugin.json +1 -1
  126. package/plugins/zettelkasten/plugin.json +1 -1
  127. package/src/hooks/ts/capture-all-events.ts +15 -3
  128. package/src/hooks/ts/lib/raw-capture.ts +7 -0
  129. package/src/hooks/ts/post-tool-use/capture-tool-output.ts +13 -3
  130. package/dist/auto-route-CjjQqU1o.mjs +0 -11
  131. package/dist/budget-Dod6rnA9.mjs +0 -15
  132. package/dist/cockpit-memory--dnShXBA.mjs +0 -20
  133. package/dist/config-Cxi-aBf4.mjs +0 -4
  134. package/dist/daemon-DKdBNKfa.mjs +0 -45
  135. package/dist/detect-D1ziFFfA.mjs +0 -10
  136. package/dist/kill-tree-BG7dRVtp.mjs +0 -10
  137. package/dist/operator-BTqvTIc8.mjs +0 -7
  138. package/dist/providers-BeDiVEb-.mjs +0 -16
  139. package/dist/registrable-BzH4TObg.mjs +0 -9
  140. package/dist/render-CoMvwMPs.mjs +0 -13
  141. package/dist/root-FtJsxYnm.mjs +0 -36
  142. package/dist/route-table-B7zpoypi.mjs +0 -15
  143. package/dist/session-BeqAbNPd.mjs +0 -35
  144. package/dist/session-scan-BJI_ZO8M.mjs +0 -9
  145. package/dist/viewer-CqozWW39.mjs +0 -22
  146. package/dist/worker-config-DESkIjMF.mjs +0 -41
  147. package/dist/workers-config-f8KrkY4L.mjs +0 -4
@@ -1,12 +1,234 @@
1
1
  import { l as claudeHomeDir, t as aibrokerHomeDir } from "./runtime-paths-WsO9sJFc.mjs";
2
- import { F as writeWorkersSection, N as resolveModelCapability, P as resolveProviderKey, c as WorkersConfigError, j as readWorkersSection } from "./config-XpZWOYOG.mjs";
3
- import { D as readJsonStrict, O as writeJsonAtomic } from "./config-Bu1_vbY8.mjs";
4
- import { u as workersLogDir } from "./paths-CHifubDh.mjs";
5
- import { _ as resolveTarget } from "./routing-BAAST9vT.mjs";
2
+ import { F as resolveProviderKey, L as writeWorkersSection, M as readWorkersSection, P as resolveModelCapability, c as WorkersConfigError } from "./config-CST2uq1v.mjs";
3
+ import { O as readJsonStrict, k as writeJsonAtomic } from "./config-DYbyEe4-.mjs";
4
+ import { u as workersLogDir } from "./paths-DS5VQKoR.mjs";
5
+ import { _ as resolveTarget } from "./routing-BlD_tKjW.mjs";
6
+ import { yt as psTable } from "./controls-OiVVbe-f.mjs";
6
7
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
7
- import { join } from "node:path";
8
- import { execFileSync } from "node:child_process";
8
+ import { isAbsolute, join, relative } from "node:path";
9
+ import { execFileSync, spawn } from "node:child_process";
10
+ import { fileURLToPath } from "node:url";
9
11
 
12
+ //#region src/workers/hang-probe.ts
13
+ /**
14
+ * hang-probe.ts — `pai worker test-run -- <command>`: runs a test command and, when it goes silent,
15
+ * tells the model WHERE it is stuck.
16
+ *
17
+ * A synchronous infinite loop blocks Node's event loop, so the test runner cannot even report its own
18
+ * timeout and the model sees nothing. Node's `--report-on-signal` does not help: it is serviced by the
19
+ * event loop (measured: a `while(true){}` process never writes the report). The child therefore runs with a
20
+ * preloaded watchdog (NODE_OPTIONS --require): a worker thread per process that, when a trigger file appears,
21
+ * attaches the inspector to the main thread, pauses it (V8 interrupts a running loop) and writes the call
22
+ * frames. After the command was silent for the idle threshold the wrapper triggers every node process under
23
+ * it and maps the frames to the worktree's files. The worker guard rewrites test commands into this wrapper
24
+ * (see wrapTestCommand).
25
+ */
26
+ /** Marker line every diagnosis carries; the loop guard keys on it. */
27
+ const HANG_MARKER = "[pai hang probe]";
28
+ const DEFAULT_IDLE_SECS = 60;
29
+ /** Seconds of the first `timeout N` in the command, else null. */
30
+ function timeoutSecs(command) {
31
+ const m = /\b(?:g?timeout)\s+(?:-[ks]\s+\S+\s+|-\S+\s+)*(\d+(?:\.\d+)?)([smhd]?)\b/.exec(command);
32
+ if (!m) return null;
33
+ return Number(m[1]) * {
34
+ "": 1,
35
+ s: 1,
36
+ m: 60,
37
+ h: 3600,
38
+ d: 86400
39
+ }[m[2]];
40
+ }
41
+ function killQuiet(pid, sig) {
42
+ try {
43
+ process.kill(pid, sig);
44
+ } catch {}
45
+ }
46
+ function rmQuiet(f) {
47
+ try {
48
+ unlinkSync(f);
49
+ } catch {}
50
+ }
51
+ const shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
52
+ /** The command line test-run executes: the decoded --cmd-b64 payload, else the words after `--`, each shell-quoted. */
53
+ function commandToRun(cmdB64, words) {
54
+ if (cmdB64) return Buffer.from(cmdB64, "base64").toString("utf8");
55
+ return words.map(shq).join(" ");
56
+ }
57
+ /** Call frames of one probed process, user code only, files relative to cwd. */
58
+ function userFrames(frames, cwd) {
59
+ const out = [];
60
+ for (const f of frames) {
61
+ let file = f.url;
62
+ if (file.startsWith("file://")) try {
63
+ file = fileURLToPath(file);
64
+ } catch {
65
+ continue;
66
+ }
67
+ if (!file || file.startsWith("node:") || file.includes("node_modules") || !isAbsolute(file)) continue;
68
+ const rel = relative(cwd, file);
69
+ if (rel.startsWith("..")) continue;
70
+ out.push({
71
+ fn: f.functionName || "(anonymous)",
72
+ file: rel,
73
+ line: f.lineNumber + 1
74
+ });
75
+ }
76
+ return out;
77
+ }
78
+ /** The model-facing diagnosis for the reports taken after `idleSecs` of silence. */
79
+ function diagnose(probed, cwd, idleSecs) {
80
+ const stuck = probed.map((r) => userFrames(r, cwd)).find((f) => f.length > 0);
81
+ if (stuck) {
82
+ const [top, ...rest] = stuck;
83
+ const via = rest[0] ? ` (called from ${rest[0].file}:${rest[0].line} ${rest[0].fn}())` : "";
84
+ return `${HANG_MARKER} test run hung with no output for ${idleSecs} s; the main thread is stuck in ${top.file}:${top.line} ${top.fn}()${via}. A synchronous loop that never ends blocks the runner's own timeout. Fix that loop; rerunning unchanged will hang again.\n` + stuck.slice(0, 6).map((f) => ` at ${f.fn} (${f.file}:${f.line})`).join("\n");
85
+ }
86
+ return `${HANG_MARKER} test run printed nothing for ${idleSecs} s and the main thread is idle in the event loop: it is waiting (async), not spinning. A test awaits a promise, timer, process or socket that never completes (vitest's own per-test timeout ends it unless the test raised it). Find the await and give it an end; rerunning unchanged will wait again.`;
87
+ }
88
+ function listProcs() {
89
+ try {
90
+ return psTable("pid=,ppid=,command=").split("\n").flatMap((l) => {
91
+ const m = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(l);
92
+ return m ? [{
93
+ pid: Number(m[1]),
94
+ ppid: Number(m[2]),
95
+ cmd: m[3]
96
+ }] : [];
97
+ });
98
+ } catch {
99
+ return [];
100
+ }
101
+ }
102
+ /** Pids of node processes below `root`. */
103
+ function nodeDescendants(root, procs = listProcs()) {
104
+ const seen = new Set([root]);
105
+ for (let grew = true; grew;) {
106
+ grew = false;
107
+ for (const p of procs) if (!seen.has(p.pid) && seen.has(p.ppid)) seen.add(p.pid), grew = true;
108
+ }
109
+ return procs.filter((p) => p.pid !== root && seen.has(p.pid) && /(?:^|\/)node(?:\s|$)/.test(p.cmd)).map((p) => p.pid);
110
+ }
111
+ /** Runs the command, passes its output through, probes once when it goes silent. Resolves to the exit code. */
112
+ async function runProbed(o) {
113
+ const out = o.out ?? ((s) => process.stdout.write(s));
114
+ const err = o.err ?? ((s) => process.stderr.write(s));
115
+ mkdirSync(o.reportDir, { recursive: true });
116
+ const t = timeoutSecs(o.command);
117
+ const idle = Math.max(2, Math.min(o.idleSecs ?? DEFAULT_IDLE_SECS, t !== null ? t - 8 : Infinity));
118
+ const preload = join(o.reportDir, "preload.cjs");
119
+ writeFileSync(preload, PRELOAD_SOURCE);
120
+ const env = {
121
+ ...process.env,
122
+ PAI_HANG_PROBE_DIR: o.reportDir,
123
+ NODE_OPTIONS: `${process.env.NODE_OPTIONS ?? ""} --require=${preload}`.trim()
124
+ };
125
+ const child = spawn("bash", ["-c", o.command], {
126
+ cwd: o.cwd,
127
+ env,
128
+ stdio: [
129
+ "ignore",
130
+ "pipe",
131
+ "pipe"
132
+ ]
133
+ });
134
+ let last = Date.now();
135
+ let probing = false;
136
+ let announcedIdle = false;
137
+ let spun = false;
138
+ let pending = Promise.resolve();
139
+ child.stdout.on("data", (d) => (last = Date.now(), out(d.toString())));
140
+ child.stderr.on("data", (d) => (last = Date.now(), err(d.toString())));
141
+ const timer = setInterval(() => {
142
+ if (probing || spun || Date.now() - last < idle * 1e3 || !child.pid) return;
143
+ probing = true;
144
+ const pids = nodeDescendants(child.pid);
145
+ for (const p of pids) writeFileSync(join(o.reportDir, `probe-${p}`), "");
146
+ pending = collectFrames(o.reportDir, pids, 3e3).then((frames) => {
147
+ for (const p of pids) rmQuiet(join(o.reportDir, `probe-${p}`));
148
+ const stuck = frames.some((f) => userFrames(f, o.cwd).length > 0);
149
+ if (stuck || !announcedIdle) err(`\n${diagnose(frames, o.cwd, idle)}\n`);
150
+ announcedIdle = true;
151
+ probing = false;
152
+ if (stuck) {
153
+ spun = true;
154
+ for (const p of [...pids, child.pid]) killQuiet(p, "SIGTERM");
155
+ setTimeout(() => pids.forEach((p) => killQuiet(p, "SIGKILL")), 2e3).unref();
156
+ }
157
+ });
158
+ }, 1e3);
159
+ return await new Promise((resolve) => {
160
+ child.on("close", (code, sig) => {
161
+ clearInterval(timer);
162
+ pending.then(() => resolve(spun ? 124 : code ?? (sig ? 128 + (sig === "SIGKILL" ? 9 : 15) : 1)));
163
+ });
164
+ });
165
+ }
166
+ /** Frames each probed pid reported (empty list: its main thread was idle); pids that never answered are left out. */
167
+ async function collectFrames(dir, pids, waitMs) {
168
+ const got = /* @__PURE__ */ new Map();
169
+ for (const end = Date.now() + waitMs; Date.now() < end && got.size < pids.length;) {
170
+ for (const p of pids) {
171
+ const f = join(dir, `frames-${p}.json`);
172
+ if (got.has(p) || !existsSync(f)) continue;
173
+ try {
174
+ got.set(p, JSON.parse(readFileSync(f, "utf8")).frames);
175
+ unlinkSync(f);
176
+ } catch {}
177
+ }
178
+ await new Promise((r) => setTimeout(r, 100));
179
+ }
180
+ return [...got.values()];
181
+ }
182
+ /** Preloaded into every node process of the test run (CommonJS, so it also loads under --require). */
183
+ const PRELOAD_SOURCE = `"use strict";
184
+ const { Worker, isMainThread } = require("node:worker_threads");
185
+ if (isMainThread) {
186
+ const code = \`
187
+ const { existsSync, unlinkSync, writeFileSync, renameSync } = require("node:fs");
188
+ const { Session } = require("node:inspector");
189
+ const { workerData } = require("node:worker_threads");
190
+ const trigger = workerData.dir + "/probe-" + workerData.pid;
191
+ const done = (frames) => {
192
+ const tmp = workerData.dir + "/tmp-" + workerData.pid;
193
+ writeFileSync(tmp, JSON.stringify({ frames }));
194
+ renameSync(tmp, workerData.dir + "/frames-" + workerData.pid + ".json");
195
+ };
196
+ setInterval(() => {
197
+ if (!existsSync(trigger)) return;
198
+ try { unlinkSync(trigger); } catch {}
199
+ const s = new Session();
200
+ s.connectToMainThread();
201
+ let answered = false;
202
+ const urls = {}, maps = {};
203
+ s.on("Debugger.scriptParsed", (m) => { urls[m.params.scriptId] = m.params.url; maps[m.params.scriptId] = m.params.sourceMapURL; });
204
+ // transformed code (vite-node, tsx) has shifted lines: map back through its inline source map
205
+ const where = (f) => {
206
+ const id = f.location.scriptId;
207
+ let url = urls[id] || f.url, line = f.location.lineNumber;
208
+ const m = /^data:[^,]*;base64,(.*)$/.exec(maps[id] || "");
209
+ if (m) try {
210
+ const e = new (require("node:module").SourceMap)(JSON.parse(Buffer.from(m[1], "base64").toString())).findEntry(line, f.location.columnNumber || 0);
211
+ if (e && e.originalSource) { url = new URL(e.originalSource, url).href; line = e.originalLine; }
212
+ } catch {}
213
+ return { functionName: f.functionName, url, lineNumber: line };
214
+ };
215
+ s.on("Debugger.paused", (m) => {
216
+ answered = true;
217
+ done(m.params.callFrames.map(where));
218
+ s.post("Debugger.resume", () => s.post("Debugger.disable", () => s.disconnect()));
219
+ });
220
+ s.post("Debugger.enable", () => s.post("Debugger.pause"));
221
+ // an idle event loop runs no JS to pause in
222
+ setTimeout(() => { if (!answered) { done([]); s.post("Debugger.disable", () => s.disconnect()); } }, 800);
223
+ }, 200);
224
+ \`;
225
+ try {
226
+ new Worker(code, { eval: true, workerData: { dir: process.env.PAI_HANG_PROBE_DIR, pid: process.pid } }).unref();
227
+ } catch {}
228
+ }
229
+ `;
230
+
231
+ //#endregion
10
232
  //#region src/workers/fallback.ts
11
233
  /**
12
234
  * fallback.ts — machine-wide Claude Code fallback to a worker provider.
@@ -271,5 +493,5 @@ function fallbackStatusText(s) {
271
493
  }
272
494
 
273
495
  //#endregion
274
- export { fallbackStatusText as i, fallbackOn as n, fallbackStatus as r, fallbackOff as t };
275
- //# sourceMappingURL=fallback-DFlItw1Z.mjs.map
496
+ export { DEFAULT_IDLE_SECS as a, fallbackStatusText as i, fallbackOn as n, commandToRun as o, fallbackStatus as r, runProbed as s, fallbackOff as t };
497
+ //# sourceMappingURL=fallback-Bj2cyVk_.mjs.map
@@ -1,7 +1,7 @@
1
1
  import { g as paiHomePath } from "./runtime-paths-WsO9sJFc.mjs";
2
- import { t as ANTHROPIC_NATIVE } from "./config-XpZWOYOG.mjs";
3
- import { A as budgetSkipReason, D as budgetExceeded, E as budgetCache, M as budgetWall } from "./routing-BAAST9vT.mjs";
4
- import { n as tierProviderNames, t as isLocalCandidate } from "./local-candidate-DLdwQt0s.mjs";
2
+ import { t as ANTHROPIC_NATIVE } from "./config-CST2uq1v.mjs";
3
+ import { A as budgetSkipReason, D as budgetExceeded, E as budgetCache, M as budgetWall } from "./routing-BlD_tKjW.mjs";
4
+ import { n as tierProviderNames, t as isLocalCandidate } from "./local-candidate-Cp_bw4kK.mjs";
5
5
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
6
6
  import { Chalk } from "chalk";
7
7
 
@@ -311,4 +311,4 @@ function saveBudgetSnapshot(map, now = Date.now()) {
311
311
 
312
312
  //#endregion
313
313
  export { colorEnabled as a, renderCommandHelp as c, renderOverview as d, renderTable as f, saveBudgetSnapshot as i, renderMarkdown as l, usageLine as m, readBudget as n, makeStyle as o, suggest as p, readBudgetSnapshot as r, mdCommand as s, providerHealth as t, renderMore as u };
314
- //# sourceMappingURL=health-Dnym-qcT.mjs.map
314
+ //# sourceMappingURL=health-kufVIf6i.mjs.map
@@ -1,4 +1,4 @@
1
- import { i as getRegistryBackend } from "./factory-DjAAsqr_.mjs";
1
+ import { i as getRegistryBackend } from "./factory-BV0MQA3T.mjs";
2
2
 
3
3
  //#region src/cli/impl/project/helpers.ts
4
4
  /**
@@ -66,4 +66,4 @@ async function upsertTag(tagName) {
66
66
 
67
67
  //#endregion
68
68
  export { getSessionCount as a, upsertTag as c, getProjectTags as i, getProject as n, requireProject as o, getProjectAliases as r, resolveIdentifier as s, getLastSessionDate as t };
69
- //# sourceMappingURL=helpers-BD6V-p9w.mjs.map
69
+ //# sourceMappingURL=helpers-CvbYEKoD.mjs.map
@@ -1,10 +1,10 @@
1
- import "./config-Bu1_vbY8.mjs";
1
+ import "./config-DYbyEe4-.mjs";
2
2
  import "./helpers-CRuBUas3.mjs";
3
- import "./factory-DjAAsqr_.mjs";
4
- import "./postgres-C6ti4haA.mjs";
5
- import "./embedding-gate-6rZrWRXa.mjs";
3
+ import "./factory-BV0MQA3T.mjs";
4
+ import "./postgres-C-T5OWer.mjs";
5
+ import "./embedding-gate-CTAk8STe.mjs";
6
6
  import "./embeddings-BVq3esBF.mjs";
7
7
  import "./router-CQSc3lk3.mjs";
8
- import { a as getSessionCount, c as upsertTag, i as getProjectTags, n as getProject, o as requireProject, r as getProjectAliases, s as resolveIdentifier, t as getLastSessionDate } from "./helpers-BD6V-p9w.mjs";
8
+ import { a as getSessionCount, c as upsertTag, i as getProjectTags, n as getProject, o as requireProject, r as getProjectAliases, s as resolveIdentifier, t as getLastSessionDate } from "./helpers-CvbYEKoD.mjs";
9
9
 
10
10
  export { getProject };
@@ -7406,7 +7406,7 @@ function parseEscalate(v, bad2) {
7406
7406
  }
7407
7407
 
7408
7408
  // src/workers/config.ts
7409
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
7409
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
7410
7410
 
7411
7411
  // src/daemon/config.ts
7412
7412
  import { homedir as homedir2, userInfo } from "node:os";
@@ -7738,8 +7738,18 @@ function maskSecrets(text) {
7738
7738
  return text.split("\n").map((l) => SECRET_LINE.test(l) ? MASKED_LINE : l).join("\n");
7739
7739
  }
7740
7740
 
7741
+ // src/utils/hash.ts
7742
+ import { createHash } from "node:crypto";
7743
+ function sha256(content) {
7744
+ return createHash("sha256").update(content).digest("hex");
7745
+ }
7746
+
7741
7747
  // src/hooks/ts/lib/raw-capture.ts
7742
7748
  var RAW_CAPTURE_MAX_BYTES = 10 * 1024 * 1024;
7749
+ function captureEventId(parts) {
7750
+ const joined = `${parts.sessionId ?? ""}|${parts.toolUseId ?? ""}|${parts.event ?? ""}|${parts.ts ?? ""}`;
7751
+ return sha256(joined).slice(0, 16);
7752
+ }
7743
7753
  function redactDeep(value) {
7744
7754
  if (typeof value === "string") return maskSecrets(value);
7745
7755
  if (Array.isArray(value)) return value.map(redactDeep);
@@ -7855,13 +7865,22 @@ async function main() {
7855
7865
  setAgentForSession(sessionId, agentName);
7856
7866
  }
7857
7867
  }
7868
+ const timestamp = Date.now();
7858
7869
  let event = {
7859
7870
  source_app: agentName,
7860
7871
  session_id: hookData.session_id || "main",
7861
7872
  hook_event_type: eventType,
7862
7873
  payload: redactDeep(hookData),
7863
- timestamp: Date.now(),
7864
- timestamp_local: getLocalTimestamp()
7874
+ timestamp,
7875
+ timestamp_local: getLocalTimestamp(),
7876
+ source: "hook",
7877
+ fidelity: "observed",
7878
+ eventId: captureEventId({
7879
+ sessionId: hookData.session_id,
7880
+ toolUseId: hookData.tool_use_id,
7881
+ event: eventType,
7882
+ ts: String(timestamp)
7883
+ })
7865
7884
  };
7866
7885
  if (isAgentSpawningCall(hookData.tool_name, hookData.tool_input)) {
7867
7886
  event = enrichEventWithAgentMetadata(
@@ -155,8 +155,18 @@ function maskSecrets(text) {
155
155
  return text.split("\n").map((l) => SECRET_LINE.test(l) ? MASKED_LINE : l).join("\n");
156
156
  }
157
157
 
158
+ // src/utils/hash.ts
159
+ import { createHash } from "node:crypto";
160
+ function sha256(content) {
161
+ return createHash("sha256").update(content).digest("hex");
162
+ }
163
+
158
164
  // src/hooks/ts/lib/raw-capture.ts
159
165
  var RAW_CAPTURE_MAX_BYTES = 10 * 1024 * 1024;
166
+ function captureEventId(parts) {
167
+ const joined = `${parts.sessionId ?? ""}|${parts.toolUseId ?? ""}|${parts.event ?? ""}|${parts.ts ?? ""}`;
168
+ return sha256(joined).slice(0, 16);
169
+ }
160
170
  function redactDeep(value) {
161
171
  if (typeof value === "string") return maskSecrets(value);
162
172
  if (Array.isArray(value)) return value.map(redactDeep);
@@ -202,12 +212,21 @@ async function main() {
202
212
  const yearMonth = today.substring(0, 7);
203
213
  const dateDir = join3(CAPTURE_DIR, yearMonth);
204
214
  const captureFile = join3(dateDir, `${today}_tool-outputs.jsonl`);
215
+ const timestamp = data.timestamp || now.toISOString();
205
216
  const captureEntry = JSON.stringify({
206
- timestamp: data.timestamp || now.toISOString(),
217
+ timestamp,
207
218
  tool: data.tool_name,
208
219
  input: redactDeep(data.tool_input),
209
220
  output: redactDeep(data.tool_response),
210
- session: data.session_id
221
+ session: data.session_id,
222
+ source: "hook",
223
+ fidelity: "observed",
224
+ eventId: captureEventId({
225
+ sessionId: data.session_id,
226
+ toolUseId: data.tool_use_id,
227
+ event: "PostToolUse",
228
+ ts: timestamp
229
+ })
211
230
  }) + "\n";
212
231
  appendRawCapture(captureFile, dateDir, captureEntry);
213
232
  process.exit(0);
@@ -7406,7 +7406,7 @@ function parseEscalate(v, bad2) {
7406
7406
  }
7407
7407
 
7408
7408
  // src/workers/config.ts
7409
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
7409
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
7410
7410
 
7411
7411
  // src/daemon/config.ts
7412
7412
  import { homedir as homedir2, userInfo } from "node:os";
@@ -8964,7 +8964,7 @@ function statusPath(logDir, id) {
8964
8964
  }
8965
8965
 
8966
8966
  // src/workers/pending.ts
8967
- import { existsSync as existsSync9, readFileSync as readFileSync9, statSync as statSync4 } from "node:fs";
8967
+ import { existsSync as existsSync9, readFileSync as readFileSync9, statSync as statSync5 } from "node:fs";
8968
8968
 
8969
8969
  // src/workers/status.ts
8970
8970
  import { execFileSync } from "node:child_process";
@@ -8975,7 +8975,7 @@ import {
8975
8975
  readdirSync as readdirSync2,
8976
8976
  renameSync as renameSync4,
8977
8977
  rmSync,
8978
- statSync as statSync2,
8978
+ statSync as statSync3,
8979
8979
  writeFileSync as writeFileSync3
8980
8980
  } from "node:fs";
8981
8981
  import { join as join7 } from "node:path";
@@ -9048,7 +9048,7 @@ var LOST_GRACE_MS = 2 * 6e4;
9048
9048
 
9049
9049
  // src/workers/worktree.ts
9050
9050
  import { execFile, execFileSync as execFileSync2, spawnSync } from "node:child_process";
9051
- import { existsSync as existsSync8, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync8, mkdtempSync, writeFileSync as writeFileSync4, readdirSync as readdirSync3, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
9051
+ import { existsSync as existsSync8, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync8, mkdtempSync, writeFileSync as writeFileSync4, readdirSync as readdirSync3, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync4 } from "node:fs";
9052
9052
 
9053
9053
  // src/workers/pool.ts
9054
9054
  var SPARE_WARM_WINDOW_MS = 60 * 6e4;
@@ -9136,7 +9136,7 @@ function pendingResults(logDir, repoRoot, now = Date.now()) {
9136
9136
  if (!commits && !dirty) continue;
9137
9137
  let ageMs = 0;
9138
9138
  try {
9139
- ageMs = Math.max(0, now - statSync4(statusPath(logDir, st.id)).mtimeMs);
9139
+ ageMs = Math.max(0, now - statSync5(statusPath(logDir, st.id)).mtimeMs);
9140
9140
  } catch {
9141
9141
  }
9142
9142
  out.push({ id: st.id, label: st.label, repo, ageMs, age: fmtAge(ageMs, { dayAfterHours: 48 }), commits, dirty });
@@ -9156,7 +9156,7 @@ import { homedir as homedir4 } from "os";
9156
9156
  import { execSync } from "child_process";
9157
9157
 
9158
9158
  // src/memory/wakeup.ts
9159
- import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync5 } from "node:fs";
9159
+ import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync6 } from "node:fs";
9160
9160
  import { join as join8, basename as basename3 } from "node:path";
9161
9161
  var L1_TOKEN_BUDGET = 300;
9162
9162
  var L1_CHAR_BUDGET = L1_TOKEN_BUDGET * 4;
@@ -9217,7 +9217,7 @@ function findSessionNotes(notesDir) {
9217
9217
  const numberOf = (p) => parseInt(basename3(p).match(/^(\d+)/)?.[1] ?? "0", 10);
9218
9218
  const mtimeOf = (p) => {
9219
9219
  try {
9220
- return statSync5(p).mtimeMs;
9220
+ return statSync6(p).mtimeMs;
9221
9221
  } catch {
9222
9222
  return 0;
9223
9223
  }
@@ -7409,7 +7409,7 @@ function parseEscalate(v, bad2) {
7409
7409
  }
7410
7410
 
7411
7411
  // src/workers/config.ts
7412
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
7412
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
7413
7413
 
7414
7414
  // src/daemon/config.ts
7415
7415
  import { homedir as homedir2, userInfo } from "node:os";
@@ -7426,7 +7426,7 @@ function parseEscalate(v, bad2) {
7426
7426
  }
7427
7427
 
7428
7428
  // src/workers/config.ts
7429
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
7429
+ import { existsSync as existsSync7, readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
7430
7430
 
7431
7431
  // src/daemon/config.ts
7432
7432
  import { homedir as homedir2, userInfo } from "node:os";
@@ -7409,7 +7409,7 @@ function parseEscalate(v, bad2) {
7409
7409
  }
7410
7410
 
7411
7411
  // src/workers/config.ts
7412
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
7412
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
7413
7413
 
7414
7414
  // src/daemon/config.ts
7415
7415
  import { homedir as homedir2, userInfo } from "node:os";
@@ -8975,7 +8975,7 @@ import {
8975
8975
  readdirSync as readdirSync2,
8976
8976
  renameSync as renameSync4,
8977
8977
  rmSync,
8978
- statSync as statSync2,
8978
+ statSync as statSync3,
8979
8979
  writeFileSync as writeFileSync3
8980
8980
  } from "node:fs";
8981
8981
  import { join as join7 } from "node:path";
@@ -7409,7 +7409,7 @@ function parseEscalate(v, bad2) {
7409
7409
  }
7410
7410
 
7411
7411
  // src/workers/config.ts
7412
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
7412
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
7413
7413
 
7414
7414
  // src/daemon/config.ts
7415
7415
  import { homedir as homedir2, userInfo } from "node:os";
@@ -8974,7 +8974,7 @@ import {
8974
8974
  readdirSync as readdirSync2,
8975
8975
  renameSync as renameSync4,
8976
8976
  rmSync,
8977
- statSync as statSync2,
8977
+ statSync as statSync3,
8978
8978
  writeFileSync as writeFileSync3
8979
8979
  } from "node:fs";
8980
8980
 
@@ -7409,7 +7409,7 @@ function parseEscalate(v, bad2) {
7409
7409
  }
7410
7410
 
7411
7411
  // src/workers/config.ts
7412
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
7412
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
7413
7413
 
7414
7414
  // src/daemon/config.ts
7415
7415
  import { homedir as homedir2, userInfo } from "node:os";
@@ -8981,7 +8981,7 @@ import {
8981
8981
  readdirSync as readdirSync2,
8982
8982
  renameSync as renameSync4,
8983
8983
  rmSync,
8984
- statSync as statSync2,
8984
+ statSync as statSync3,
8985
8985
  writeFileSync as writeFileSync3
8986
8986
  } from "node:fs";
8987
8987
  import { join as join8 } from "node:path";
@@ -10380,7 +10380,7 @@ function decideOrchestratorBash(rawCommand) {
10380
10380
  // src/hooks/ts/lib/loop-guard.ts
10381
10381
  import { execFileSync as execFileSync8 } from "node:child_process";
10382
10382
  import { createHash } from "node:crypto";
10383
- import { readFileSync as readFileSync13, statSync as statSync3 } from "node:fs";
10383
+ import { readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
10384
10384
  import { isAbsolute as isAbsolute2, join as join11, resolve as resolve5 } from "node:path";
10385
10385
 
10386
10386
  // src/workers/ps-table.ts
@@ -10613,7 +10613,7 @@ function worktreeFingerprint(cwd) {
10613
10613
  if (!p || p.startsWith(".") || p.includes("/.")) continue;
10614
10614
  try {
10615
10615
  const file = join11(root, p);
10616
- const st = statSync3(file);
10616
+ const st = statSync4(file);
10617
10617
  if (!st.isFile() || !st.size) continue;
10618
10618
  if (st.size > FP_FULL_READ_BYTES || st.size > budget) {
10619
10619
  h.update(`${p}\0${st.size}\0${st.mtimeMs}`);
@@ -10916,7 +10916,7 @@ function saveLoopState(file, state) {
10916
10916
  }
10917
10917
 
10918
10918
  // src/workers/tool-errors.ts
10919
- import { appendFileSync, existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync15, statSync as statSync4, openSync, readSync, closeSync } from "node:fs";
10919
+ import { appendFileSync, existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync15, statSync as statSync5, openSync, readSync, closeSync } from "node:fs";
10920
10920
  import { join as join13 } from "node:path";
10921
10921
 
10922
10922
  // src/workers/agentish.ts
@@ -11131,7 +11131,7 @@ function appendStopEvent(logDir, id, stop) {
11131
11131
  }
11132
11132
 
11133
11133
  // src/builds/queue.ts
11134
- import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync4, readFileSync as readFileSync16, renameSync as renameSync6, rmSync as rmSync4, rmdirSync, statSync as statSync5, writeFileSync as writeFileSync7 } from "node:fs";
11134
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync4, readFileSync as readFileSync16, renameSync as renameSync6, rmSync as rmSync4, rmdirSync, statSync as statSync6, writeFileSync as writeFileSync7 } from "node:fs";
11135
11135
  import { join as join14 } from "node:path";
11136
11136
  var defaultDeps = () => ({
11137
11137
  isAlive: (pid) => {
@@ -11162,13 +11162,13 @@ function breakStaleMutex(m, staleMs = 5e3) {
11162
11162
  mkdirSync8(guard);
11163
11163
  } catch {
11164
11164
  try {
11165
- if (Date.now() - statSync5(guard).mtimeMs > staleMs) rmdirSync(guard);
11165
+ if (Date.now() - statSync6(guard).mtimeMs > staleMs) rmdirSync(guard);
11166
11166
  } catch {
11167
11167
  }
11168
11168
  return;
11169
11169
  }
11170
11170
  try {
11171
- if (Date.now() - statSync5(m).mtimeMs > staleMs) rmdirSync(m);
11171
+ if (Date.now() - statSync6(m).mtimeMs > staleMs) rmdirSync(m);
11172
11172
  } catch {
11173
11173
  } finally {
11174
11174
  try {
@@ -7409,7 +7409,7 @@ function parseEscalate(v, bad2) {
7409
7409
  }
7410
7410
 
7411
7411
  // src/workers/config.ts
7412
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
7412
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
7413
7413
 
7414
7414
  // src/daemon/config.ts
7415
7415
  import { homedir as homedir2, userInfo } from "node:os";
@@ -8959,7 +8959,7 @@ import {
8959
8959
  readdirSync as readdirSync2,
8960
8960
  renameSync as renameSync4,
8961
8961
  rmSync,
8962
- statSync as statSync2,
8962
+ statSync as statSync3,
8963
8963
  writeFileSync as writeFileSync3
8964
8964
  } from "node:fs";
8965
8965