@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.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/{auto-route-B7Yu6Uam.mjs → auto-route-21GdHL70.mjs} +1 -1
- package/dist/{auto-route-CjjQqU1o.mjs → auto-route-C3S2W3Bc.mjs} +1 -1
- package/dist/{budget-Dod6rnA9.mjs → budget-BrloTOZW.mjs} +1 -1
- package/dist/{chain-7n9Gx56r.mjs → chain-D7PFur8Y.mjs} +2 -2
- package/dist/cli/index.mjs +70 -180
- package/dist/{cockpit-actions-DR9X514g.mjs → cockpit-actions-BfRMr2sm.mjs} +6 -6
- package/dist/{cockpit-actions-BBHeyEZI.mjs → cockpit-actions-OodH_vx_.mjs} +20 -18
- package/dist/{cockpit-memory--dnShXBA.mjs → cockpit-memory-teG9M8bG.mjs} +6 -6
- package/dist/config/defaults/config.yaml +6 -0
- package/dist/{controls-CPoeudou.mjs → controls-JLm6g0yP.mjs} +7 -5
- package/dist/daemon/index.mjs +18 -18
- package/dist/{daemon-DKdBNKfa.mjs → daemon-DeYImF1F.mjs} +18 -18
- package/dist/daemon-mcp/index.mjs +18 -18
- package/dist/{daemon-DPAo6WTa.mjs → daemon-nXanenDM.mjs} +21 -17
- package/dist/{detect-9CZNO0yf.mjs → detect-BLKsUOgG.mjs} +2 -2
- package/dist/{detect-D1ziFFfA.mjs → detect-Rrimh0xc.mjs} +3 -3
- package/dist/{embed-pass-DIWjA68P.mjs → embed-pass-e64AwDCa.mjs} +1 -1
- package/dist/{endreason-C577xIQz.mjs → endreason-BC-v05H_.mjs} +17 -3
- package/dist/{factory-DjAAsqr_.mjs → factory-B7nv75rC.mjs} +2 -2
- package/dist/{factory-Bsib-qDG.mjs → factory-Bs9imVMq.mjs} +2 -2
- package/dist/{fallback-DFlItw1Z.mjs → fallback-CBWpp2F2.mjs} +227 -5
- package/dist/{health-Dnym-qcT.mjs → health-C4FQmkJn.mjs} +2 -2
- package/dist/{helpers-BD6V-p9w.mjs → helpers-DSNK_1KP.mjs} +2 -2
- package/dist/{helpers-CMGjy_0N.mjs → helpers-Dz7mjTSw.mjs} +3 -3
- package/dist/hooks/capture-all-events.mjs +21 -2
- package/dist/hooks/capture-tool-output.mjs +21 -2
- package/dist/hooks/worker-pool-host.mjs +52 -9
- package/dist/hooks/worker-status-line.mjs +34 -6
- package/dist/{indexer-backend-Cll7le4P.mjs → indexer-backend-BdjeL4P8.mjs} +31 -228
- package/dist/{ipc-client-CaiGJnSM.mjs → ipc-client-CnYEF31R.mjs} +1 -1
- package/dist/{ipc-client-ZRmDKd5Z.mjs → ipc-client-DN7EvoVo.mjs} +1 -1
- package/dist/{kill-tree-EWYaEk5v.mjs → kill-tree-DleMNZX3.mjs} +2 -2
- package/dist/{kill-tree-BG7dRVtp.mjs → kill-tree-SV6hrIa3.mjs} +2 -2
- package/dist/{launch-Jh5NVeNe.mjs → launch-Di7N-J5X.mjs} +2 -2
- package/dist/{launch-CXVlZjHt.mjs → launch-DlZTKE3u.mjs} +2 -2
- package/dist/{main-config-ops-BEMpuhDz.mjs → main-config-ops-kTXR8Pyv.mjs} +1 -1
- package/dist/{main-resolver-3Ns1HPmS.mjs → main-resolver-yp_puSSb.mjs} +5 -5
- package/dist/{model-inventory-BFVSNFT3.mjs → model-inventory-h6Z9fN_o.mjs} +1 -1
- package/dist/{models-add-C1QIc1DA.mjs → models-add-TVni3A7V.mjs} +19 -19
- package/dist/{pane-audit-DKQt71s4.mjs → pane-audit-Lqy9N_Ki.mjs} +23 -5
- package/dist/{planner-BmFACdLa.mjs → planner-j77ndkBf.mjs} +11 -11
- package/dist/{postgres-BdBNkL_l.mjs → postgres-BK4FP9Zg.mjs} +1 -1
- package/dist/{postgres-C6ti4haA.mjs → postgres-DNIOQijl.mjs} +17 -7
- package/dist/{project-setup-jvfONJTQ.mjs → project-setup-CBRtFFh0.mjs} +15 -235
- package/dist/{providers-CblTFrhz.mjs → providers-C9QC0TtO.mjs} +2 -2
- package/dist/{providers-BeDiVEb-.mjs → providers-FvCLzFge.mjs} +2 -2
- package/dist/{query-feedback-BRfPtqki.mjs → query-feedback-DN52N9C5.mjs} +1 -1
- package/dist/{render-CoMvwMPs.mjs → render-B35bQTE-.mjs} +2 -2
- package/dist/{render-BYmQkuLd.mjs → render-C84BqQRC.mjs} +2 -2
- package/dist/{root-Xglu0OPD.mjs → root-C4oXlXS_.mjs} +7 -7
- package/dist/{root-FtJsxYnm.mjs → root-DD1xx_Vu.mjs} +12 -12
- package/dist/route-table-FEfDuhIV.mjs +15 -0
- package/dist/{routing-BAAST9vT.mjs → routing-DmkheCRH.mjs} +126 -8
- package/dist/{routing-CENt4HH1.mjs → routing-f2zulGwD.mjs} +1 -1
- package/dist/{run-CSC7mgPf.mjs → run-BwZKBJZZ.mjs} +25 -11
- package/dist/{scan-C_Y5W9py.mjs → scan-BBGvU5EB.mjs} +2 -2
- package/dist/{session-BeqAbNPd.mjs → session-BAhtXdxj.mjs} +11 -11
- package/dist/{session-8iqQDkqb.mjs → session-Dn5OJXlg.mjs} +20 -20
- package/dist/{session-scan-BJI_ZO8M.mjs → session-scan-B57dNuA4.mjs} +1 -1
- package/dist/{session-scan-W-gbwJHM.mjs → session-scan-DUn10MBd.mjs} +2 -2
- package/dist/session-usage-CPvXvSkw.mjs +222 -0
- package/dist/{sources-ESUkZaWv.mjs → sources-CiE3rZek.mjs} +1 -1
- package/dist/{viewer-CqozWW39.mjs → viewer-DEySE4c_.mjs} +6 -6
- package/dist/{viewer-mDXUNHNy.mjs → viewer-wfKnvnrI.mjs} +5 -5
- package/dist/{worker-config-DESkIjMF.mjs → worker-config-CO5FXLAT.mjs} +18 -18
- package/dist/{worker-config-CJhEMk19.mjs → worker-config-eY5Qy4vl.mjs} +318 -21
- package/dist/{zettelkasten-BzMm8REH.mjs → zettelkasten-_h_tUm2T.mjs} +1 -1
- package/dist/{zettelkasten-B_rRRRvD.mjs → zettelkasten-tpCRBYvH.mjs} +2 -2
- package/docs/cockpit.md +1 -0
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/pai-plugin.json +1 -1
- package/plugins/context-preservation/plugin.json +1 -1
- package/plugins/core/plugin.json +1 -1
- package/plugins/creative/plugin.json +1 -1
- package/plugins/observability/plugin.json +1 -1
- package/plugins/productivity/plugin.json +1 -1
- package/plugins/semantic-search/plugin.json +1 -1
- package/plugins/ui/plugin.json +1 -1
- package/plugins/zettelkasten/plugin.json +1 -1
- package/src/hooks/ts/capture-all-events.ts +15 -3
- package/src/hooks/ts/lib/raw-capture.ts +7 -0
- package/src/hooks/ts/post-tool-use/capture-tool-output.ts +13 -3
- package/dist/route-table-B7zpoypi.mjs +0 -15
|
@@ -7,7 +7,7 @@ import { E as worktreesStatsCached, l as formatWorktreesStats } from "./worktree
|
|
|
7
7
|
import { T as ownsPidCached, b as loadStatusesCached, g as isLive, v as loadStatus } from "./status-ggJ7lgVg.mjs";
|
|
8
8
|
import { C as describeMcp, _ as readPoolMetrics, f as poolMetricsPath, h as poolSettings, i as bundleBuildStamp, l as poolHostProbe, o as poolCarry, u as poolHostStale, v as readPoolState, x as warmOrCold } from "./pool-Dh-q3iJi.mjs";
|
|
9
9
|
import { a as ownerSessionName } from "./scope-D-h6F6DN.mjs";
|
|
10
|
-
import { $
|
|
10
|
+
import { $n as splitBySessionKind, $t as POLICY_MODES, E as budgetCache, Ft as applyPolicyToRoutes, Gt as routeText, Ht as resolveSessionArg, I as buildSpeed, It as clearSessionRoute, Kt as servingChip, L as percentile, Mt as isTailnetOrLocalName, Nt as loopbackRefusal, Sn as probeLoopback, Tt as duplicateModelIds, Ut as routeChoices, W as currentInventory, Wt as routeRows, Xn as markTimeOrdered, Xt as setSessionRoute, Yn as isTimeOrdered, Yt as setRoute, an as parseDuration, at as readInventoryInput, bn as localTierOrder, cn as writePolicy, en as clearPolicy, er as usageLogFile, fn as getProfile, in as onBattery, it as readInventory, mn as readProfiles, on as readPolicy, rn as findLocal, rt as processTreeRssMb, sn as routeRun, vn as currentLocalStatus, xn as pickLocal, zt as flowText } from "./routing-DmkheCRH.mjs";
|
|
11
11
|
import { C as stopLines, T as tierButtons, _ as readLocalConfig, a as LocalTierError, c as fetchServedModels, f as listensOnPort, g as planStart, h as parseServers, l as fmtRss, m as parseLocalConfig, u as healthUrl, v as resolveModelPath, x as stopCommandFor } from "./local-tiers-BE4jGaMa.mjs";
|
|
12
12
|
import { _ as buildSlots, a as buildsDir, g as waitReason, l as listBuildsSync, m as ticketKind, n as breakStaleMutex, x as testSlots } from "./caller-identity-yLFb7CKq.mjs";
|
|
13
13
|
import { f as sharedScheduler, o as readSchedFile, u as schedPlain } from "./scheduler-CtUyWJ3O.mjs";
|
|
@@ -16,25 +16,28 @@ import { a as instancesFromProcesses, d as reconcileInstances, i as instanceNumb
|
|
|
16
16
|
import { i as localStatusLine, s as readLocalStatus } from "./local-candidate-DLdwQt0s.mjs";
|
|
17
17
|
import { t as LOCAL_PROBE_TIMEOUT_MS } from "./timeouts-1MLooEBB.mjs";
|
|
18
18
|
import { n as OPT, t as CliError } from "./define-CjJsmq-6.mjs";
|
|
19
|
-
import { c as resolveFromModule } from "./postgres-
|
|
20
|
-
import { c as ensureProxyRunning, l as proxyPort, u as stopProxy } from "./launch-
|
|
21
|
-
import { a as colorEnabled, f as renderTable, n as readBudget, o as makeStyle } from "./health-
|
|
22
|
-
import { H as testProvider, o as sessionWorkersLine } from "./run-
|
|
23
|
-
import { U as planRows, V as loadPlanStatuses, i as localIdleLine, n as markControlsHeld, s as readLocalIdle, t as clickrControlsArgv, yt as psTable } from "./controls-
|
|
19
|
+
import { c as resolveFromModule } from "./postgres-DNIOQijl.mjs";
|
|
20
|
+
import { c as ensureProxyRunning, l as proxyPort, u as stopProxy } from "./launch-Di7N-J5X.mjs";
|
|
21
|
+
import { a as colorEnabled, f as renderTable, n as readBudget, o as makeStyle } from "./health-C4FQmkJn.mjs";
|
|
22
|
+
import { H as testProvider, o as sessionWorkersLine } from "./run-BwZKBJZZ.mjs";
|
|
23
|
+
import { U as planRows, V as loadPlanStatuses, i as localIdleLine, n as markControlsHeld, s as readLocalIdle, t as clickrControlsArgv, yt as psTable } from "./controls-JLm6g0yP.mjs";
|
|
24
|
+
import { w as parseDenialText } from "./render-C84BqQRC.mjs";
|
|
24
25
|
import { n as diskBundleMtime, r as ensurePoolHost } from "./pool-cli-BggU8GRp.mjs";
|
|
25
|
-
import { a as readAudit, d as loadPaneRunStatuses, f as paneRunRows } from "./pane-audit-
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
26
|
+
import { a as readAudit, d as loadPaneRunStatuses, f as paneRunRows } from "./pane-audit-Lqy9N_Ki.mjs";
|
|
27
|
+
import { i as fallbackStatusText, n as fallbackOn, r as fallbackStatus, t as fallbackOff } from "./fallback-CBWpp2F2.mjs";
|
|
28
|
+
import { b as useProvider, f as setClass, g as setWorkersEnabled, l as removeProvider, m as setProviderEnabled, p as setProviderCap, r as classTargetText, s as describeProviders, t as addProvider, v as unsetClass, y as updateProvider } from "./providers-C9QC0TtO.mjs";
|
|
29
|
+
import { A as cockpitMemoryPayload, B as cockpitLayout, N as liveMemoryDeps, O as embeddingRuntimeName, P as memoryJobProgress, S as runtimeJobProgress, V as cockpitProbeSettings, c as defaultDeps, g as readLabels, h as readCockpitJob, k as readRuntimeCommands, t as COCKPIT_ACTION_EVENT, v as runCockpitAction, x as runningRuntimeJobs } from "./cockpit-actions-OodH_vx_.mjs";
|
|
30
|
+
import { n as isRealUserPrompt, t as extractUserPromptText } from "./session-usage-CPvXvSkw.mjs";
|
|
31
|
+
import { appendFileSync, chmodSync, closeSync, copyFileSync, createReadStream, existsSync, fstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
30
32
|
import { createHash } from "node:crypto";
|
|
31
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
33
|
+
import { basename, dirname, isAbsolute, join, normalize, relative, resolve } from "node:path";
|
|
32
34
|
import chalk from "chalk";
|
|
33
35
|
import { homedir, loadavg } from "node:os";
|
|
34
36
|
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
35
37
|
import { fileURLToPath } from "node:url";
|
|
36
38
|
import { LineCounter, isMap, isScalar, parse, parseDocument } from "yaml";
|
|
37
39
|
import { connect, createServer } from "node:net";
|
|
40
|
+
import { createInterface } from "node:readline";
|
|
38
41
|
|
|
39
42
|
//#region src/cli/commands/providers.ts
|
|
40
43
|
/**
|
|
@@ -1469,6 +1472,7 @@ main.win .cbody>.tw{padding:6px 10px}.wt-auto>.sp{padding:6px 10px}
|
|
|
1469
1472
|
.wc-svg{overflow:hidden}.wc-svg svg{display:block}.wc-leg{overflow:hidden;padding:0 10px}main.win .cbody:has(.wt-svg,.wc-svg){overflow:hidden}
|
|
1470
1473
|
.wr{padding:6px 10px;min-width:0}.wr-top{display:flex;flex-wrap:wrap;gap:6px 10px;align-items:center}.wr-sel{flex:none}.wr .rtcls{font-size:11px}.wr-sess{margin-top:4px}.wr-sess .rtsess{display:flex;flex-wrap:wrap;gap:4px 8px;align-items:center;margin-left:12px}
|
|
1471
1474
|
.wmd{padding:6px 10px;min-width:0}.wmd-top{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.wmd-g{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:6px 10px;margin-top:6px}.wmdt{font-size:11px;color:var(--dim)}.wmdv{font-size:18px}.wmds{font-size:11px}.wmd-n{margin-top:4px}.wmd-i .linst{margin-top:3px}.wmd-in{font:inherit;min-width:120px}.wmd-e .eprow{padding:2px 0}
|
|
1475
|
+
.wr-row[data-sess]{cursor:pointer}.sd{margin-top:8px;border-top:1px solid var(--bd,rgba(128,128,128,.3));padding-top:6px}.sd-h{display:flex;flex-wrap:wrap;gap:4px 10px;align-items:center}.sd-h a{text-decoration:none}.sd-h a.on{font-weight:600;text-decoration:underline}.sd-x{margin-left:auto}
|
|
1472
1476
|
${winSelectCss()}
|
|
1473
1477
|
${winGridCss()}
|
|
1474
1478
|
`;
|
|
@@ -5147,12 +5151,16 @@ WCHX.speed={
|
|
|
5147
5151
|
resize:function(){}
|
|
5148
5152
|
};
|
|
5149
5153
|
function wtFit(ctx){fitTables(ctx.body)}
|
|
5150
|
-
/** Row-level disclosures: a click on a fold or "+N more" row stores its state and re-feeds the page's data. */
|
|
5154
|
+
/** Row-level disclosures: a click on a fold or "+N more" row stores its state and re-feeds the page's data; a click on a session row opens its detail panel (SESS_OPEN), a tab or close button changes it. */
|
|
5151
5155
|
function wtWire(ctx){
|
|
5152
5156
|
ctx.body.addEventListener("click",function(e){
|
|
5153
|
-
var t=e.target&&e.target.closest?e.target.closest("[data-wrfold],[data-sessmore]"):null;
|
|
5157
|
+
var t=e.target&&e.target.closest?e.target.closest("[data-wrfold],[data-sessmore],[data-sess],[data-sesstab],[data-sessclose]"):null;
|
|
5154
5158
|
if(!t||!ctx.body.contains(t))return;
|
|
5155
5159
|
e.preventDefault();
|
|
5160
|
+
var redraw=function(){Win.update("data",data)};
|
|
5161
|
+
if(t.hasAttribute("data-sess"))return sessDetailOpen(t.getAttribute("data-sess"),redraw);
|
|
5162
|
+
if(t.hasAttribute("data-sesstab")){SESS_TAB=t.getAttribute("data-sesstab");return redraw()}
|
|
5163
|
+
if(t.hasAttribute("data-sessclose")){SESS_OPEN=null;return redraw()}
|
|
5156
5164
|
if(t.hasAttribute("data-wrfold")){var f=wrFolded(),k=t.getAttribute("data-wrfold");if(f[k])delete f[k];else f[k]=1;try{localStorage.setItem(WR_FOLD_KEY,JSON.stringify(f))}catch(x){}}
|
|
5157
5165
|
else{try{localStorage.setItem(SESS_MORE_KEY,t.getAttribute("data-sessmore"))}catch(x){}}
|
|
5158
5166
|
Win.update("data",data);
|
|
@@ -5493,7 +5501,7 @@ function runHead(timeLab){
|
|
|
5493
5501
|
}
|
|
5494
5502
|
function runRow(o){
|
|
5495
5503
|
var pc=o.w>0?Math.max(2,Math.round(o.w/o.max*100)):0;
|
|
5496
|
-
return'<div class="wr-row" title="'+esc(o.full)+'"><i class="wr-dot wr-'+o.st+'" title="'+esc(o.dotTitle)+'"></i><span class="wr-lab">'+esc(o.lab)+'</span><span class="badge" title="'+esc((o.models||[]).join(", ")||"no model")+'">'+esc(o.model)+'</span><span class="wr-bar" title="tokens: '+fmt(o.w)+'"><i style="width:'+pc+'%"></i></span><span class="wr-n">'+(o.w?fmt(o.w):"–")+'</span><span class="wr-du">'+o.du+'</span><span class="wr-hit '+hitCls(o.hit)+'" title="cache hit">'+(o.hit==null?"–":pct(o.hit))+'</span><span class="wr-fl">'+(o.extra||"")+'</span></div>'
|
|
5504
|
+
return'<div class="wr-row"'+(o.sid?' data-sess="'+esc(o.sid)+'"':'')+' title="'+esc(o.full)+'"><i class="wr-dot wr-'+o.st+'" title="'+esc(o.dotTitle)+'"></i><span class="wr-lab">'+esc(o.lab)+'</span><span class="badge" title="'+esc((o.models||[]).join(", ")||"no model")+'">'+esc(o.model)+'</span><span class="wr-bar" title="tokens: '+fmt(o.w)+'"><i style="width:'+pc+'%"></i></span><span class="wr-n">'+(o.w?fmt(o.w):"–")+'</span><span class="wr-du">'+o.du+'</span><span class="wr-hit '+hitCls(o.hit)+'" title="cache hit">'+(o.hit==null?"–":pct(o.hit))+'</span><span class="wr-fl">'+(o.extra||"")+'</span></div>'
|
|
5497
5505
|
}
|
|
5498
5506
|
/** Sessions by tokens: one runRow per session (dot: error red, worker verdict colour, active in the last 5 min blue, else dim), rows past the top N folded into one expandable row (state in localStorage like folds). */
|
|
5499
5507
|
var SESS_MORE_KEY="pai.cockpit.sessmore";
|
|
@@ -5506,14 +5514,38 @@ function sessionsHtml(d){
|
|
|
5506
5514
|
var lab=s.name||s.session.slice(0,8);
|
|
5507
5515
|
var full=lab+" · "+s.session+(s.worker?" · worker "+s.worker+" · "+(s.verdict||"worker"):"")+"\n"+s.requests+" req · fresh in "+fmt(s.input+s.cacheWrite)+" · out "+fmt(s.output)+" · hit "+pct(s.hitRatio)+" · busts "+(s.busts||0)+" · errors "+(s.errors||0)+" · last "+ago(s.lastTs);
|
|
5508
5516
|
var ex=(s.busts?'<span class="chip warn" title="cache busts">'+s.busts+'</span>':'')+(s.errors?'<span class="chip bad" title="errors">'+s.errors+'</span>':'');
|
|
5509
|
-
return runRow({st:st,lab:lab,full:full,dotTitle:s.verdict||(recent?"active":"idle"),model:ms[0]||"–",models:ms,w:t,max:max,du:ago(s.lastTs),hit:s.requests?s.hitRatio:null,extra:ex});
|
|
5517
|
+
return runRow({sid:s.session,st:st,lab:lab,full:full,dotTitle:s.verdict||(recent?"active":"idle"),model:ms[0]||"–",models:ms,w:t,max:max,du:ago(s.lastTs),hit:s.requests?s.hitRatio:null,extra:ex});
|
|
5510
5518
|
}
|
|
5511
5519
|
var shown=open?all:all.slice(0,top),rest=all.slice(top),h=shown.map(row).join("");
|
|
5512
5520
|
if(rest.length){
|
|
5513
5521
|
var rt=rest.reduce(function(a,s){return a+tot(s)},0);
|
|
5514
5522
|
h+='<div class="wr-row wr-span"><a href="#" class="dim" data-sessmore="'+(open?"0":"1")+'">'+(open?"show fewer sessions":"+"+rest.length+" more sessions ("+fmt(rt)+" tokens)")+'</a></div>';
|
|
5515
5523
|
}
|
|
5516
|
-
return'<div class="wr-cq"><div class="wr-tbl">'+runHead("age")+'<div class="wr-group">'+h+'</div></div></div>';
|
|
5524
|
+
return'<div class="wr-cq"><div class="wr-tbl">'+runHead("age")+'<div class="wr-group">'+h+'</div></div></div>'+(typeof sessDetailHtml==="function"?sessDetailHtml():"");
|
|
5525
|
+
}
|
|
5526
|
+
/** Session drill-down state: the opened id, its tab and the fetched detail (survives the poll redraw; the panel is part of the sessions html). */
|
|
5527
|
+
var SESS_OPEN=null,SESS_TAB="timeline",SESS_DET=null;
|
|
5528
|
+
/** One tab of the opened session's detail, drawn with table(). */
|
|
5529
|
+
function sessDetailBody(d,tab){
|
|
5530
|
+
if(tab==="files")return table(["file","edits .n","~lines .n"],d.filesChanged.map(function(f){return[esc(f.path),f.edits,f.approxLinesChanged]}));
|
|
5531
|
+
if(tab==="skills")return table(["skill","time"],d.skills.map(function(k){return[esc(k.name),esc(ago(k.ts))]}));
|
|
5532
|
+
if(tab==="mcp")return table(["server","calls .n","failures .n"],d.mcp.map(function(m){return[esc(m.server),m.calls,m.failures]}));
|
|
5533
|
+
if(tab==="denials")return table(["time","tool","reason"],d.denials.map(function(x){return[esc(ago(x.ts)),esc(x.tool),esc(x.reason)]}));
|
|
5534
|
+
return table(["# .n","time","prompt","tools","edits","cmds .n"],d.turns.map(function(t){return[t.idx+1,esc(ago(t.ts)),esc(t.prompt),esc(t.tools.map(function(x){return x.name+"×"+x.count}).join(", ")),esc(t.edits.join(", ")),t.commands]}));
|
|
5535
|
+
}
|
|
5536
|
+
/** The detail panel under the sessions table: tabs Timeline / Files / Skills / MCP / Denials and a close button; empty when no session is open. */
|
|
5537
|
+
function sessDetailHtml(){
|
|
5538
|
+
if(!SESS_OPEN)return"";
|
|
5539
|
+
var d=SESS_DET&&SESS_DET.id===SESS_OPEN?SESS_DET:null,tabs=[["timeline","Timeline"],["files","Files"],["skills","Skills"],["mcp","MCP"],["denials","Denials"]];
|
|
5540
|
+
var head='<div class="sd-h"><b title="'+esc(SESS_OPEN)+'">'+esc(SESS_OPEN.slice(0,8))+'</b>'+tabs.map(function(t){return'<a href="#" data-sesstab="'+t[0]+'"'+(t[0]===SESS_TAB?' class="on"':'')+'>'+t[1]+(d&&t[0]!=="timeline"?" ("+(t[0]==="files"?d.filesChanged:t[0]==="skills"?d.skills:t[0]==="mcp"?d.mcp:d.denials).length+")":"")+'</a>'}).join("")+'<a href="#" class="dim sd-x" data-sessclose="1" title="close">close ✕</a></div>';
|
|
5541
|
+
var meta=d?'<div class="dim">'+esc(d.cwd)+(d.models.length?" · "+esc(d.models.join(", ")):"")+(d.turnCount>d.turns.length?" · last "+d.turns.length+" of "+d.turnCount+" turns":"")+'</div>':"";
|
|
5542
|
+
return'<div class="sd" data-sd="'+esc(SESS_OPEN)+'">'+head+meta+(d?sessDetailBody(d,SESS_TAB):'<span class="dim">'+(SESS_DET&&SESS_DET.error?esc(SESS_DET.error):"loading…")+'</span>')+'</div>';
|
|
5543
|
+
}
|
|
5544
|
+
/** Opens (or closes, on a second click) a session's detail and fetches it; the panel redraws with the sessions card. */
|
|
5545
|
+
function sessDetailOpen(id,redraw){
|
|
5546
|
+
if(SESS_OPEN===id){SESS_OPEN=null;redraw();return}
|
|
5547
|
+
SESS_OPEN=id;SESS_DET=null;redraw();
|
|
5548
|
+
fetch("/api/session/"+encodeURIComponent(id)+"/detail",{cache:"no-store"}).then(function(r){return r.json().then(function(j){if(!r.ok)throw new Error(j.error||"HTTP "+r.status);return j})}).then(function(j){if(SESS_OPEN===id){SESS_DET=j;redraw()}},function(e){if(SESS_OPEN===id){SESS_DET={id:id,error:String(e.message||e)};redraw()}});
|
|
5517
5549
|
}
|
|
5518
5550
|
/** Worker runs grouped under the spawning session: newest run first, groups by their newest run; weighted tokens per run on one scale shared by all rows. */
|
|
5519
5551
|
function workerGroups(ws,W){
|
|
@@ -6491,6 +6523,264 @@ function selectorChoices(workers, endpoints) {
|
|
|
6491
6523
|
];
|
|
6492
6524
|
}
|
|
6493
6525
|
|
|
6526
|
+
//#endregion
|
|
6527
|
+
//#region src/hooks/ts/lib/build-gate.ts
|
|
6528
|
+
/** Tiny reader for the two keys of builds.yaml: flow `[a, b]` or block `- a` lists. */
|
|
6529
|
+
function parseBuildsYaml(text) {
|
|
6530
|
+
const res = {
|
|
6531
|
+
doors: [],
|
|
6532
|
+
patterns: []
|
|
6533
|
+
};
|
|
6534
|
+
let key = null;
|
|
6535
|
+
const unq = (s) => s.trim().replace(/^(['"])(.*)\1$/, "$2");
|
|
6536
|
+
for (const raw of text.split("\n")) {
|
|
6537
|
+
const line = raw.replace(/(^|\s)#.*$/, "");
|
|
6538
|
+
if (!line.trim()) continue;
|
|
6539
|
+
const m = /^(doors|patterns)\s*:\s*(.*)$/.exec(line);
|
|
6540
|
+
if (m) {
|
|
6541
|
+
key = m[1];
|
|
6542
|
+
const rest = m[2].trim();
|
|
6543
|
+
if (rest.startsWith("[")) {
|
|
6544
|
+
res[key].push(...rest.replace(/^\[|\]$/g, "").split(",").map(unq).filter(Boolean));
|
|
6545
|
+
key = null;
|
|
6546
|
+
}
|
|
6547
|
+
continue;
|
|
6548
|
+
}
|
|
6549
|
+
const li = /^\s*-\s+(.*)$/.exec(line);
|
|
6550
|
+
if (li && key) res[key].push(unq(li[1]));
|
|
6551
|
+
else if (!/^\s/.test(line)) key = null;
|
|
6552
|
+
}
|
|
6553
|
+
return res;
|
|
6554
|
+
}
|
|
6555
|
+
/** The nearest `.pai/builds.yaml` at or above `cwd`. */
|
|
6556
|
+
function loadBuildsConfig(cwd) {
|
|
6557
|
+
let dir = resolve(cwd);
|
|
6558
|
+
for (;;) {
|
|
6559
|
+
const f = join(dir, ".pai", "builds.yaml");
|
|
6560
|
+
if (existsSync(f)) try {
|
|
6561
|
+
return {
|
|
6562
|
+
root: dir,
|
|
6563
|
+
...parseBuildsYaml(readFileSync(f, "utf8"))
|
|
6564
|
+
};
|
|
6565
|
+
} catch {
|
|
6566
|
+
return null;
|
|
6567
|
+
}
|
|
6568
|
+
const up = dirname(dir);
|
|
6569
|
+
if (up === dir) return null;
|
|
6570
|
+
dir = up;
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
|
|
6574
|
+
//#endregion
|
|
6575
|
+
//#region src/hooks/ts/lib/loop-guard.ts
|
|
6576
|
+
const LOOP_MAX_REPEATS = 4;
|
|
6577
|
+
/** Consecutive same-template read-only Bash calls without a worktree change after which the next one is denied (env PAI_LOOP_READ_STREAK overrides). */
|
|
6578
|
+
const LOOP_READ_STREAK = Number(process.env.PAI_LOOP_READ_STREAK) > 0 ? Number(process.env.PAI_LOOP_READ_STREAK) : 15;
|
|
6579
|
+
const SELF = "If your part is done, commit and write your R report. If you are stuck, report the blocker in R x= (or use pai worker help if available).";
|
|
6580
|
+
const LOOP_DENY_REASON = `loop guard: you ran this command ${LOOP_MAX_REPEATS} times with the same result and nothing changed. Stop repeating it. ${SELF}`;
|
|
6581
|
+
/** Total polling time per command key after which a "still running" helper is treated as an ordinary repeat again. */
|
|
6582
|
+
const WAIT_CAP_MS = 45 * 6e4;
|
|
6583
|
+
/** Untracked files up to this size are hashed by content; larger ones by size + mtime. */
|
|
6584
|
+
const FP_FULL_READ_BYTES = 256 * 1024;
|
|
6585
|
+
/** Content bytes read per fingerprint across all untracked files; past it files are hashed by size + mtime. */
|
|
6586
|
+
const FP_TOTAL_BYTES = 4 * 1024 * 1024;
|
|
6587
|
+
const SECRET_LINE = /pass(?:word|wd)|secret|token|api[_-]?key|credential|authorization|bearer|private key|^\s*(?:export\s+)?[A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASS\w*|CRED\w*|AUTH\w*)\s*=|\b(?:sk|ghp|gho|xox[bap])[-_][A-Za-z0-9_-]{16,}|\b[A-Za-z0-9+]{40,}={0,2}(?![\w/])|\b[a-z][a-z0-9+.-]*:\/\/[^\s\/:@]+:[^\s\/@]+@|\b(?:AKIA|ASIA|AGPA|AIDA|AROA)[A-Z0-9]{16}\b|\bgh[pousr]_[A-Za-z0-9]{20,}/i;
|
|
6588
|
+
const MASKED_LINE = "[line masked: looks like a secret]";
|
|
6589
|
+
/** Result text with lines that look like secrets replaced by a marker. */
|
|
6590
|
+
function maskSecrets(text) {
|
|
6591
|
+
return text.split("\n").map((l) => SECRET_LINE.test(l) ? MASKED_LINE : l).join("\n");
|
|
6592
|
+
}
|
|
6593
|
+
|
|
6594
|
+
//#endregion
|
|
6595
|
+
//#region src/router/session-detail.ts
|
|
6596
|
+
/**
|
|
6597
|
+
* session-detail.ts — one session's drill-down for the cockpit: turns, files
|
|
6598
|
+
* changed, skills, MCP calls and hook denials, read from the Claude Code
|
|
6599
|
+
* transcript (<projects dir>/<encoded cwd>/<session id>.jsonl) by streaming it
|
|
6600
|
+
* line by line. Prompt text is secret-masked.
|
|
6601
|
+
*/
|
|
6602
|
+
/** Session ids the endpoint accepts: no path separators, no dots. */
|
|
6603
|
+
const SESSION_ID_RE = /^[A-Za-z0-9-]{8,64}$/;
|
|
6604
|
+
const PROMPT_CHARS = 300;
|
|
6605
|
+
/** The transcript file of a session id, or null. */
|
|
6606
|
+
function findTranscript(id, projectsDir = claudeProjectsDir()) {
|
|
6607
|
+
if (!SESSION_ID_RE.test(id) || !existsSync(projectsDir)) return null;
|
|
6608
|
+
let dirs = [];
|
|
6609
|
+
try {
|
|
6610
|
+
dirs = readdirSync(projectsDir);
|
|
6611
|
+
} catch {
|
|
6612
|
+
return null;
|
|
6613
|
+
}
|
|
6614
|
+
for (const d of dirs) {
|
|
6615
|
+
const f = join(projectsDir, d, `${id}.jsonl`);
|
|
6616
|
+
if (existsSync(f)) return f;
|
|
6617
|
+
}
|
|
6618
|
+
return null;
|
|
6619
|
+
}
|
|
6620
|
+
const lineCount = (s) => typeof s === "string" && s ? s.split("\n").length : 0;
|
|
6621
|
+
/** Text of a tool_result's content: a string or the text blocks joined. */
|
|
6622
|
+
function resultText(c) {
|
|
6623
|
+
if (typeof c === "string") return c;
|
|
6624
|
+
if (Array.isArray(c)) return c.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join("\n");
|
|
6625
|
+
return "";
|
|
6626
|
+
}
|
|
6627
|
+
/** Lines written by one Edit / Write / MultiEdit input. */
|
|
6628
|
+
function changedLines(name, input) {
|
|
6629
|
+
if (name === "Write") return lineCount(input.content);
|
|
6630
|
+
if (name === "Edit") return lineCount(input.new_string);
|
|
6631
|
+
if (name === "MultiEdit" && Array.isArray(input.edits)) return input.edits.reduce((a, e) => a + lineCount(e?.new_string), 0);
|
|
6632
|
+
return 0;
|
|
6633
|
+
}
|
|
6634
|
+
/** Skill name a user line announces ("/name" command tag or "Launching skill: name"), or null. */
|
|
6635
|
+
function loadedSkill(text) {
|
|
6636
|
+
const cmd = text.match(/<command-name>\/?([^<\s]+)<\/command-name>/);
|
|
6637
|
+
if (cmd) return cmd[1];
|
|
6638
|
+
const l = text.match(/^Launching skill: (\S+)/);
|
|
6639
|
+
return l ? l[1] : null;
|
|
6640
|
+
}
|
|
6641
|
+
/** Streams the session's transcript into a SessionDetail; null when the id has no transcript. */
|
|
6642
|
+
async function sessionDetail(id, opts = {}) {
|
|
6643
|
+
const file = findTranscript(id, opts.projectsDir);
|
|
6644
|
+
if (!file) return null;
|
|
6645
|
+
const maxTurns = Math.max(1, opts.maxTurns ?? cockpitLayout().sessionDetailTurns);
|
|
6646
|
+
const out = {
|
|
6647
|
+
id,
|
|
6648
|
+
cwd: "",
|
|
6649
|
+
models: [],
|
|
6650
|
+
turns: [],
|
|
6651
|
+
turnCount: 0,
|
|
6652
|
+
filesChanged: [],
|
|
6653
|
+
skills: [],
|
|
6654
|
+
mcp: [],
|
|
6655
|
+
denials: []
|
|
6656
|
+
};
|
|
6657
|
+
const models = /* @__PURE__ */ new Set();
|
|
6658
|
+
const files = /* @__PURE__ */ new Map();
|
|
6659
|
+
const mcp = /* @__PURE__ */ new Map();
|
|
6660
|
+
const toolOf = /* @__PURE__ */ new Map();
|
|
6661
|
+
const mcpOf = /* @__PURE__ */ new Map();
|
|
6662
|
+
let cur = null;
|
|
6663
|
+
const close = () => {
|
|
6664
|
+
if (!cur) return;
|
|
6665
|
+
cur.turn.tools = [...cur.tools].map(([name, count]) => ({
|
|
6666
|
+
name,
|
|
6667
|
+
count
|
|
6668
|
+
}));
|
|
6669
|
+
cur.turn.edits = [...cur.edits];
|
|
6670
|
+
out.turns.push(cur.turn);
|
|
6671
|
+
if (out.turns.length > maxTurns) out.turns.shift();
|
|
6672
|
+
cur = null;
|
|
6673
|
+
};
|
|
6674
|
+
const rl = createInterface({
|
|
6675
|
+
input: createReadStream(file, { encoding: "utf8" }),
|
|
6676
|
+
crlfDelay: Infinity
|
|
6677
|
+
});
|
|
6678
|
+
for await (const raw of rl) {
|
|
6679
|
+
if (!raw) continue;
|
|
6680
|
+
let obj;
|
|
6681
|
+
try {
|
|
6682
|
+
obj = JSON.parse(raw);
|
|
6683
|
+
} catch {
|
|
6684
|
+
continue;
|
|
6685
|
+
}
|
|
6686
|
+
const ts = obj.timestamp ?? "";
|
|
6687
|
+
if (!out.cwd && typeof obj.cwd === "string") out.cwd = obj.cwd;
|
|
6688
|
+
const content = obj.message?.content;
|
|
6689
|
+
if (obj.type === "user") {
|
|
6690
|
+
if (isRealUserPrompt(obj)) {
|
|
6691
|
+
close();
|
|
6692
|
+
const text = extractUserPromptText(obj);
|
|
6693
|
+
const skill = loadedSkill(text);
|
|
6694
|
+
if (skill) out.skills.push({
|
|
6695
|
+
name: skill,
|
|
6696
|
+
ts
|
|
6697
|
+
});
|
|
6698
|
+
cur = {
|
|
6699
|
+
turn: {
|
|
6700
|
+
idx: out.turnCount++,
|
|
6701
|
+
ts,
|
|
6702
|
+
prompt: maskSecrets(text).slice(0, PROMPT_CHARS),
|
|
6703
|
+
tools: [],
|
|
6704
|
+
edits: [],
|
|
6705
|
+
commands: 0
|
|
6706
|
+
},
|
|
6707
|
+
tools: /* @__PURE__ */ new Map(),
|
|
6708
|
+
edits: /* @__PURE__ */ new Set()
|
|
6709
|
+
};
|
|
6710
|
+
continue;
|
|
6711
|
+
}
|
|
6712
|
+
const blocks = Array.isArray(content) ? content : [];
|
|
6713
|
+
for (const b of blocks) {
|
|
6714
|
+
if (b.type === "text" && typeof b.text === "string" && obj.isMeta) {
|
|
6715
|
+
const skill = loadedSkill(b.text);
|
|
6716
|
+
if (skill) out.skills.push({
|
|
6717
|
+
name: skill,
|
|
6718
|
+
ts
|
|
6719
|
+
});
|
|
6720
|
+
}
|
|
6721
|
+
if (b.type !== "tool_result") continue;
|
|
6722
|
+
const text = resultText(b.content);
|
|
6723
|
+
const server = b.tool_use_id ? mcpOf.get(b.tool_use_id) : void 0;
|
|
6724
|
+
if (server && b.is_error) mcp.get(server).failures++;
|
|
6725
|
+
const d = parseDenialText(text, b.tool_use_id && toolOf.get(b.tool_use_id) || "?");
|
|
6726
|
+
if (d) out.denials.push({
|
|
6727
|
+
ts,
|
|
6728
|
+
tool: d.tool,
|
|
6729
|
+
reason: maskSecrets(d.reason).slice(0, 200)
|
|
6730
|
+
});
|
|
6731
|
+
}
|
|
6732
|
+
continue;
|
|
6733
|
+
}
|
|
6734
|
+
if (obj.type !== "assistant") continue;
|
|
6735
|
+
if (obj.message?.model) models.add(obj.message.model);
|
|
6736
|
+
if (!Array.isArray(content)) continue;
|
|
6737
|
+
for (const b of content) {
|
|
6738
|
+
if (b.type !== "tool_use" || !b.name) continue;
|
|
6739
|
+
const input = b.input ?? {};
|
|
6740
|
+
if (b.id) toolOf.set(b.id, b.name);
|
|
6741
|
+
if (cur) cur.tools.set(b.name, (cur.tools.get(b.name) ?? 0) + 1);
|
|
6742
|
+
if (b.name === "Bash" && cur) cur.turn.commands++;
|
|
6743
|
+
if (b.name === "Skill" && typeof input.skill === "string") out.skills.push({
|
|
6744
|
+
name: input.skill,
|
|
6745
|
+
ts
|
|
6746
|
+
});
|
|
6747
|
+
if (b.name.startsWith("mcp__")) {
|
|
6748
|
+
const server = b.name.split("__")[1];
|
|
6749
|
+
if (server) {
|
|
6750
|
+
const m = mcp.get(server) ?? {
|
|
6751
|
+
calls: 0,
|
|
6752
|
+
failures: 0
|
|
6753
|
+
};
|
|
6754
|
+
m.calls++;
|
|
6755
|
+
mcp.set(server, m);
|
|
6756
|
+
if (b.id) mcpOf.set(b.id, server);
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
if ((b.name === "Edit" || b.name === "Write" || b.name === "MultiEdit") && typeof input.file_path === "string") {
|
|
6760
|
+
const f = files.get(input.file_path) ?? {
|
|
6761
|
+
edits: 0,
|
|
6762
|
+
approxLinesChanged: 0
|
|
6763
|
+
};
|
|
6764
|
+
f.edits++;
|
|
6765
|
+
f.approxLinesChanged += changedLines(b.name, input);
|
|
6766
|
+
files.set(input.file_path, f);
|
|
6767
|
+
cur?.edits.add(input.file_path);
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
}
|
|
6771
|
+
close();
|
|
6772
|
+
out.models = [...models];
|
|
6773
|
+
out.filesChanged = [...files].map(([path, f]) => ({
|
|
6774
|
+
path,
|
|
6775
|
+
...f
|
|
6776
|
+
})).sort((a, b) => b.approxLinesChanged - a.approxLinesChanged);
|
|
6777
|
+
out.mcp = [...mcp].map(([server, m]) => ({
|
|
6778
|
+
server,
|
|
6779
|
+
...m
|
|
6780
|
+
})).sort((a, b) => b.calls - a.calls);
|
|
6781
|
+
return out;
|
|
6782
|
+
}
|
|
6783
|
+
|
|
6494
6784
|
//#endregion
|
|
6495
6785
|
//#region src/router/cockpit-layout-default.ts
|
|
6496
6786
|
/**
|
|
@@ -7855,7 +8145,8 @@ function cockpitBuildId() {
|
|
|
7855
8145
|
*/
|
|
7856
8146
|
function handleCockpit(req, res, limits, readRows = cachedRows(), sessionMap = loadSessionMap, system, workerWindows = loadWorkerWindows, readPool = cachedPoolCarry(), readPoolPanel = sharedPoolPanel, queueLogDir = workersLogDir) {
|
|
7857
8147
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
7858
|
-
|
|
8148
|
+
const detail = url.pathname.match(/^\/api\/session\/([^/]*)\/detail$/);
|
|
8149
|
+
if (!detail && url.pathname !== "/cockpit" && !url.pathname.startsWith("/cockpit/")) return false;
|
|
7859
8150
|
const send = (status, type, body) => {
|
|
7860
8151
|
res.writeHead(status, {
|
|
7861
8152
|
"content-type": type,
|
|
@@ -7864,6 +8155,12 @@ function handleCockpit(req, res, limits, readRows = cachedRows(), sessionMap = l
|
|
|
7864
8155
|
});
|
|
7865
8156
|
res.end(body);
|
|
7866
8157
|
};
|
|
8158
|
+
if (detail) {
|
|
8159
|
+
if (req.method !== "GET") return send(405, "text/plain", "GET only"), true;
|
|
8160
|
+
if (!SESSION_ID_RE.test(detail[1])) return send(400, "application/json", "{\"error\":\"invalid session id\"}"), true;
|
|
8161
|
+
sessionDetail(detail[1]).then((d) => d ? send(200, "application/json", JSON.stringify(d)) : send(404, "application/json", "{\"error\":\"no such session\"}"), (e) => send(500, "application/json", JSON.stringify({ error: e.message })));
|
|
8162
|
+
return true;
|
|
8163
|
+
}
|
|
7867
8164
|
if (req.method === "POST" && (url.pathname === "/cockpit/queue/next" || url.pathname === "/cockpit/queue/front" || url.pathname === "/cockpit/queue/up" || url.pathname === "/cockpit/queue/move")) {
|
|
7868
8165
|
if (!sameOriginAction(req)) return send(403, "application/json", JSON.stringify({
|
|
7869
8166
|
ok: false,
|
|
@@ -10271,7 +10568,7 @@ async function stopInstance(cfg, target, opts = {}, deps = {}) {
|
|
|
10271
10568
|
*/
|
|
10272
10569
|
async function cliInflight(cfg, deps = {}) {
|
|
10273
10570
|
const [{ engineInflight }, { readSchedFile }, { instanceEngineKey }] = await Promise.all([
|
|
10274
|
-
import("./cockpit-actions-
|
|
10571
|
+
import("./cockpit-actions-BfRMr2sm.mjs"),
|
|
10275
10572
|
import("./scheduler-dmHXoRvm.mjs"),
|
|
10276
10573
|
import("./local-instances-ZeRkkuS5.mjs")
|
|
10277
10574
|
]);
|
|
@@ -10867,5 +11164,5 @@ const WORKER_CONFIG_COMMANDS = [
|
|
|
10867
11164
|
];
|
|
10868
11165
|
|
|
10869
11166
|
//#endregion
|
|
10870
|
-
export { exportDefaultLayout as $, workerMcpCommand as A, mergeSettings as B, workerBenchCommand as C, workerFallbackCommand as D, workerControlsCommand as E, workerProvidersCommand as F, cachedRows as G, canBind as H, workerProxyCommand as I, handleCockpit as J, cachedSessionMap as K, workerRolesCommand as L, workerOnCommand as M, workerPolicyCommand as N, workerInstallCommand as O, workerPoolCommand as P, warmCockpit as Q, workerRouteCommand as R, stopInstance as S, workerConfigCommand as T, sizeOnDiskGb as U, allocatePort as V, SystemMonitor as W, setEndpointHealth as X, setActionHooks as Y, setRuntimeSampler as Z, portOpen as _, collectEnv as a,
|
|
10871
|
-
//# sourceMappingURL=worker-config-
|
|
11167
|
+
export { exportDefaultLayout as $, workerMcpCommand as A, mergeSettings as B, workerBenchCommand as C, workerFallbackCommand as D, workerControlsCommand as E, workerProvidersCommand as F, cachedRows as G, canBind as H, workerProxyCommand as I, handleCockpit as J, cachedSessionMap as K, workerRolesCommand as L, workerOnCommand as M, workerPolicyCommand as N, workerInstallCommand as O, workerPoolCommand as P, warmCockpit as Q, workerRouteCommand as R, stopInstance as S, workerConfigCommand as T, sizeOnDiskGb as U, allocatePort as V, SystemMonitor as W, setEndpointHealth as X, setActionHooks as Y, setRuntimeSampler as Z, portOpen as _, collectEnv as a, withWorkerAttribution as at, startTier as b, currentInstances as c, saveRegistry as ct, isUp as d, loadBuildsConfig as et, killInstance as f, portListeners as g, parseCostTier as h, cliInflight as i, sessionDisplayName as it, workerOffCommand as j, workerLocalCommand as k, fail as l, providersCommand as lt, listServers as m, WORKER_CONFIG_COMMANDS as n, loadSessionMap as nt, collectTags as o, defaultRegistryCtx as ot, launchDetached as p, cachedWorkerWindows as q, buildClassTarget as r, loadWorkerWindows as rt, controlsArgv as s, hashText as st, PROVIDER_EDIT_COMMANDS as t, createEndpointHealth as tt, healthOk as u, serverLine as v, workerClassesCommand as w, stopAll as x, startInstance as y, installWorkers as z };
|
|
11168
|
+
//# sourceMappingURL=worker-config-eY5Qy4vl.mjs.map
|
|
@@ -1213,4 +1213,4 @@ async function zettelCommunities(backend, opts) {
|
|
|
1213
1213
|
|
|
1214
1214
|
//#endregion
|
|
1215
1215
|
export { zettelThemes as a, zettelExplore as c, zettelHealth as i, zettelGodNotes as n, zettelConverse as o, zettelSuggest as r, zettelSurprise as s, zettelCommunities as t };
|
|
1216
|
-
//# sourceMappingURL=zettelkasten-
|
|
1216
|
+
//# sourceMappingURL=zettelkasten-_h_tUm2T.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./config-Bu1_vbY8.mjs";
|
|
2
2
|
import "./embedding-gate-6rZrWRXa.mjs";
|
|
3
3
|
import "./embeddings-BVq3esBF.mjs";
|
|
4
|
-
import { r as saveQueryResult } from "./query-feedback-
|
|
5
|
-
import { a as zettelThemes, c as zettelExplore, i as zettelHealth, n as zettelGodNotes, o as zettelConverse, r as zettelSuggest, s as zettelSurprise, t as zettelCommunities } from "./zettelkasten-
|
|
4
|
+
import { r as saveQueryResult } from "./query-feedback-DN52N9C5.mjs";
|
|
5
|
+
import { a as zettelThemes, c as zettelExplore, i as zettelHealth, n as zettelGodNotes, o as zettelConverse, r as zettelSuggest, s as zettelSurprise, t as zettelCommunities } from "./zettelkasten-_h_tUm2T.mjs";
|
|
6
6
|
|
|
7
7
|
export { zettelConverse, zettelExplore, zettelHealth, zettelSuggest, zettelSurprise, zettelThemes };
|
package/docs/cockpit.md
CHANGED
|
@@ -169,6 +169,7 @@ An empty list shows every section. Unknown ids are ignored, and if all are unkno
|
|
|
169
169
|
| `dailyDays` | 14 | Days in Tokens per day per model |
|
|
170
170
|
| `sessionsRows` | 20 | Rows in Sessions by tokens |
|
|
171
171
|
| `sessionsTopRows` | 10 | Sessions shown before the rest collapse into one "+N more sessions" row |
|
|
172
|
+
| `sessionDetailTurns` | 200 | Turns kept in the per-session drill-down (click a session row) |
|
|
172
173
|
| `sessionNameMaxPct` | 35 | Session-name column cap, percent of the table width |
|
|
173
174
|
| `workerRows` | 15 | Rows in Worker runs |
|
|
174
175
|
| `lowRequestMin` | 5 | Models under this many requests become chips, not rings |
|
package/gemini-extension.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tekmidian/pai",
|
|
3
3
|
"displayName": "PAI Knowledge OS",
|
|
4
4
|
"description": "Personal AI Infrastructure — persistent memory and session continuity for AI coding assistants",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.4",
|
|
6
6
|
"author": "Matthias Nott",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"homepage": "https://github.com/mnott/PAI",
|
package/package.json
CHANGED
package/pai-plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "@tekmidian/pai",
|
|
4
4
|
"displayName": "PAI Knowledge OS",
|
|
5
5
|
"description": "Personal AI Infrastructure — persistent memory, session continuity, and knowledge graph",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.4",
|
|
7
7
|
"author": "Matthias Nott",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"homepage": "https://github.com/mnott/PAI",
|
package/plugins/core/plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "pai-productivity",
|
|
3
3
|
"displayName": "PAI Productivity",
|
|
4
4
|
"description": "Planning, journaling, reviewing, research, sharing, and cross-session task routing",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.4",
|
|
6
6
|
"hooks": "./hooks/hooks.json",
|
|
7
7
|
"skills": ["./skills/"]
|
|
8
8
|
}
|
package/plugins/ui/plugin.json
CHANGED
|
@@ -11,7 +11,7 @@ import { join } from 'path';
|
|
|
11
11
|
import { historyDir, agentSessionsPath } from './lib/pai-paths.js';
|
|
12
12
|
import { enrichEventWithAgentMetadata, isAgentSpawningCall } from './lib/metadata-extraction.js';
|
|
13
13
|
import { isProbeSession } from './lib/project-utils.js';
|
|
14
|
-
import { appendRawCapture, redactDeep } from './lib/raw-capture.js';
|
|
14
|
+
import { appendRawCapture, captureEventId, redactDeep } from './lib/raw-capture.js';
|
|
15
15
|
import { writeJsonAtomic } from '../../config/json-store.js';
|
|
16
16
|
|
|
17
17
|
interface HookEvent {
|
|
@@ -21,6 +21,9 @@ interface HookEvent {
|
|
|
21
21
|
payload: Record<string, any>;
|
|
22
22
|
timestamp: number;
|
|
23
23
|
timestamp_local: string;
|
|
24
|
+
source: string;
|
|
25
|
+
fidelity: string;
|
|
26
|
+
eventId: string;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
// Get local timestamp using system timezone
|
|
@@ -154,13 +157,22 @@ async function main() {
|
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
// Create base event object
|
|
160
|
+
const timestamp = Date.now();
|
|
157
161
|
let event: HookEvent = {
|
|
158
162
|
source_app: agentName,
|
|
159
163
|
session_id: hookData.session_id || 'main',
|
|
160
164
|
hook_event_type: eventType,
|
|
161
165
|
payload: redactDeep(hookData),
|
|
162
|
-
timestamp
|
|
163
|
-
timestamp_local: getLocalTimestamp()
|
|
166
|
+
timestamp,
|
|
167
|
+
timestamp_local: getLocalTimestamp(),
|
|
168
|
+
source: 'hook',
|
|
169
|
+
fidelity: 'observed',
|
|
170
|
+
eventId: captureEventId({
|
|
171
|
+
sessionId: hookData.session_id,
|
|
172
|
+
toolUseId: hookData.tool_use_id,
|
|
173
|
+
event: eventType,
|
|
174
|
+
ts: String(timestamp)
|
|
175
|
+
})
|
|
164
176
|
};
|
|
165
177
|
|
|
166
178
|
// Enrich with agent instance metadata if this is a Task tool call
|
|
@@ -5,9 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
import { appendFileSync, chmodSync, existsSync, mkdirSync, renameSync, statSync } from 'fs';
|
|
7
7
|
import { maskSecrets } from './loop-guard.js';
|
|
8
|
+
import { sha256 } from '../../../utils/hash.js';
|
|
8
9
|
|
|
9
10
|
export const RAW_CAPTURE_MAX_BYTES = 10 * 1024 * 1024;
|
|
10
11
|
|
|
12
|
+
/** Deterministic 16-hex id for a captured event: sha256 of the joined parts. */
|
|
13
|
+
export function captureEventId(parts: { sessionId?: string; toolUseId?: string; event?: string; ts?: string }): string {
|
|
14
|
+
const joined = `${parts.sessionId ?? ""}|${parts.toolUseId ?? ""}|${parts.event ?? ""}|${parts.ts ?? ""}`;
|
|
15
|
+
return sha256(joined).slice(0, 16);
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
/** Deep copy with every string value passed through the loop guard's secret-line masking. */
|
|
12
19
|
export function redactDeep<T>(value: T): T {
|
|
13
20
|
if (typeof value === 'string') return maskSecrets(value) as unknown as T;
|