@tekmidian/pai 0.60.0 → 0.61.0
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/dist/{chain-DFlzAfDE.mjs → chain-C5oeyUg4.mjs} +4 -4
- package/dist/{chain-DFlzAfDE.mjs.map → chain-C5oeyUg4.mjs.map} +1 -1
- package/dist/cli/index.mjs +12 -12
- package/dist/cli/program.mjs +12 -12
- package/dist/{config-msER8xj_.mjs → config-CQCmH_gV.mjs} +2 -1
- package/dist/config-CQCmH_gV.mjs.map +1 -0
- package/dist/{config-GceCMUxa.mjs → config-DjXq_W42.mjs} +1 -1
- package/dist/{context-handover-cache-DF0xWVB0.mjs → context-handover-cache-BdtJZpgv.mjs} +4 -4
- package/dist/{context-handover-cache-DF0xWVB0.mjs.map → context-handover-cache-BdtJZpgv.mjs.map} +1 -1
- package/dist/daemon/index.mjs +11 -11
- package/dist/{daemon-DArRKBlk.mjs → daemon-DSsIEqzW.mjs} +26 -19
- package/dist/{daemon-DArRKBlk.mjs.map → daemon-DSsIEqzW.mjs.map} +1 -1
- package/dist/daemon-MoFF4oeg.mjs +19 -0
- package/dist/daemon-mcp/index.mjs +9 -11
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{factory-D6I2jmjQ.mjs → factory-DW7BCJJT.mjs} +1 -1
- package/dist/{factory-ChfAvCzO.mjs → factory-EUUhSSoq.mjs} +5 -6
- package/dist/factory-EUUhSSoq.mjs.map +1 -0
- package/dist/{fallback-B7VEMMRn.mjs → fallback-DDbJVaR8.mjs} +12 -14
- package/dist/{fallback-B7VEMMRn.mjs.map → fallback-DDbJVaR8.mjs.map} +1 -1
- package/dist/hooks/block-sleep-poll.mjs.map +2 -2
- package/dist/hooks/context-compression-hook.mjs.map +1 -1
- package/dist/hooks/post-compact-inject.mjs.map +1 -1
- package/dist/hooks/route-agents-to-worker.mjs +5 -5
- package/dist/hooks/route-agents-to-worker.mjs.map +3 -3
- package/dist/hooks/security-validator.mjs +60 -60
- package/dist/hooks/security-validator.mjs.map +3 -3
- package/dist/hooks/stop-hook.mjs +15 -15
- package/dist/hooks/stop-hook.mjs.map +3 -3
- package/dist/hooks/update-tab-on-action.mjs +14 -12
- package/dist/hooks/update-tab-on-action.mjs.map +3 -3
- package/dist/hooks/update-tab-titles.mjs +13 -9
- package/dist/hooks/update-tab-titles.mjs.map +3 -3
- package/dist/hooks/whisper-rules.mjs +2 -2
- package/dist/hooks/whisper-rules.mjs.map +3 -3
- package/dist/hooks/worker-proxy.mjs +5 -5
- package/dist/hooks/worker-proxy.mjs.map +3 -3
- package/dist/hooks/worker-status-line.mjs +5 -5
- package/dist/hooks/worker-status-line.mjs.map +3 -3
- package/dist/hooks/worker-supervision.mjs +5 -5
- package/dist/hooks/worker-supervision.mjs.map +3 -3
- package/dist/main-resolver-CxOsxtOr.mjs +7 -0
- package/dist/{main-resolver-BqTjt-GL.mjs → main-resolver-DIQ6m_RA.mjs} +4 -4
- package/dist/{main-resolver-BqTjt-GL.mjs.map → main-resolver-DIQ6m_RA.mjs.map} +1 -1
- package/dist/{planner-DbpsMZqY.mjs → planner-D8jAr_sx.mjs} +7 -7
- package/dist/{planner-DbpsMZqY.mjs.map → planner-D8jAr_sx.mjs.map} +1 -1
- package/dist/{program-QSuci3RH.mjs → program-BTdspOOa.mjs} +131 -81
- package/dist/program-BTdspOOa.mjs.map +1 -0
- package/dist/{run-BFxuNnIJ.mjs → run-CxxZKEZw.mjs} +336 -174
- package/dist/run-CxxZKEZw.mjs.map +1 -0
- package/dist/{run-env-kbWVwUvU.mjs → run-env-BE5xyCwy.mjs} +26 -7
- package/dist/run-env-BE5xyCwy.mjs.map +1 -0
- package/dist/{server-B0vBmfJt.mjs → server-BY6AedmC.mjs} +2 -2
- package/dist/{server-B0vBmfJt.mjs.map → server-BY6AedmC.mjs.map} +1 -1
- package/dist/{session-keepalive-CUcFa8ld.mjs → session-keepalive-urk--pfa.mjs} +5 -5
- package/dist/{session-keepalive-CUcFa8ld.mjs.map → session-keepalive-urk--pfa.mjs.map} +1 -1
- package/dist/{tools-CaTZnIau.mjs → tools-qXyHFwNj.mjs} +2 -2
- package/dist/{tools-CaTZnIau.mjs.map → tools-qXyHFwNj.mjs.map} +1 -1
- package/dist/{tools-_7MF8Ebb.mjs → tools-r-7ZG8B5.mjs} +3 -3
- package/dist/{wakeup-CTX3rlz-.mjs → wakeup-C48hcN6O.mjs} +2 -2
- package/dist/{wakeup-CTX3rlz-.mjs.map → wakeup-C48hcN6O.mjs.map} +1 -1
- package/dist/work-queue-worker-1_VBfI_k.mjs +11 -0
- package/dist/{work-queue-worker-B4eZGy5n.mjs → work-queue-worker-DUvPiwGO.mjs} +2 -2
- package/dist/{work-queue-worker-B4eZGy5n.mjs.map → work-queue-worker-DUvPiwGO.mjs.map} +1 -1
- package/docs/commands/worker.md +1 -0
- package/package.json +1 -1
- package/src/hooks/ts/lib/tab-title.ts +18 -0
- package/src/hooks/ts/post-tool-use/update-tab-on-action.ts +3 -20
- package/src/hooks/ts/stop/stop-hook.ts +4 -15
- package/src/hooks/ts/user-prompt/update-tab-titles.ts +2 -10
- package/dist/config-msER8xj_.mjs.map +0 -1
- package/dist/daemon-BppwJtrH.mjs +0 -19
- package/dist/factory-ChfAvCzO.mjs.map +0 -1
- package/dist/main-resolver-CCojcYr2.mjs +0 -7
- package/dist/program-QSuci3RH.mjs.map +0 -1
- package/dist/run-BFxuNnIJ.mjs.map +0 -1
- package/dist/run-env-kbWVwUvU.mjs.map +0 -1
- package/dist/work-queue-worker-DuhZmfDB.mjs +0 -11
|
@@ -8,8 +8,19 @@ function isWorkerSession(env = process.env) {
|
|
|
8
8
|
return env.PAI_WORKER === "1";
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
// src/hooks/ts/lib/tab-title.ts
|
|
12
|
+
var CONTROL_CHARS_RE = /[\x00-\x1f\x7f]/g;
|
|
13
|
+
function setTabTitle(title) {
|
|
14
|
+
const sanitized = title.replace(CONTROL_CHARS_RE, "");
|
|
15
|
+
try {
|
|
16
|
+
process.stderr.write(`\x1B]0;${sanitized}\x07`);
|
|
17
|
+
process.stderr.write(`\x1B]2;${sanitized}\x07`);
|
|
18
|
+
process.stderr.write(`\x1B]30;${sanitized}\x07`);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
11
23
|
// src/hooks/ts/post-tool-use/update-tab-on-action.ts
|
|
12
|
-
import { execSync } from "child_process";
|
|
13
24
|
var ACTION_TOOLS = /* @__PURE__ */ new Set([
|
|
14
25
|
"Edit",
|
|
15
26
|
"Write",
|
|
@@ -37,16 +48,6 @@ async function readStdinWithTimeout(timeout = 3e3) {
|
|
|
37
48
|
});
|
|
38
49
|
});
|
|
39
50
|
}
|
|
40
|
-
function setTabTitle(title) {
|
|
41
|
-
try {
|
|
42
|
-
const truncated = title.length > 50 ? title.slice(0, 47) + "..." : title;
|
|
43
|
-
const escaped = truncated.replace(/'/g, "'\\''");
|
|
44
|
-
execSync(`printf '\\033]0;${escaped}\\007' >&2`, { stdio: ["pipe", "pipe", "inherit"] });
|
|
45
|
-
execSync(`printf '\\033]2;${escaped}\\007' >&2`, { stdio: ["pipe", "pipe", "inherit"] });
|
|
46
|
-
execSync(`printf '\\033]30;${escaped}\\007' >&2`, { stdio: ["pipe", "pipe", "inherit"] });
|
|
47
|
-
} catch {
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
51
|
function generateTitle(toolName, input) {
|
|
51
52
|
if (input.description) {
|
|
52
53
|
return input.description;
|
|
@@ -89,7 +90,8 @@ async function main() {
|
|
|
89
90
|
process.exit(0);
|
|
90
91
|
}
|
|
91
92
|
const title = generateTitle(data.tool_name, data.tool_input);
|
|
92
|
-
|
|
93
|
+
const truncated = title.length > 50 ? title.slice(0, 47) + "..." : title;
|
|
94
|
+
setTabTitle(truncated);
|
|
93
95
|
process.exit(0);
|
|
94
96
|
} catch {
|
|
95
97
|
process.exit(0);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../src/hooks/ts/lib/worker-session.ts", "../../../src/hooks/ts/post-tool-use/update-tab-on-action.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Worker-session detection.\n *\n * A disposable headless worker (a `claude -p` run started by an orchestrating\n * session, possibly against a different model provider with a different\n * context window) shares the project directory and the hook configuration\n * with the real session that spawned it. Left alone, the hooks treat it as a\n * session in its own right: they inject project context into it, create and\n * rename a numbered session note for it, autosave it, and enqueue a\n * model-written handover for it. Its compactions also land in the project's\n * transcript folder, where they are indistinguishable from a real session's\n * and drag the measured compaction trigger down (observed 2026-09-17: two\n * workers compacting at ~151k tokens pulled a project's trigger from ~784k\n * to ~151k, and the real session's handover fired at ~50k tokens).\n *\n * The launcher marks such sessions with `PAI_WORKER=1`. Every hook that does\n * per-session bookkeeping returns immediately when this predicate is true.\n * Deliberately NOT guarded: the security validator (a worker's shell\n * commands must still be checked) and observability capture.\n */\nexport function isWorkerSession(env: NodeJS.ProcessEnv = process.env): boolean {\n return env.PAI_WORKER === \"1\";\n}\n", "#!/usr/bin/env node\n/**\n * Tab Title Update on Action Tools\n * Updates Kitty tab title when action tools complete (Edit, Write, Bash, Task)\n * Uses the tool's description directly - no AI call needed\n *\n * PAI-safe: No sensitive data, just tool descriptions\n */\n\nimport { isWorkerSession } from \"../lib/worker-session.js\";\nimport {
|
|
5
|
-
"mappings": ";;;;;;AAoBO,SAAS,gBAAgB,MAAyB,QAAQ,KAAc;AAC7E,SAAO,IAAI,eAAe;AAC5B;;;
|
|
3
|
+
"sources": ["../../../src/hooks/ts/lib/worker-session.ts", "../../../src/hooks/ts/lib/tab-title.ts", "../../../src/hooks/ts/post-tool-use/update-tab-on-action.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Worker-session detection.\n *\n * A disposable headless worker (a `claude -p` run started by an orchestrating\n * session, possibly against a different model provider with a different\n * context window) shares the project directory and the hook configuration\n * with the real session that spawned it. Left alone, the hooks treat it as a\n * session in its own right: they inject project context into it, create and\n * rename a numbered session note for it, autosave it, and enqueue a\n * model-written handover for it. Its compactions also land in the project's\n * transcript folder, where they are indistinguishable from a real session's\n * and drag the measured compaction trigger down (observed 2026-09-17: two\n * workers compacting at ~151k tokens pulled a project's trigger from ~784k\n * to ~151k, and the real session's handover fired at ~50k tokens).\n *\n * The launcher marks such sessions with `PAI_WORKER=1`. Every hook that does\n * per-session bookkeeping returns immediately when this predicate is true.\n * Deliberately NOT guarded: the security validator (a worker's shell\n * commands must still be checked) and observability capture.\n */\nexport function isWorkerSession(env: NodeJS.ProcessEnv = process.env): boolean {\n return env.PAI_WORKER === \"1\";\n}\n", "/**\n * Terminal tab title escape sequences (OSC 0/2/30), written directly to\n * stderr. No shell involved, so untrusted title text (derived from user\n * prompts) can't reach a shell for interpolation.\n */\n\nconst CONTROL_CHARS_RE = /[\\x00-\\x1f\\x7f]/g;\n\nexport function setTabTitle(title: string): void {\n const sanitized = title.replace(CONTROL_CHARS_RE, '');\n try {\n process.stderr.write(`\\x1b]0;${sanitized}\\x07`);\n process.stderr.write(`\\x1b]2;${sanitized}\\x07`);\n process.stderr.write(`\\x1b]30;${sanitized}\\x07`);\n } catch {\n // Silently fail - don't interrupt Claude's work\n }\n}\n", "#!/usr/bin/env node\n/**\n * Tab Title Update on Action Tools\n * Updates Kitty tab title when action tools complete (Edit, Write, Bash, Task)\n * Uses the tool's description directly - no AI call needed\n *\n * PAI-safe: No sensitive data, just tool descriptions\n */\n\nimport { isWorkerSession } from \"../lib/worker-session.js\";\nimport { setTabTitle } from \"../lib/tab-title.js\";\n\n// Action tools that warrant a tab title update\nconst ACTION_TOOLS = new Set([\n 'Edit',\n 'Write',\n 'Bash',\n 'Task',\n 'NotebookEdit',\n 'MultiEdit'\n]);\n\ninterface HookInput {\n session_id: string;\n tool_name: string;\n tool_input: {\n description?: string;\n command?: string;\n file_path?: string;\n prompt?: string;\n subagent_type?: string;\n };\n tool_response?: unknown;\n hook_event_name: string;\n}\n\n/**\n * Read stdin with timeout\n */\nasync function readStdinWithTimeout(timeout: number = 3000): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = '';\n const timer = setTimeout(() => {\n reject(new Error('Timeout'));\n }, timeout);\n\n process.stdin.on('data', (chunk) => {\n data += chunk.toString();\n });\n\n process.stdin.on('end', () => {\n clearTimeout(timer);\n resolve(data);\n });\n\n process.stdin.on('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n });\n}\n\n/**\n * Generate a human-readable title from tool input\n */\nfunction generateTitle(toolName: string, input: HookInput['tool_input']): string {\n // Use explicit description if provided\n if (input.description) {\n return input.description;\n }\n\n // Generate based on tool type\n switch (toolName) {\n case 'Edit':\n case 'Write':\n case 'MultiEdit':\n case 'NotebookEdit':\n if (input.file_path) {\n const filename = input.file_path.split('/').pop() || input.file_path;\n return `Editing ${filename}`;\n }\n return `Editing file`;\n\n case 'Bash':\n if (input.command) {\n // Extract first command word\n const cmd = input.command.split(/\\s+/)[0].split('/').pop() || 'command';\n return `Running ${cmd}`;\n }\n return 'Running command';\n\n case 'Task':\n if (input.subagent_type) {\n return `Agent: ${input.subagent_type}`;\n }\n if (input.prompt) {\n const words = input.prompt.slice(0, 30).split(/\\s+/).slice(0, 3).join(' ');\n return `Task: ${words}...`;\n }\n return 'Spawning agent';\n\n default:\n return `${toolName}...`;\n }\n}\n\nasync function main() {\n if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping\n try {\n const input = await readStdinWithTimeout();\n const data: HookInput = JSON.parse(input);\n\n // Only process action tools\n if (!ACTION_TOOLS.has(data.tool_name)) {\n process.exit(0);\n }\n\n // Generate and set title\n const title = generateTitle(data.tool_name, data.tool_input);\n const truncated = title.length > 50 ? title.slice(0, 47) + '...' : title;\n setTabTitle(truncated);\n\n process.exit(0);\n } catch {\n // Silently exit - don't interrupt Claude's flow\n process.exit(0);\n }\n}\n\nmain();\n"],
|
|
5
|
+
"mappings": ";;;;;;AAoBO,SAAS,gBAAgB,MAAyB,QAAQ,KAAc;AAC7E,SAAO,IAAI,eAAe;AAC5B;;;AChBA,IAAM,mBAAmB;AAElB,SAAS,YAAY,OAAqB;AAC/C,QAAM,YAAY,MAAM,QAAQ,kBAAkB,EAAE;AACpD,MAAI;AACF,YAAQ,OAAO,MAAM,UAAU,SAAS,MAAM;AAC9C,YAAQ,OAAO,MAAM,UAAU,SAAS,MAAM;AAC9C,YAAQ,OAAO,MAAM,WAAW,SAAS,MAAM;AAAA,EACjD,QAAQ;AAAA,EAER;AACF;;;ACJA,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAmBD,eAAe,qBAAqB,UAAkB,KAAuB;AAC3E,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,OAAO;AACX,UAAM,QAAQ,WAAW,MAAM;AAC7B,aAAO,IAAI,MAAM,SAAS,CAAC;AAAA,IAC7B,GAAG,OAAO;AAEV,YAAQ,MAAM,GAAG,QAAQ,CAAC,UAAU;AAClC,cAAQ,MAAM,SAAS;AAAA,IACzB,CAAC;AAED,YAAQ,MAAM,GAAG,OAAO,MAAM;AAC5B,mBAAa,KAAK;AAClB,cAAQ,IAAI;AAAA,IACd,CAAC;AAED,YAAQ,MAAM,GAAG,SAAS,CAAC,QAAQ;AACjC,mBAAa,KAAK;AAClB,aAAO,GAAG;AAAA,IACZ,CAAC;AAAA,EACH,CAAC;AACH;AAKA,SAAS,cAAc,UAAkB,OAAwC;AAE/E,MAAI,MAAM,aAAa;AACrB,WAAO,MAAM;AAAA,EACf;AAGA,UAAQ,UAAU;AAAA,IAChB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,UAAI,MAAM,WAAW;AACnB,cAAM,WAAW,MAAM,UAAU,MAAM,GAAG,EAAE,IAAI,KAAK,MAAM;AAC3D,eAAO,WAAW,QAAQ;AAAA,MAC5B;AACA,aAAO;AAAA,IAET,KAAK;AACH,UAAI,MAAM,SAAS;AAEjB,cAAM,MAAM,MAAM,QAAQ,MAAM,KAAK,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,IAAI,KAAK;AAC9D,eAAO,WAAW,GAAG;AAAA,MACvB;AACA,aAAO;AAAA,IAET,KAAK;AACH,UAAI,MAAM,eAAe;AACvB,eAAO,UAAU,MAAM,aAAa;AAAA,MACtC;AACA,UAAI,MAAM,QAAQ;AAChB,cAAM,QAAQ,MAAM,OAAO,MAAM,GAAG,EAAE,EAAE,MAAM,KAAK,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AACzE,eAAO,SAAS,KAAK;AAAA,MACvB;AACA,aAAO;AAAA,IAET;AACE,aAAO,GAAG,QAAQ;AAAA,EACtB;AACF;AAEA,eAAe,OAAO;AACpB,MAAI,gBAAgB,EAAG;AACvB,MAAI;AACF,UAAM,QAAQ,MAAM,qBAAqB;AACzC,UAAM,OAAkB,KAAK,MAAM,KAAK;AAGxC,QAAI,CAAC,aAAa,IAAI,KAAK,SAAS,GAAG;AACrC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,QAAQ,cAAc,KAAK,WAAW,KAAK,UAAU;AAC3D,UAAM,YAAY,MAAM,SAAS,KAAK,MAAM,MAAM,GAAG,EAAE,IAAI,QAAQ;AACnE,gBAAY,SAAS;AAErB,YAAQ,KAAK,CAAC;AAAA,EAChB,QAAQ;AAEN,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -8,8 +8,19 @@ function isWorkerSession(env = process.env) {
|
|
|
8
8
|
return env.PAI_WORKER === "1";
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
// src/hooks/ts/lib/tab-title.ts
|
|
12
|
+
var CONTROL_CHARS_RE = /[\x00-\x1f\x7f]/g;
|
|
13
|
+
function setTabTitle(title) {
|
|
14
|
+
const sanitized = title.replace(CONTROL_CHARS_RE, "");
|
|
15
|
+
try {
|
|
16
|
+
process.stderr.write(`\x1B]0;${sanitized}\x07`);
|
|
17
|
+
process.stderr.write(`\x1B]2;${sanitized}\x07`);
|
|
18
|
+
process.stderr.write(`\x1B]30;${sanitized}\x07`);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
11
23
|
// src/hooks/ts/user-prompt/update-tab-titles.ts
|
|
12
|
-
import { execSync } from "child_process";
|
|
13
24
|
async function readStdinWithTimeout(timeout = 5e3) {
|
|
14
25
|
return new Promise((resolve, reject) => {
|
|
15
26
|
let data = "";
|
|
@@ -46,14 +57,7 @@ async function main() {
|
|
|
46
57
|
tabTitle += "...";
|
|
47
58
|
}
|
|
48
59
|
}
|
|
49
|
-
|
|
50
|
-
const titleWithEmoji = "\u267B\uFE0F " + tabTitle;
|
|
51
|
-
const escapedTitle = titleWithEmoji.replace(/'/g, "'\\''");
|
|
52
|
-
execSync(`printf '\\033]0;${escapedTitle}\\007' >&2`);
|
|
53
|
-
execSync(`printf '\\033]2;${escapedTitle}\\007' >&2`);
|
|
54
|
-
execSync(`printf '\\033]30;${escapedTitle}\\007' >&2`);
|
|
55
|
-
} catch (e) {
|
|
56
|
-
}
|
|
60
|
+
setTabTitle("\u267B\uFE0F " + tabTitle);
|
|
57
61
|
process.exit(0);
|
|
58
62
|
} catch (error) {
|
|
59
63
|
console.error("Tab title update error:", error);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../src/hooks/ts/lib/worker-session.ts", "../../../src/hooks/ts/user-prompt/update-tab-titles.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Worker-session detection.\n *\n * A disposable headless worker (a `claude -p` run started by an orchestrating\n * session, possibly against a different model provider with a different\n * context window) shares the project directory and the hook configuration\n * with the real session that spawned it. Left alone, the hooks treat it as a\n * session in its own right: they inject project context into it, create and\n * rename a numbered session note for it, autosave it, and enqueue a\n * model-written handover for it. Its compactions also land in the project's\n * transcript folder, where they are indistinguishable from a real session's\n * and drag the measured compaction trigger down (observed 2026-09-17: two\n * workers compacting at ~151k tokens pulled a project's trigger from ~784k\n * to ~151k, and the real session's handover fired at ~50k tokens).\n *\n * The launcher marks such sessions with `PAI_WORKER=1`. Every hook that does\n * per-session bookkeeping returns immediately when this predicate is true.\n * Deliberately NOT guarded: the security validator (a worker's shell\n * commands must still be checked) and observability capture.\n */\nexport function isWorkerSession(env: NodeJS.ProcessEnv = process.env): boolean {\n return env.PAI_WORKER === \"1\";\n}\n", "#!/usr/bin/env node\n/**\n * Tab Title Update Hook\n * Updates tab title based on user prompt\n *\n * Note: All context loading handled by PAI skill system (core identity in skill description, full context in SKILL.md)\n */\n\nimport { isWorkerSession } from \"../lib/worker-session.js\";\nimport {
|
|
5
|
-
"mappings": ";;;;;;AAoBO,SAAS,gBAAgB,MAAyB,QAAQ,KAAc;AAC7E,SAAO,IAAI,eAAe;AAC5B;;;
|
|
3
|
+
"sources": ["../../../src/hooks/ts/lib/worker-session.ts", "../../../src/hooks/ts/lib/tab-title.ts", "../../../src/hooks/ts/user-prompt/update-tab-titles.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Worker-session detection.\n *\n * A disposable headless worker (a `claude -p` run started by an orchestrating\n * session, possibly against a different model provider with a different\n * context window) shares the project directory and the hook configuration\n * with the real session that spawned it. Left alone, the hooks treat it as a\n * session in its own right: they inject project context into it, create and\n * rename a numbered session note for it, autosave it, and enqueue a\n * model-written handover for it. Its compactions also land in the project's\n * transcript folder, where they are indistinguishable from a real session's\n * and drag the measured compaction trigger down (observed 2026-09-17: two\n * workers compacting at ~151k tokens pulled a project's trigger from ~784k\n * to ~151k, and the real session's handover fired at ~50k tokens).\n *\n * The launcher marks such sessions with `PAI_WORKER=1`. Every hook that does\n * per-session bookkeeping returns immediately when this predicate is true.\n * Deliberately NOT guarded: the security validator (a worker's shell\n * commands must still be checked) and observability capture.\n */\nexport function isWorkerSession(env: NodeJS.ProcessEnv = process.env): boolean {\n return env.PAI_WORKER === \"1\";\n}\n", "/**\n * Terminal tab title escape sequences (OSC 0/2/30), written directly to\n * stderr. No shell involved, so untrusted title text (derived from user\n * prompts) can't reach a shell for interpolation.\n */\n\nconst CONTROL_CHARS_RE = /[\\x00-\\x1f\\x7f]/g;\n\nexport function setTabTitle(title: string): void {\n const sanitized = title.replace(CONTROL_CHARS_RE, '');\n try {\n process.stderr.write(`\\x1b]0;${sanitized}\\x07`);\n process.stderr.write(`\\x1b]2;${sanitized}\\x07`);\n process.stderr.write(`\\x1b]30;${sanitized}\\x07`);\n } catch {\n // Silently fail - don't interrupt Claude's work\n }\n}\n", "#!/usr/bin/env node\n/**\n * Tab Title Update Hook\n * Updates tab title based on user prompt\n *\n * Note: All context loading handled by PAI skill system (core identity in skill description, full context in SKILL.md)\n */\n\nimport { isWorkerSession } from \"../lib/worker-session.js\";\nimport { setTabTitle } from \"../lib/tab-title.js\";\n\ninterface HookInput {\n session_id: string;\n prompt: string;\n transcript_path: string;\n hook_event_name: string;\n}\n\n/**\n * Read stdin with timeout\n */\nasync function readStdinWithTimeout(timeout: number = 5000): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = '';\n const timer = setTimeout(() => {\n reject(new Error('Timeout reading from stdin'));\n }, timeout);\n\n process.stdin.on('data', (chunk) => {\n data += chunk.toString();\n });\n\n process.stdin.on('end', () => {\n clearTimeout(timer);\n resolve(data);\n });\n\n process.stdin.on('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n });\n}\n\nasync function main() {\n if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping\n try {\n // Read the hook input from stdin\n const input = await readStdinWithTimeout();\n const data: HookInput = JSON.parse(input);\n\n const prompt = data.prompt || '';\n\n // UPDATE TAB TITLE\n // Generate quick fallback tab title\n let tabTitle = 'Processing request...';\n if (prompt) {\n const words = prompt.replace(/[^\\w\\s]/g, ' ').trim().split(/\\s+/)\n .filter(w => w.length > 2 && !['the', 'and', 'but', 'for', 'are', 'with', 'you', 'can'].includes(w.toLowerCase()))\n .slice(0, 3);\n\n if (words.length > 0) {\n tabTitle = words[0].charAt(0).toUpperCase() + words[0].slice(1).toLowerCase();\n if (words.length > 1) {\n tabTitle += ' ' + words.slice(1).map(w => w.toLowerCase()).join(' ');\n }\n tabTitle += '...';\n }\n }\n\n // Set initial tab title with recycle emoji\n setTabTitle('\u267B\uFE0F ' + tabTitle);\n\n process.exit(0);\n } catch (error) {\n // Silently fail to not interrupt Claude's flow\n console.error('Tab title update error:', error);\n process.exit(0);\n }\n}\n\nmain();\n"],
|
|
5
|
+
"mappings": ";;;;;;AAoBO,SAAS,gBAAgB,MAAyB,QAAQ,KAAc;AAC7E,SAAO,IAAI,eAAe;AAC5B;;;AChBA,IAAM,mBAAmB;AAElB,SAAS,YAAY,OAAqB;AAC/C,QAAM,YAAY,MAAM,QAAQ,kBAAkB,EAAE;AACpD,MAAI;AACF,YAAQ,OAAO,MAAM,UAAU,SAAS,MAAM;AAC9C,YAAQ,OAAO,MAAM,UAAU,SAAS,MAAM;AAC9C,YAAQ,OAAO,MAAM,WAAW,SAAS,MAAM;AAAA,EACjD,QAAQ;AAAA,EAER;AACF;;;ACIA,eAAe,qBAAqB,UAAkB,KAAuB;AAC3E,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,OAAO;AACX,UAAM,QAAQ,WAAW,MAAM;AAC7B,aAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,IAChD,GAAG,OAAO;AAEV,YAAQ,MAAM,GAAG,QAAQ,CAAC,UAAU;AAClC,cAAQ,MAAM,SAAS;AAAA,IACzB,CAAC;AAED,YAAQ,MAAM,GAAG,OAAO,MAAM;AAC5B,mBAAa,KAAK;AAClB,cAAQ,IAAI;AAAA,IACd,CAAC;AAED,YAAQ,MAAM,GAAG,SAAS,CAAC,QAAQ;AACjC,mBAAa,KAAK;AAClB,aAAO,GAAG;AAAA,IACZ,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAe,OAAO;AACpB,MAAI,gBAAgB,EAAG;AACvB,MAAI;AAEF,UAAM,QAAQ,MAAM,qBAAqB;AACzC,UAAM,OAAkB,KAAK,MAAM,KAAK;AAExC,UAAM,SAAS,KAAK,UAAU;AAI9B,QAAI,WAAW;AACf,QAAI,QAAQ;AACV,YAAM,QAAQ,OAAO,QAAQ,YAAY,GAAG,EAAE,KAAK,EAAE,MAAM,KAAK,EAC7D,OAAO,OAAK,EAAE,SAAS,KAAK,CAAC,CAAC,OAAO,OAAO,OAAO,OAAO,OAAO,QAAQ,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,EAChH,MAAM,GAAG,CAAC;AAEb,UAAI,MAAM,SAAS,GAAG;AACpB,mBAAW,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,YAAY;AAC5E,YAAI,MAAM,SAAS,GAAG;AACpB,sBAAY,MAAM,MAAM,MAAM,CAAC,EAAE,IAAI,OAAK,EAAE,YAAY,CAAC,EAAE,KAAK,GAAG;AAAA,QACrE;AACA,oBAAY;AAAA,MACd;AAAA,IACF;AAGA,gBAAY,kBAAQ,QAAQ;AAE5B,YAAQ,KAAK,CAAC;AAAA,EAChB,SAAS,OAAO;AAEd,YAAQ,MAAM,2BAA2B,KAAK;AAC9C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7441,7 +7441,7 @@ function advisorModePath() {
|
|
|
7441
7441
|
var SESSION_STOP_LOCK_ABANDONED_AFTER_MS = 60 * 60 * 1e3;
|
|
7442
7442
|
|
|
7443
7443
|
// src/daemon/config.ts
|
|
7444
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, chmodSync as
|
|
7444
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, chmodSync as chmodSync3, readdirSync as readdirSync2 } from "node:fs";
|
|
7445
7445
|
import { homedir as homedir3, userInfo } from "node:os";
|
|
7446
7446
|
import { join as join4, dirname as dirname3, basename as basename2 } from "node:path";
|
|
7447
7447
|
|
|
@@ -7498,7 +7498,7 @@ function paiSocketPath() {
|
|
|
7498
7498
|
|
|
7499
7499
|
// src/config/main-config.ts
|
|
7500
7500
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
7501
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3, renameSync as renameSync3 } from "node:fs";
|
|
7501
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, renameSync as renameSync3, chmodSync as chmodSync2 } from "node:fs";
|
|
7502
7502
|
import { dirname as dirname2, join as join3, basename } from "node:path";
|
|
7503
7503
|
|
|
7504
7504
|
// src/config/json-store.ts
|