@tekmidian/pai 1.0.3 → 1.0.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.
Files changed (85) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/{auto-route-B7Yu6Uam.mjs → auto-route-21GdHL70.mjs} +1 -1
  3. package/dist/{auto-route-CjjQqU1o.mjs → auto-route-C3S2W3Bc.mjs} +1 -1
  4. package/dist/{budget-Dod6rnA9.mjs → budget-BrloTOZW.mjs} +1 -1
  5. package/dist/{chain-7n9Gx56r.mjs → chain-D7PFur8Y.mjs} +2 -2
  6. package/dist/cli/index.mjs +70 -180
  7. package/dist/{cockpit-actions-DR9X514g.mjs → cockpit-actions-BfRMr2sm.mjs} +6 -6
  8. package/dist/{cockpit-actions-BBHeyEZI.mjs → cockpit-actions-OodH_vx_.mjs} +20 -18
  9. package/dist/{cockpit-memory--dnShXBA.mjs → cockpit-memory-teG9M8bG.mjs} +6 -6
  10. package/dist/config/defaults/config.yaml +6 -0
  11. package/dist/{controls-CPoeudou.mjs → controls-JLm6g0yP.mjs} +7 -5
  12. package/dist/daemon/index.mjs +18 -18
  13. package/dist/{daemon-DKdBNKfa.mjs → daemon-DeYImF1F.mjs} +18 -18
  14. package/dist/daemon-mcp/index.mjs +18 -18
  15. package/dist/{daemon-DPAo6WTa.mjs → daemon-nXanenDM.mjs} +21 -17
  16. package/dist/{detect-9CZNO0yf.mjs → detect-BLKsUOgG.mjs} +2 -2
  17. package/dist/{detect-D1ziFFfA.mjs → detect-Rrimh0xc.mjs} +3 -3
  18. package/dist/{embed-pass-DIWjA68P.mjs → embed-pass-e64AwDCa.mjs} +1 -1
  19. package/dist/{endreason-C577xIQz.mjs → endreason-BC-v05H_.mjs} +17 -3
  20. package/dist/{factory-DjAAsqr_.mjs → factory-B7nv75rC.mjs} +2 -2
  21. package/dist/{factory-Bsib-qDG.mjs → factory-Bs9imVMq.mjs} +2 -2
  22. package/dist/{fallback-DFlItw1Z.mjs → fallback-CBWpp2F2.mjs} +227 -5
  23. package/dist/{health-Dnym-qcT.mjs → health-C4FQmkJn.mjs} +2 -2
  24. package/dist/{helpers-BD6V-p9w.mjs → helpers-DSNK_1KP.mjs} +2 -2
  25. package/dist/{helpers-CMGjy_0N.mjs → helpers-Dz7mjTSw.mjs} +3 -3
  26. package/dist/hooks/capture-all-events.mjs +21 -2
  27. package/dist/hooks/capture-tool-output.mjs +21 -2
  28. package/dist/hooks/worker-pool-host.mjs +52 -9
  29. package/dist/hooks/worker-status-line.mjs +34 -6
  30. package/dist/{indexer-backend-Cll7le4P.mjs → indexer-backend-BdjeL4P8.mjs} +31 -228
  31. package/dist/{ipc-client-CaiGJnSM.mjs → ipc-client-CnYEF31R.mjs} +1 -1
  32. package/dist/{ipc-client-ZRmDKd5Z.mjs → ipc-client-DN7EvoVo.mjs} +1 -1
  33. package/dist/{kill-tree-EWYaEk5v.mjs → kill-tree-DleMNZX3.mjs} +2 -2
  34. package/dist/{kill-tree-BG7dRVtp.mjs → kill-tree-SV6hrIa3.mjs} +2 -2
  35. package/dist/{launch-Jh5NVeNe.mjs → launch-Di7N-J5X.mjs} +2 -2
  36. package/dist/{launch-CXVlZjHt.mjs → launch-DlZTKE3u.mjs} +2 -2
  37. package/dist/{main-config-ops-BEMpuhDz.mjs → main-config-ops-kTXR8Pyv.mjs} +1 -1
  38. package/dist/{main-resolver-3Ns1HPmS.mjs → main-resolver-yp_puSSb.mjs} +5 -5
  39. package/dist/{model-inventory-BFVSNFT3.mjs → model-inventory-h6Z9fN_o.mjs} +1 -1
  40. package/dist/{models-add-C1QIc1DA.mjs → models-add-TVni3A7V.mjs} +19 -19
  41. package/dist/{pane-audit-DKQt71s4.mjs → pane-audit-Lqy9N_Ki.mjs} +23 -5
  42. package/dist/{planner-BmFACdLa.mjs → planner-j77ndkBf.mjs} +11 -11
  43. package/dist/{postgres-BdBNkL_l.mjs → postgres-BK4FP9Zg.mjs} +1 -1
  44. package/dist/{postgres-C6ti4haA.mjs → postgres-DNIOQijl.mjs} +17 -7
  45. package/dist/{project-setup-jvfONJTQ.mjs → project-setup-CBRtFFh0.mjs} +15 -235
  46. package/dist/{providers-CblTFrhz.mjs → providers-C9QC0TtO.mjs} +2 -2
  47. package/dist/{providers-BeDiVEb-.mjs → providers-FvCLzFge.mjs} +2 -2
  48. package/dist/{query-feedback-BRfPtqki.mjs → query-feedback-DN52N9C5.mjs} +1 -1
  49. package/dist/{render-CoMvwMPs.mjs → render-B35bQTE-.mjs} +2 -2
  50. package/dist/{render-BYmQkuLd.mjs → render-C84BqQRC.mjs} +2 -2
  51. package/dist/{root-Xglu0OPD.mjs → root-C4oXlXS_.mjs} +7 -7
  52. package/dist/{root-FtJsxYnm.mjs → root-DD1xx_Vu.mjs} +12 -12
  53. package/dist/route-table-FEfDuhIV.mjs +15 -0
  54. package/dist/{routing-BAAST9vT.mjs → routing-DmkheCRH.mjs} +126 -8
  55. package/dist/{routing-CENt4HH1.mjs → routing-f2zulGwD.mjs} +1 -1
  56. package/dist/{run-CSC7mgPf.mjs → run-BwZKBJZZ.mjs} +25 -11
  57. package/dist/{scan-C_Y5W9py.mjs → scan-BBGvU5EB.mjs} +2 -2
  58. package/dist/{session-BeqAbNPd.mjs → session-BAhtXdxj.mjs} +11 -11
  59. package/dist/{session-8iqQDkqb.mjs → session-Dn5OJXlg.mjs} +20 -20
  60. package/dist/{session-scan-BJI_ZO8M.mjs → session-scan-B57dNuA4.mjs} +1 -1
  61. package/dist/{session-scan-W-gbwJHM.mjs → session-scan-DUn10MBd.mjs} +2 -2
  62. package/dist/session-usage-CPvXvSkw.mjs +222 -0
  63. package/dist/{sources-ESUkZaWv.mjs → sources-CiE3rZek.mjs} +1 -1
  64. package/dist/{viewer-CqozWW39.mjs → viewer-DEySE4c_.mjs} +6 -6
  65. package/dist/{viewer-mDXUNHNy.mjs → viewer-wfKnvnrI.mjs} +5 -5
  66. package/dist/{worker-config-DESkIjMF.mjs → worker-config-CO5FXLAT.mjs} +18 -18
  67. package/dist/{worker-config-CJhEMk19.mjs → worker-config-eY5Qy4vl.mjs} +318 -21
  68. package/dist/{zettelkasten-BzMm8REH.mjs → zettelkasten-_h_tUm2T.mjs} +1 -1
  69. package/dist/{zettelkasten-B_rRRRvD.mjs → zettelkasten-tpCRBYvH.mjs} +2 -2
  70. package/docs/cockpit.md +1 -0
  71. package/gemini-extension.json +1 -1
  72. package/package.json +1 -1
  73. package/pai-plugin.json +1 -1
  74. package/plugins/context-preservation/plugin.json +1 -1
  75. package/plugins/core/plugin.json +1 -1
  76. package/plugins/creative/plugin.json +1 -1
  77. package/plugins/observability/plugin.json +1 -1
  78. package/plugins/productivity/plugin.json +1 -1
  79. package/plugins/semantic-search/plugin.json +1 -1
  80. package/plugins/ui/plugin.json +1 -1
  81. package/plugins/zettelkasten/plugin.json +1 -1
  82. package/src/hooks/ts/capture-all-events.ts +15 -3
  83. package/src/hooks/ts/lib/raw-capture.ts +7 -0
  84. package/src/hooks/ts/post-tool-use/capture-tool-output.ts +13 -3
  85. package/dist/route-table-B7zpoypi.mjs +0 -15
@@ -2,11 +2,233 @@ import { l as claudeHomeDir, t as aibrokerHomeDir } from "./runtime-paths-WsO9sJ
2
2
  import { F as writeWorkersSection, N as resolveModelCapability, P as resolveProviderKey, c as WorkersConfigError, j as readWorkersSection } from "./config-XpZWOYOG.mjs";
3
3
  import { D as readJsonStrict, O as writeJsonAtomic } from "./config-Bu1_vbY8.mjs";
4
4
  import { u as workersLogDir } from "./paths-CHifubDh.mjs";
5
- import { _ as resolveTarget } from "./routing-BAAST9vT.mjs";
5
+ import { _ as resolveTarget } from "./routing-DmkheCRH.mjs";
6
+ import { yt as psTable } from "./controls-JLm6g0yP.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-CBWpp2F2.mjs.map
@@ -1,6 +1,6 @@
1
1
  import { g as paiHomePath } from "./runtime-paths-WsO9sJFc.mjs";
2
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";
3
+ import { A as budgetSkipReason, D as budgetExceeded, E as budgetCache, M as budgetWall } from "./routing-DmkheCRH.mjs";
4
4
  import { n as tierProviderNames, t as isLocalCandidate } from "./local-candidate-DLdwQt0s.mjs";
5
5
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
6
6
  import { Chalk } from "chalk";
@@ -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-C4FQmkJn.mjs.map
@@ -1,4 +1,4 @@
1
- import { i as getRegistryBackend } from "./factory-DjAAsqr_.mjs";
1
+ import { i as getRegistryBackend } from "./factory-B7nv75rC.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-DSNK_1KP.mjs.map
@@ -1,10 +1,10 @@
1
1
  import "./config-Bu1_vbY8.mjs";
2
2
  import "./helpers-CRuBUas3.mjs";
3
- import "./factory-DjAAsqr_.mjs";
4
- import "./postgres-C6ti4haA.mjs";
3
+ import "./factory-B7nv75rC.mjs";
4
+ import "./postgres-DNIOQijl.mjs";
5
5
  import "./embedding-gate-6rZrWRXa.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-DSNK_1KP.mjs";
9
9
 
10
10
  export { getProject };
@@ -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);
@@ -20935,6 +20935,17 @@ var init_kg_triples = __esm({
20935
20935
  async function ensureObservationTables(pool) {
20936
20936
  if (_tablesEnsured) return;
20937
20937
  await pool.query(SCHEMA_SQL);
20938
+ const colCheck = await pool.query(
20939
+ `SELECT column_name FROM information_schema.columns
20940
+ WHERE table_name = 'pai_observations' AND column_name IN ('source', 'fidelity')`
20941
+ );
20942
+ const existing = new Set(colCheck.rows.map((r) => r.column_name));
20943
+ if (!existing.has("source")) {
20944
+ await pool.query("ALTER TABLE pai_observations ADD COLUMN IF NOT EXISTS source TEXT");
20945
+ }
20946
+ if (!existing.has("fidelity")) {
20947
+ await pool.query("ALTER TABLE pai_observations ADD COLUMN IF NOT EXISTS fidelity TEXT");
20948
+ }
20938
20949
  _tablesEnsured = true;
20939
20950
  }
20940
20951
  async function ensureSkillTelemetryTable(pool) {
@@ -20962,8 +20973,9 @@ async function storeObservation(pool, obs) {
20962
20973
  const result = await pool.query(
20963
20974
  `INSERT INTO pai_observations
20964
20975
  (session_id, project_id, project_slug, type, title, narrative,
20965
- tool_name, tool_input_summary, files_read, files_modified, concepts, content_hash)
20966
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10::jsonb, $11::jsonb, $12)
20976
+ tool_name, tool_input_summary, files_read, files_modified, concepts, content_hash,
20977
+ source, fidelity)
20978
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10::jsonb, $11::jsonb, $12, $13, $14)
20967
20979
  RETURNING id`,
20968
20980
  [
20969
20981
  obs.session_id,
@@ -20977,7 +20989,9 @@ async function storeObservation(pool, obs) {
20977
20989
  JSON.stringify(obs.files_read),
20978
20990
  JSON.stringify(obs.files_modified),
20979
20991
  JSON.stringify(obs.concepts),
20980
- hash
20992
+ hash,
20993
+ obs.source ?? null,
20994
+ obs.fidelity ?? null
20981
20995
  ]
20982
20996
  );
20983
20997
  return result.rows[0]?.id ?? null;
@@ -21019,7 +21033,7 @@ async function queryObservations(pool, opts = {}) {
21019
21033
  `SELECT id, session_id, project_id, project_slug, type, title, narrative,
21020
21034
  tool_name, tool_input_summary,
21021
21035
  files_read, files_modified, concepts,
21022
- content_hash, created_at
21036
+ content_hash, source, fidelity, created_at
21023
21037
  FROM pai_observations
21024
21038
  ${where}
21025
21039
  ORDER BY created_at DESC
@@ -21034,7 +21048,7 @@ async function queryRecentObservations(pool, projectId, limit) {
21034
21048
  `SELECT id, session_id, project_id, project_slug, type, title, narrative,
21035
21049
  tool_name, tool_input_summary,
21036
21050
  files_read, files_modified, concepts,
21037
- content_hash, created_at
21051
+ content_hash, source, fidelity, created_at
21038
21052
  FROM pai_observations
21039
21053
  WHERE project_id = $1
21040
21054
  ORDER BY created_at DESC
@@ -21049,7 +21063,7 @@ async function querySessionObservations(pool, sessionId) {
21049
21063
  `SELECT id, session_id, project_id, project_slug, type, title, narrative,
21050
21064
  tool_name, tool_input_summary,
21051
21065
  files_read, files_modified, concepts,
21052
- content_hash, created_at
21066
+ content_hash, source, fidelity, created_at
21053
21067
  FROM pai_observations
21054
21068
  WHERE session_id = $1
21055
21069
  ORDER BY created_at ASC`,
@@ -21219,6 +21233,8 @@ CREATE TABLE IF NOT EXISTS pai_observations (
21219
21233
  files_modified JSONB DEFAULT '[]'::jsonb,
21220
21234
  concepts JSONB DEFAULT '[]'::jsonb,
21221
21235
  content_hash TEXT,
21236
+ source TEXT,
21237
+ fidelity TEXT,
21222
21238
  created_at TIMESTAMPTZ DEFAULT NOW()
21223
21239
  );
21224
21240
 
@@ -27711,6 +27727,16 @@ var init_route_table = __esm({
27711
27727
  }
27712
27728
  });
27713
27729
 
27730
+ // src/router/cockpit-auth.ts
27731
+ var init_cockpit_auth = __esm({
27732
+ "src/router/cockpit-auth.ts"() {
27733
+ "use strict";
27734
+ init_pai_home();
27735
+ init_loopback();
27736
+ init_timeouts();
27737
+ }
27738
+ });
27739
+
27714
27740
  // src/router/router.ts
27715
27741
  var routerPort;
27716
27742
  var init_router2 = __esm({
@@ -27730,6 +27756,7 @@ var init_router2 = __esm({
27730
27756
  init_local_instances();
27731
27757
  init_route_table();
27732
27758
  init_loopback_guard();
27759
+ init_cockpit_auth();
27733
27760
  routerPort = () => loadConfig().router.port;
27734
27761
  }
27735
27762
  });
@@ -28014,6 +28041,13 @@ function requestKill(logDir2, ledger, id, by = callerDescription(), forced = fal
28014
28041
  writeFileAtomic(killMarkerPath(logDir2, id), JSON.stringify(req));
28015
28042
  appendLedger(ledger, "WORKER-KILL", { id, by, pid: req.pid, ppid: req.ppid, cwd: req.cwd, ...forced ? { forced: 1 } : {}, ...reason ? { reason } : {} });
28016
28043
  }
28044
+ function peekMarker(logDir2, id) {
28045
+ try {
28046
+ return JSON.parse(readFileSync36(killMarkerPath(logDir2, id), "utf8"));
28047
+ } catch {
28048
+ return null;
28049
+ }
28050
+ }
28017
28051
  function readMarker(logDir2, id) {
28018
28052
  const p = killMarkerPath(logDir2, id);
28019
28053
  if (!existsSync36(p)) return null;
@@ -28047,8 +28081,11 @@ function parentInfo() {
28047
28081
  function decideEndReason(a) {
28048
28082
  if (a.timedOut) return { reason: "deadline" };
28049
28083
  if (a.loopStopped) return { reason: "loop-stopped" };
28050
- if (!a.signal) return { reason: "exit" };
28051
- const marker = readMarker(a.logDir, a.id);
28084
+ const marker = a.signal || peekMarker(a.logDir, a.id)?.by === "pane audit" ? readMarker(a.logDir, a.id) : null;
28085
+ if (!a.signal && !marker) return { reason: "exit" };
28086
+ if (!a.signal && marker) {
28087
+ return { reason: "pai-worker-kill", by: marker.by, note: `killed by ${marker.by}${marker.reason ? `: ${marker.reason}` : ""}`, ...marker.reason ? { killReason: marker.reason } : {}, forced: true };
28088
+ }
28052
28089
  if (marker) {
28053
28090
  return {
28054
28091
  reason: "pai-worker-kill",
@@ -30957,6 +30994,7 @@ function fireDeadline(logDir2, status, ledger, limit) {
30957
30994
  status.last = what;
30958
30995
  saveStatus(logDir2, status);
30959
30996
  appendLedger(ledger, "WORKER-DEADLINE", { id: status.id, minutes: limit.minutes, waitedMin: waited });
30997
+ appendStopEvent(logDir2, status.id, { killed_by: "deadline", forced: false, reason: `${what} (timer: --max-minutes/--deadline, armed at spawn)` });
30960
30998
  void killTree(logDir2, status.id, { skipRoot: true, by: "deadline", reason: `parent worker ${what}` }).catch(() => void 0);
30961
30999
  }
30962
31000
  function trackBlocked(logDir2, status, limit) {
@@ -31032,6 +31070,10 @@ async function finaliseRun(f) {
31032
31070
  }
31033
31071
  const end = decideEndReason({ logDir: logDir2, id: wid, timedOut: f.timedOut !== void 0, loopStopped: !!loopStop });
31034
31072
  status.endReason = end.reason;
31073
+ if (end.reason === "pai-worker-kill" && !f.timedOut && end.killReason && !succeeded) {
31074
+ status.last = shortText(end.killReason, 90);
31075
+ report = { ...report ?? {}, result: report?.result ?? "-", why: end.killReason, notes: [end.killReason, report?.notes ?? f.fallbackText].filter(Boolean).join("\n") };
31076
+ }
31035
31077
  status.claimsContinue = report?.result === "~";
31036
31078
  saveStatus(logDir2, status);
31037
31079
  appendLedger(ledger, "WORKER-END", { id: wid, ...f.end, rc, secs: f.secs, ...endFields(end) });
@@ -31658,7 +31700,8 @@ async function openOwnedPane(o) {
31658
31700
  let pidWaiter = null;
31659
31701
  let ended = false;
31660
31702
  let startedAt = 0;
31661
- const record = (extra = {}) => writeJson(recordPath(o.id), { id: o.id, kind: o.kind, caller: process.pid, wrapper: wrapper || void 0, session, startedMs: startedAt || Date.now(), ...extra });
31703
+ const openedAt = Date.now();
31704
+ const record = (extra = {}) => writeJson(recordPath(o.id), { id: o.id, kind: o.kind, caller: process.pid, wrapper: wrapper || void 0, session, openedMs: openedAt, startedMs: startedAt || openedAt, ...extra });
31662
31705
  const settle = (code, signal) => {
31663
31706
  if (ended) return;
31664
31707
  ended = true;
@@ -10565,6 +10565,7 @@ var init_pane_audit = __esm({
10565
10565
  init_ledger();
10566
10566
  init_paths();
10567
10567
  init_kill_tree();
10568
+ init_endreason();
10568
10569
  init_status();
10569
10570
  init_plan_status();
10570
10571
  init_pane_run();
@@ -18041,6 +18042,17 @@ var init_kg_triples = __esm({
18041
18042
  async function ensureObservationTables(pool) {
18042
18043
  if (_tablesEnsured) return;
18043
18044
  await pool.query(SCHEMA_SQL);
18045
+ const colCheck = await pool.query(
18046
+ `SELECT column_name FROM information_schema.columns
18047
+ WHERE table_name = 'pai_observations' AND column_name IN ('source', 'fidelity')`
18048
+ );
18049
+ const existing = new Set(colCheck.rows.map((r) => r.column_name));
18050
+ if (!existing.has("source")) {
18051
+ await pool.query("ALTER TABLE pai_observations ADD COLUMN IF NOT EXISTS source TEXT");
18052
+ }
18053
+ if (!existing.has("fidelity")) {
18054
+ await pool.query("ALTER TABLE pai_observations ADD COLUMN IF NOT EXISTS fidelity TEXT");
18055
+ }
18044
18056
  _tablesEnsured = true;
18045
18057
  }
18046
18058
  async function ensureSkillTelemetryTable(pool) {
@@ -18068,8 +18080,9 @@ async function storeObservation(pool, obs) {
18068
18080
  const result = await pool.query(
18069
18081
  `INSERT INTO pai_observations
18070
18082
  (session_id, project_id, project_slug, type, title, narrative,
18071
- tool_name, tool_input_summary, files_read, files_modified, concepts, content_hash)
18072
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10::jsonb, $11::jsonb, $12)
18083
+ tool_name, tool_input_summary, files_read, files_modified, concepts, content_hash,
18084
+ source, fidelity)
18085
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10::jsonb, $11::jsonb, $12, $13, $14)
18073
18086
  RETURNING id`,
18074
18087
  [
18075
18088
  obs.session_id,
@@ -18083,7 +18096,9 @@ async function storeObservation(pool, obs) {
18083
18096
  JSON.stringify(obs.files_read),
18084
18097
  JSON.stringify(obs.files_modified),
18085
18098
  JSON.stringify(obs.concepts),
18086
- hash
18099
+ hash,
18100
+ obs.source ?? null,
18101
+ obs.fidelity ?? null
18087
18102
  ]
18088
18103
  );
18089
18104
  return result.rows[0]?.id ?? null;
@@ -18125,7 +18140,7 @@ async function queryObservations(pool, opts = {}) {
18125
18140
  `SELECT id, session_id, project_id, project_slug, type, title, narrative,
18126
18141
  tool_name, tool_input_summary,
18127
18142
  files_read, files_modified, concepts,
18128
- content_hash, created_at
18143
+ content_hash, source, fidelity, created_at
18129
18144
  FROM pai_observations
18130
18145
  ${where}
18131
18146
  ORDER BY created_at DESC
@@ -18140,7 +18155,7 @@ async function queryRecentObservations(pool, projectId, limit) {
18140
18155
  `SELECT id, session_id, project_id, project_slug, type, title, narrative,
18141
18156
  tool_name, tool_input_summary,
18142
18157
  files_read, files_modified, concepts,
18143
- content_hash, created_at
18158
+ content_hash, source, fidelity, created_at
18144
18159
  FROM pai_observations
18145
18160
  WHERE project_id = $1
18146
18161
  ORDER BY created_at DESC
@@ -18155,7 +18170,7 @@ async function querySessionObservations(pool, sessionId) {
18155
18170
  `SELECT id, session_id, project_id, project_slug, type, title, narrative,
18156
18171
  tool_name, tool_input_summary,
18157
18172
  files_read, files_modified, concepts,
18158
- content_hash, created_at
18173
+ content_hash, source, fidelity, created_at
18159
18174
  FROM pai_observations
18160
18175
  WHERE session_id = $1
18161
18176
  ORDER BY created_at ASC`,
@@ -18325,6 +18340,8 @@ CREATE TABLE IF NOT EXISTS pai_observations (
18325
18340
  files_modified JSONB DEFAULT '[]'::jsonb,
18326
18341
  concepts JSONB DEFAULT '[]'::jsonb,
18327
18342
  content_hash TEXT,
18343
+ source TEXT,
18344
+ fidelity TEXT,
18328
18345
  created_at TIMESTAMPTZ DEFAULT NOW()
18329
18346
  );
18330
18347
 
@@ -24629,6 +24646,16 @@ var init_loopback_guard = __esm({
24629
24646
  }
24630
24647
  });
24631
24648
 
24649
+ // src/router/cockpit-auth.ts
24650
+ var init_cockpit_auth = __esm({
24651
+ "src/router/cockpit-auth.ts"() {
24652
+ "use strict";
24653
+ init_pai_home();
24654
+ init_loopback();
24655
+ init_timeouts();
24656
+ }
24657
+ });
24658
+
24632
24659
  // src/router/router.ts
24633
24660
  var init_router2 = __esm({
24634
24661
  "src/router/router.ts"() {
@@ -24647,6 +24674,7 @@ var init_router2 = __esm({
24647
24674
  init_local_instances();
24648
24675
  init_route_table();
24649
24676
  init_loopback_guard();
24677
+ init_cockpit_auth();
24650
24678
  }
24651
24679
  });
24652
24680