@tekmidian/pai 0.36.1 → 0.37.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/{auto-route-Byf8ENXj.mjs → auto-route-DM7GhJ8y.mjs} +2 -2
- package/dist/{auto-route-Byf8ENXj.mjs.map → auto-route-DM7GhJ8y.mjs.map} +1 -1
- package/dist/cli/index.mjs +5 -4
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/program.d.mts.map +1 -1
- package/dist/cli/program.mjs +5 -4
- package/dist/{clusters-wZgTCYCB.mjs → clusters-Do4tEGyc.mjs} +1 -1
- package/dist/{clusters-wZgTCYCB.mjs.map → clusters-Do4tEGyc.mjs.map} +1 -1
- package/dist/{context-handover-cache-PtNvj_8D.mjs → context-handover-cache-BpUojjsi.mjs} +2 -2
- package/dist/{context-handover-cache-PtNvj_8D.mjs.map → context-handover-cache-BpUojjsi.mjs.map} +1 -1
- package/dist/daemon/index.mjs +8 -8
- package/dist/{daemon-BZ93KRBo.mjs → daemon-CGg1VCbA.mjs} +25 -25
- package/dist/{daemon-BZ93KRBo.mjs.map → daemon-CGg1VCbA.mjs.map} +1 -1
- package/dist/daemon-DsGGiIJM.mjs +20 -0
- package/dist/daemon-mcp/index.mjs +428 -1
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{detector--Gg5JRN5.mjs → detector-CMap-9vw.mjs} +1 -1
- package/dist/{detector--Gg5JRN5.mjs.map → detector-CMap-9vw.mjs.map} +1 -1
- package/dist/detector-DtLExmHN.mjs +5 -0
- package/dist/factory-BXzqRYVZ.mjs +3 -0
- package/dist/{factory-Bsp7xOpO.mjs → factory-DD2T33C9.mjs} +5 -5
- package/dist/{factory-Bsp7xOpO.mjs.map → factory-DD2T33C9.mjs.map} +1 -1
- package/dist/hooks/context-compression-hook.mjs +164 -14
- package/dist/hooks/context-compression-hook.mjs.map +4 -4
- package/dist/hooks/initialize-session.mjs +6 -0
- package/dist/hooks/initialize-session.mjs.map +3 -3
- package/dist/hooks/load-core-context.mjs +6 -0
- package/dist/hooks/load-core-context.mjs.map +3 -3
- package/dist/hooks/load-project-context.mjs +6 -0
- package/dist/hooks/load-project-context.mjs.map +3 -3
- package/dist/hooks/post-compact-inject.mjs +48 -7
- package/dist/hooks/post-compact-inject.mjs.map +4 -4
- package/dist/hooks/route-agents-to-worker.mjs +456 -0
- package/dist/hooks/route-agents-to-worker.mjs.map +7 -0
- package/dist/hooks/status-line.mjs +400 -0
- package/dist/hooks/status-line.mjs.map +7 -0
- package/dist/hooks/stop-hook.mjs +6 -0
- package/dist/hooks/stop-hook.mjs.map +3 -3
- package/dist/hooks/subagent-stop-hook.mjs +6 -0
- package/dist/hooks/subagent-stop-hook.mjs.map +3 -3
- package/dist/hooks/sync-todo-to-md.mjs +6 -0
- package/dist/hooks/sync-todo-to-md.mjs.map +3 -3
- package/dist/hooks/update-tab-on-action.mjs +6 -0
- package/dist/hooks/update-tab-on-action.mjs.map +3 -3
- package/dist/hooks/update-tab-titles.mjs +6 -0
- package/dist/hooks/update-tab-titles.mjs.map +3 -3
- package/dist/hooks/worker-proxy.mjs +796 -0
- package/dist/hooks/worker-proxy.mjs.map +7 -0
- package/dist/hooks/worker-status-line.mjs +483 -0
- package/dist/hooks/worker-status-line.mjs.map +7 -0
- package/dist/{indexer-backend-nQZuEx6N.mjs → indexer-backend-Cox9BCo-.mjs} +1 -1
- package/dist/{indexer-backend-nQZuEx6N.mjs.map → indexer-backend-Cox9BCo-.mjs.map} +1 -1
- package/dist/{kg-entity-DbOMPdF9.mjs → kg-entity-r8duqhi9.mjs} +1 -1
- package/dist/{kg-entity-DbOMPdF9.mjs.map → kg-entity-r8duqhi9.mjs.map} +1 -1
- package/dist/{latent-ideas-Bn6A5-5P.mjs → latent-ideas-BC1oINZ-.mjs} +2 -2
- package/dist/{latent-ideas-Bn6A5-5P.mjs.map → latent-ideas-BC1oINZ-.mjs.map} +1 -1
- package/dist/{link-boost-fYjUnxCN.mjs → link-boost-QFLrJwD6.mjs} +1 -1
- package/dist/{link-boost-fYjUnxCN.mjs.map → link-boost-QFLrJwD6.mjs.map} +1 -1
- package/dist/{main-resolver-BAbhKpeX.mjs → main-resolver-DlaLOFBA.mjs} +1 -1
- package/dist/{main-resolver-BAbhKpeX.mjs.map → main-resolver-DlaLOFBA.mjs.map} +1 -1
- package/dist/{main-resolver-Dxh444GO.mjs → main-resolver-IhZo4pI0.mjs} +1 -1
- package/dist/{neighborhood-DpaEM991.mjs → neighborhood-D9MJ1c8f.mjs} +1 -1
- package/dist/{neighborhood-DpaEM991.mjs.map → neighborhood-D9MJ1c8f.mjs.map} +1 -1
- package/dist/{note-context-DrcY4cWm.mjs → note-context-d1wT_-GA.mjs} +1 -1
- package/dist/{note-context-DrcY4cWm.mjs.map → note-context-d1wT_-GA.mjs.map} +1 -1
- package/dist/{postgres-BVme6qX0.mjs → postgres--BjPtLa0.mjs} +1 -1
- package/dist/{postgres-BVme6qX0.mjs.map → postgres--BjPtLa0.mjs.map} +1 -1
- package/dist/{program-C-fUghPv.mjs → program-CEIHn_Ma.mjs} +693 -101
- package/dist/program-CEIHn_Ma.mjs.map +1 -0
- package/dist/providers-sXcK5bDZ.mjs +3067 -0
- package/dist/providers-sXcK5bDZ.mjs.map +1 -0
- package/dist/{query-feedback-C1T6kS18.mjs → query-feedback-BUJxgw5B.mjs} +1 -1
- package/dist/{query-feedback-C1T6kS18.mjs.map → query-feedback-BUJxgw5B.mjs.map} +1 -1
- package/dist/query-feedback-DhyLOe5S.mjs +3 -0
- package/dist/router-1zi8jiNF.mjs +3 -0
- package/dist/{router-CsDm7HvK.mjs → router-DK_sLsUL.mjs} +1 -1
- package/dist/{router-CsDm7HvK.mjs.map → router-DK_sLsUL.mjs.map} +1 -1
- package/dist/skills/Worker/SKILL.md +41 -0
- package/dist/{sources-D8ZdNfvK.mjs → sources-Bi7--33T.mjs} +1 -1
- package/dist/{sources-D8ZdNfvK.mjs.map → sources-Bi7--33T.mjs.map} +1 -1
- package/dist/{sqlite-D1IaR8Am.mjs → sqlite-DtaL1glm.mjs} +1 -1
- package/dist/{sqlite-D1IaR8Am.mjs.map → sqlite-DtaL1glm.mjs.map} +1 -1
- package/dist/{state-qtmrBWCm.mjs → state-8Hm9E4tW.mjs} +1 -1
- package/dist/{state-WaXhLr6R.mjs → state-BY2L6-vX.mjs} +1 -1
- package/dist/{state-WaXhLr6R.mjs.map → state-BY2L6-vX.mjs.map} +1 -1
- package/dist/{themes-XPkj_bfP.mjs → themes-BN0a2duq.mjs} +1 -1
- package/dist/{themes-XPkj_bfP.mjs.map → themes-BN0a2duq.mjs.map} +1 -1
- package/dist/{tools-DEt6YPfc.mjs → tools-CGPqpU3A.mjs} +1 -1
- package/dist/{tools-ceiy7ANX.mjs → tools-y2bJpKom.mjs} +14 -14
- package/dist/{tools-ceiy7ANX.mjs.map → tools-y2bJpKom.mjs.map} +1 -1
- package/dist/{trace-DfyGmMG_.mjs → trace-h23JCcFD.mjs} +1 -1
- package/dist/{trace-DfyGmMG_.mjs.map → trace-h23JCcFD.mjs.map} +1 -1
- package/dist/{vault-indexer-CFvlPUMB.mjs → vault-indexer-DgsPjMgs.mjs} +1 -1
- package/dist/{vault-indexer-CFvlPUMB.mjs.map → vault-indexer-DgsPjMgs.mjs.map} +1 -1
- package/dist/{work-queue-worker-B8W8_3Rn.mjs → work-queue-worker-Dva_v_pI.mjs} +4 -4
- package/dist/{work-queue-worker-B8W8_3Rn.mjs.map → work-queue-worker-Dva_v_pI.mjs.map} +1 -1
- package/dist/{work-queue-worker-HN2Ufg-L.mjs → work-queue-worker-gsKd2LJa.mjs} +4 -4
- package/dist/{zettelkasten-CvjmMghT.mjs → zettelkasten-vo7psPdT.mjs} +3 -3
- package/dist/{zettelkasten-CvjmMghT.mjs.map → zettelkasten-vo7psPdT.mjs.map} +1 -1
- package/docs/commands/README.md +28 -0
- package/docs/commands/backup.md +1 -1
- package/docs/commands/clear-names.md +1 -1
- package/docs/commands/daemon.md +1 -1
- package/docs/commands/db.md +1 -1
- package/docs/commands/end.md +1 -1
- package/docs/commands/help.md +1 -1
- package/docs/commands/identity.md +1 -1
- package/docs/commands/kg.md +1 -1
- package/docs/commands/mcp.md +1 -1
- package/docs/commands/memory.md +1 -1
- package/docs/commands/notify.md +1 -1
- package/docs/commands/observation.md +1 -1
- package/docs/commands/obsidian.md +1 -1
- package/docs/commands/pause.md +1 -1
- package/docs/commands/project.md +1 -1
- package/docs/commands/projects.md +1 -1
- package/docs/commands/registry.md +1 -1
- package/docs/commands/restore.md +1 -1
- package/docs/commands/session.md +1 -1
- package/docs/commands/sessions.md +1 -1
- package/docs/commands/setup.md +1 -1
- package/docs/commands/shell-init.md +1 -1
- package/docs/commands/skill.md +1 -1
- package/docs/commands/task.md +1 -1
- package/docs/commands/topic.md +1 -1
- package/docs/commands/update.md +1 -1
- package/docs/commands/worker.md +361 -0
- package/docs/commands/zettel.md +1 -1
- package/docs/worker.md +303 -0
- package/package.json +1 -1
- package/scripts/build-hooks.mjs +46 -1
- package/src/hooks/pre-compact.sh +1 -0
- package/src/hooks/session-autosave.sh +1 -0
- package/src/hooks/session-stop.sh +1 -0
- package/src/hooks/ts/lib/context-fill.test.ts +104 -0
- package/src/hooks/ts/lib/context-fill.ts +73 -0
- package/src/hooks/ts/lib/handover-evidence.ts +227 -0
- package/src/hooks/ts/lib/worker-session.test.ts +24 -0
- package/src/hooks/ts/lib/worker-session.ts +23 -0
- package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +2 -0
- package/src/hooks/ts/post-tool-use/update-tab-on-action.ts +2 -0
- package/src/hooks/ts/pre-compact/context-compression-hook.ts +17 -0
- package/src/hooks/ts/pre-tool-use/route-agents-to-worker.ts +128 -0
- package/src/hooks/ts/session-start/initialize-session.ts +2 -0
- package/src/hooks/ts/session-start/load-core-context.ts +2 -0
- package/src/hooks/ts/session-start/load-project-context.ts +2 -0
- package/src/hooks/ts/session-start/post-compact-inject.ts +13 -0
- package/src/hooks/ts/stop/stop-hook.ts +2 -0
- package/src/hooks/ts/subagent-stop/subagent-stop-hook.ts +2 -0
- package/src/hooks/ts/user-prompt/update-tab-titles.ts +2 -0
- package/statusline-command.sh +17 -0
- package/dist/daemon-DRdoA489.mjs +0 -20
- package/dist/detector-DGAk1iBR.mjs +0 -5
- package/dist/factory-CrokPMk2.mjs +0 -3
- package/dist/program-C-fUghPv.mjs.map +0 -1
- package/dist/query-feedback-BBMBp96K.mjs +0 -3
- package/dist/router-BMkOb62X.mjs +0 -3
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evidence binding for the model-written pre-compaction handover.
|
|
3
|
+
*
|
|
4
|
+
* The handover is prose written by a model about the session. Nothing else
|
|
5
|
+
* checks its claims, and the successor session cannot: by the time it reads
|
|
6
|
+
* the handover, the transcript it describes has been compacted away. This
|
|
7
|
+
* module does the one check that is cheap and deterministic — every
|
|
8
|
+
* checkable identifier in the handover (commit hashes, versions, paths,
|
|
9
|
+
* pids, timestamps, token counts) must appear verbatim in the transcript
|
|
10
|
+
* the handover was written from. Identifiers that do not are reported as
|
|
11
|
+
* unverified. Nothing is ever dropped or rewritten: the reader gets the
|
|
12
|
+
* handover unchanged plus one footer line saying how much of it is
|
|
13
|
+
* anchored in the record.
|
|
14
|
+
*
|
|
15
|
+
* Borrowed from the evidence-binding step of graph extractors that check an
|
|
16
|
+
* LLM's output against the chunk it was given. No model is called.
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync } from "node:fs";
|
|
19
|
+
import { resolve } from "node:path";
|
|
20
|
+
import { contentToText } from "./transcript-text.js";
|
|
21
|
+
|
|
22
|
+
export interface EvidenceResult {
|
|
23
|
+
/** Distinct checkable identifiers found in the handover. */
|
|
24
|
+
total: number;
|
|
25
|
+
/** Identifiers present verbatim (after normalisation) in the transcript. */
|
|
26
|
+
found: string[];
|
|
27
|
+
/** Identifiers absent from the transcript — unverified, not wrong. */
|
|
28
|
+
missing: string[];
|
|
29
|
+
/** One line for the reader, never empty. */
|
|
30
|
+
footer: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Minimum identifier length worth checking; shorter strings match by accident. */
|
|
34
|
+
const MIN_TOKEN_LENGTH = 4;
|
|
35
|
+
/** How many unverified identifiers the footer names before "+N more". */
|
|
36
|
+
const FOOTER_NAMED_MISSING = 5;
|
|
37
|
+
/** Length of the handover's opening used to recognise its own echo in a tool result. */
|
|
38
|
+
const ECHO_HEAD_LENGTH = 80;
|
|
39
|
+
|
|
40
|
+
/** Markers of text that was injected INTO the session rather than produced by it. */
|
|
41
|
+
const INJECTED_MARKERS = [
|
|
42
|
+
"HANDOVER FROM THE PREVIOUS SESSION",
|
|
43
|
+
"MODEL-WRITTEN HANDOVER",
|
|
44
|
+
"SESSION STATE RECOVERED AFTER COMPACTION",
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
/** Tools whose input would contain the handover itself when it targets the handover file. */
|
|
48
|
+
const HANDOVER_WRITERS = new Set(["Write", "Edit", "NotebookEdit"]);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Identifier classes, in priority order. Each pattern yields either its
|
|
52
|
+
* whole match or capture group 1 when present. Kept deliberately literal:
|
|
53
|
+
* an identifier is something a reader could grep for.
|
|
54
|
+
*/
|
|
55
|
+
const TOKEN_PATTERNS: RegExp[] = [
|
|
56
|
+
// git hashes: 7-40 hex chars with at least one letter (a plain number is not a hash)
|
|
57
|
+
/\b(?=[0-9a-f]*[a-f])[0-9a-f]{7,40}\b/g,
|
|
58
|
+
// package@version
|
|
59
|
+
/(?:@[\w.-]+\/)?[\w.-]+@\d+\.\d+\.\d+(?:-[\w.]+)?/g,
|
|
60
|
+
// semver
|
|
61
|
+
/\bv?\d+\.\d+\.\d+(?:-[\w.]+)?\b/g,
|
|
62
|
+
// absolute paths (two or more segments)
|
|
63
|
+
/(?:^|[\s(`'"])(\/[\w.-]+(?:\/[\w.-]+)+)/gm,
|
|
64
|
+
// relative paths with a slash and an extension
|
|
65
|
+
/(?:^|[\s(`'"])((?:~\/|\.\.?\/)?[\w.-]+(?:\/[\w.-]+)+\.[a-z0-9]{1,5})\b/gm,
|
|
66
|
+
// process ids
|
|
67
|
+
/\bpid\s*[:=#]?\s*(\d{3,7})\b/gi,
|
|
68
|
+
// ISO dates and timestamps
|
|
69
|
+
/\b\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?Z?)?\b/g,
|
|
70
|
+
// numbers with units — digits only are kept
|
|
71
|
+
/\b(\d[\d,]*)\s*(?:tokens?|ms|MB|GB)\b/g,
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
/** Strip thousands separators so "784,000" and "784000" compare equal. */
|
|
75
|
+
function normalise(text: string): string {
|
|
76
|
+
return text.replace(/(\d),(?=\d{3}\b)/g, "$1");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function stripSystemReminders(text: string): string {
|
|
80
|
+
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Every distinct checkable identifier in a handover, in order of first appearance. */
|
|
84
|
+
export function extractCheckableTokens(markdown: string): string[] {
|
|
85
|
+
const seen = new Set<string>();
|
|
86
|
+
const out: string[] = [];
|
|
87
|
+
for (const pattern of TOKEN_PATTERNS) {
|
|
88
|
+
pattern.lastIndex = 0;
|
|
89
|
+
for (const match of markdown.matchAll(pattern)) {
|
|
90
|
+
const raw = (match[1] ?? match[0]).trim();
|
|
91
|
+
const token = normalise(raw);
|
|
92
|
+
if (token.length < MIN_TOKEN_LENGTH || seen.has(token)) continue;
|
|
93
|
+
seen.add(token);
|
|
94
|
+
out.push(token);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface TranscriptEntry {
|
|
101
|
+
type?: string;
|
|
102
|
+
message?: { content?: unknown };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface ContentPart {
|
|
106
|
+
type?: string;
|
|
107
|
+
text?: string;
|
|
108
|
+
name?: string;
|
|
109
|
+
input?: { file_path?: unknown } & Record<string, unknown>;
|
|
110
|
+
content?: unknown;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Everything the session itself said, did, or was shown — minus the two
|
|
115
|
+
* places where the handover could verify against itself: the tool call
|
|
116
|
+
* that wrote the handover file, and injected blocks carried over from an
|
|
117
|
+
* earlier compaction.
|
|
118
|
+
*/
|
|
119
|
+
function buildHaystack(
|
|
120
|
+
transcriptPaths: string[],
|
|
121
|
+
handoverPath: string | null,
|
|
122
|
+
handoverHead: string
|
|
123
|
+
): string {
|
|
124
|
+
const parts: string[] = [];
|
|
125
|
+
const resolvedHandover = handoverPath ? resolve(handoverPath) : null;
|
|
126
|
+
|
|
127
|
+
const pushUserText = (text: string): void => {
|
|
128
|
+
if (INJECTED_MARKERS.some((m) => text.includes(m))) return;
|
|
129
|
+
parts.push(stripSystemReminders(text));
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
for (const path of transcriptPaths) {
|
|
133
|
+
let raw: string;
|
|
134
|
+
try {
|
|
135
|
+
raw = readFileSync(path, "utf-8");
|
|
136
|
+
} catch {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
for (const line of raw.split("\n")) {
|
|
140
|
+
if (!line.trim()) continue;
|
|
141
|
+
let entry: TranscriptEntry;
|
|
142
|
+
try {
|
|
143
|
+
entry = JSON.parse(line) as TranscriptEntry;
|
|
144
|
+
} catch {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const content = entry.message?.content;
|
|
148
|
+
if (content === undefined) continue;
|
|
149
|
+
|
|
150
|
+
if (entry.type === "user") {
|
|
151
|
+
if (typeof content === "string") {
|
|
152
|
+
pushUserText(content);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (!Array.isArray(content)) continue;
|
|
156
|
+
for (const part of content as ContentPart[]) {
|
|
157
|
+
if (part?.type === "text" && typeof part.text === "string") pushUserText(part.text);
|
|
158
|
+
else if (part?.type === "tool_result") {
|
|
159
|
+
const text = contentToText(part.content);
|
|
160
|
+
if (handoverHead && text.startsWith(handoverHead)) continue;
|
|
161
|
+
parts.push(text);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} else if (entry.type === "assistant" && Array.isArray(content)) {
|
|
165
|
+
for (const part of content as ContentPart[]) {
|
|
166
|
+
if (part?.type === "text" && typeof part.text === "string") parts.push(part.text);
|
|
167
|
+
else if (part?.type === "tool_use") {
|
|
168
|
+
const target = part.input?.file_path;
|
|
169
|
+
if (
|
|
170
|
+
resolvedHandover &&
|
|
171
|
+
HANDOVER_WRITERS.has(part.name ?? "") &&
|
|
172
|
+
typeof target === "string" &&
|
|
173
|
+
resolve(target) === resolvedHandover
|
|
174
|
+
) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
parts.push(JSON.stringify(part.input ?? {}));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return normalise(parts.join("\n"));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function footerFor(found: string[], missing: string[]): string {
|
|
187
|
+
const total = found.length + missing.length;
|
|
188
|
+
if (total === 0) return "Evidence: no checkable identifiers";
|
|
189
|
+
let footer = `Evidence: ${found.length}/${total} identifiers found in the transcript`;
|
|
190
|
+
if (missing.length > 0) {
|
|
191
|
+
const named = missing.slice(0, FOOTER_NAMED_MISSING).join(", ");
|
|
192
|
+
const more = missing.length - FOOTER_NAMED_MISSING;
|
|
193
|
+
footer += `; unverified: ${named}${more > 0 ? ` +${more} more` : ""}`;
|
|
194
|
+
}
|
|
195
|
+
return footer;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Check a handover's identifiers against the transcript(s) it was written
|
|
200
|
+
* from. Never throws: any failure degrades to a footer that says the check
|
|
201
|
+
* was unavailable, so the handover itself is always delivered.
|
|
202
|
+
*
|
|
203
|
+
* @param handoverMarkdown the handover text, returned to the reader unchanged
|
|
204
|
+
* @param handoverPath where the handover was written, if it was written
|
|
205
|
+
* to a file — its own Write/Edit is excluded as evidence
|
|
206
|
+
* @param transcriptPaths the session's transcript file(s)
|
|
207
|
+
*/
|
|
208
|
+
export function bindHandoverEvidence(
|
|
209
|
+
handoverMarkdown: string,
|
|
210
|
+
handoverPath: string | null,
|
|
211
|
+
transcriptPaths: string[]
|
|
212
|
+
): EvidenceResult {
|
|
213
|
+
try {
|
|
214
|
+
const tokens = extractCheckableTokens(handoverMarkdown);
|
|
215
|
+
if (tokens.length === 0) {
|
|
216
|
+
return { total: 0, found: [], missing: [], footer: footerFor([], []) };
|
|
217
|
+
}
|
|
218
|
+
const head = handoverMarkdown.trim().slice(0, ECHO_HEAD_LENGTH);
|
|
219
|
+
const haystack = buildHaystack(transcriptPaths, handoverPath, head);
|
|
220
|
+
const found: string[] = [];
|
|
221
|
+
const missing: string[] = [];
|
|
222
|
+
for (const token of tokens) (haystack.includes(token) ? found : missing).push(token);
|
|
223
|
+
return { total: tokens.length, found, missing, footer: footerFor(found, missing) };
|
|
224
|
+
} catch {
|
|
225
|
+
return { total: 0, found: [], missing: [], footer: "Evidence: check unavailable" };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { isWorkerSession } from "./worker-session.js";
|
|
3
|
+
|
|
4
|
+
describe("isWorkerSession", () => {
|
|
5
|
+
it("is true only for the exact launcher mark PAI_WORKER=1", () => {
|
|
6
|
+
expect(isWorkerSession({ PAI_WORKER: "1" })).toBe(true);
|
|
7
|
+
expect(isWorkerSession({ PAI_WORKER: "true" })).toBe(false);
|
|
8
|
+
expect(isWorkerSession({ PAI_WORKER: "" })).toBe(false);
|
|
9
|
+
expect(isWorkerSession({})).toBe(false);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("reads process.env by default", () => {
|
|
13
|
+
const prev = process.env.PAI_WORKER;
|
|
14
|
+
try {
|
|
15
|
+
process.env.PAI_WORKER = "1";
|
|
16
|
+
expect(isWorkerSession()).toBe(true);
|
|
17
|
+
delete process.env.PAI_WORKER;
|
|
18
|
+
expect(isWorkerSession()).toBe(false);
|
|
19
|
+
} finally {
|
|
20
|
+
if (prev === undefined) delete process.env.PAI_WORKER;
|
|
21
|
+
else process.env.PAI_WORKER = prev;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker-session detection.
|
|
3
|
+
*
|
|
4
|
+
* A disposable headless worker (a `claude -p` run started by an orchestrating
|
|
5
|
+
* session, possibly against a different model provider with a different
|
|
6
|
+
* context window) shares the project directory and the hook configuration
|
|
7
|
+
* with the real session that spawned it. Left alone, the hooks treat it as a
|
|
8
|
+
* session in its own right: they inject project context into it, create and
|
|
9
|
+
* rename a numbered session note for it, autosave it, and enqueue a
|
|
10
|
+
* model-written handover for it. Its compactions also land in the project's
|
|
11
|
+
* transcript folder, where they are indistinguishable from a real session's
|
|
12
|
+
* and drag the measured compaction trigger down (observed 2026-09-17: two
|
|
13
|
+
* workers compacting at ~151k tokens pulled a project's trigger from ~784k
|
|
14
|
+
* to ~151k, and the real session's handover fired at ~50k tokens).
|
|
15
|
+
*
|
|
16
|
+
* The launcher marks such sessions with `PAI_WORKER=1`. Every hook that does
|
|
17
|
+
* per-session bookkeeping returns immediately when this predicate is true.
|
|
18
|
+
* Deliberately NOT guarded: the security validator (a worker's shell
|
|
19
|
+
* commands must still be checked) and observability capture.
|
|
20
|
+
*/
|
|
21
|
+
export function isWorkerSession(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
22
|
+
return env.PAI_WORKER === "1";
|
|
23
|
+
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* IMPORTANT: This hook PRESERVES user content. It only updates "Current Session".
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
13
14
|
import { writeFileSync, existsSync, readFileSync, mkdirSync } from 'fs';
|
|
14
15
|
import { join } from 'path';
|
|
15
16
|
import {
|
|
@@ -131,6 +132,7 @@ ${formatSessionTodos(todos)}`;
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
async function main() {
|
|
135
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
134
136
|
try {
|
|
135
137
|
const chunks: Buffer[] = [];
|
|
136
138
|
for await (const chunk of process.stdin) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* PAI-safe: No sensitive data, just tool descriptions
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
10
11
|
import { execSync } from 'child_process';
|
|
11
12
|
|
|
12
13
|
// Action tools that warrant a tab title update
|
|
@@ -122,6 +123,7 @@ function generateTitle(toolName: string, input: HookInput['tool_input']): string
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
async function main() {
|
|
126
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
125
127
|
try {
|
|
126
128
|
const input = await readStdinWithTimeout();
|
|
127
129
|
const data: HookInput = JSON.parse(input);
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* and work items from earlier in the session.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
16
17
|
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
17
18
|
import { basename, dirname, join } from 'path';
|
|
18
19
|
import { tmpdir } from 'os';
|
|
@@ -34,6 +35,7 @@ import {
|
|
|
34
35
|
import { getContextFill, formatContextFill } from '../lib/context-fill.js';
|
|
35
36
|
import { contentToText, isNoiseFilePath, preferCwdFiles } from '../lib/transcript-text.js';
|
|
36
37
|
import { readContextHandoverCache } from '../lib/context-handover-cache.js';
|
|
38
|
+
import { bindHandoverEvidence } from '../lib/handover-evidence.js';
|
|
37
39
|
|
|
38
40
|
interface HookInput {
|
|
39
41
|
session_id: string;
|
|
@@ -515,6 +517,7 @@ function enqueueSessionSummary(payload: {
|
|
|
515
517
|
// ---------------------------------------------------------------------------
|
|
516
518
|
|
|
517
519
|
async function main() {
|
|
520
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
518
521
|
// Skip probe/health-check sessions (e.g. CodexBar ClaudeProbe)
|
|
519
522
|
if (isProbeSession()) {
|
|
520
523
|
process.exit(0);
|
|
@@ -714,11 +717,13 @@ async function main() {
|
|
|
714
717
|
// -------------------------------------------------------------------
|
|
715
718
|
let sourceLabel: string;
|
|
716
719
|
let handoverBlock: string;
|
|
720
|
+
let handoverSummary: string | null = null;
|
|
717
721
|
try {
|
|
718
722
|
const cachedHandover = readContextHandoverCache(hookInput.session_id);
|
|
719
723
|
const handoverIsFresh =
|
|
720
724
|
cachedHandover !== null &&
|
|
721
725
|
(!previousCompactionAt || new Date(cachedHandover.generatedAt) > new Date(previousCompactionAt));
|
|
726
|
+
handoverSummary = handoverIsFresh ? cachedHandover!.summary : null;
|
|
722
727
|
|
|
723
728
|
sourceLabel = handoverIsFresh
|
|
724
729
|
? `model-written handover (${cachedHandover!.model}, generated ${cachedHandover!.generatedAt}, ` +
|
|
@@ -739,6 +744,18 @@ async function main() {
|
|
|
739
744
|
handoverBlock = '';
|
|
740
745
|
}
|
|
741
746
|
|
|
747
|
+
// Evidence binding: which of the handover's identifiers the transcript
|
|
748
|
+
// actually contains. One footer line, content untouched; its own
|
|
749
|
+
// failure changes nothing else about the injection.
|
|
750
|
+
if (handoverSummary !== null && typeof hookInput.transcript_path === 'string') {
|
|
751
|
+
try {
|
|
752
|
+
const evidence = bindHandoverEvidence(handoverSummary, null, [hookInput.transcript_path]);
|
|
753
|
+
handoverBlock += `\n${evidence.footer}`;
|
|
754
|
+
} catch (err) {
|
|
755
|
+
console.error(`Evidence check skipped: ${err}`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
742
759
|
const injection = [
|
|
743
760
|
'<system-reminder>',
|
|
744
761
|
`SESSION STATE RECOVERED AFTER COMPACTION (${compactType}, ~${tokenDisplay} tokens)`,
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* route-agents-to-worker.ts — PreToolUse hook on the Agent tool.
|
|
5
|
+
*
|
|
6
|
+
* Denies every in-process subagent when worker providers are configured, so no
|
|
7
|
+
* worker ever runs on Anthropic. The deny reason tells the orchestrator how to
|
|
8
|
+
* delegate through `pai worker run` instead — same idea as the old
|
|
9
|
+
* route-agents-to-glm.sh, but provider-neutral and ledgered in the workers
|
|
10
|
+
* log dir.
|
|
11
|
+
*
|
|
12
|
+
* Allows (and does not touch) when:
|
|
13
|
+
* - the tool is not the Agent tool (this hook is Agent-matched, but stay safe)
|
|
14
|
+
* - ALLOW_ANTHROPIC_AGENTS=1 is in the environment (one-session bypass)
|
|
15
|
+
* - workers are off or no provider is configured
|
|
16
|
+
*
|
|
17
|
+
* Every decision is appended to the routing ledger read by `pai worker log`.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { readWorkersSection } from "../../../workers/config.js";
|
|
21
|
+
import { workersLogDir, ledgerPath } from "../../../workers/paths.js";
|
|
22
|
+
import { appendLedger } from "../../../workers/ledger.js";
|
|
23
|
+
|
|
24
|
+
interface HookInput {
|
|
25
|
+
session_id?: string;
|
|
26
|
+
cwd?: string;
|
|
27
|
+
tool_name?: string;
|
|
28
|
+
tool_input?: { description?: string; prompt?: string; subagent_type?: string } | string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ALLOW = JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow" } });
|
|
32
|
+
|
|
33
|
+
function deny(reason: string): string {
|
|
34
|
+
return JSON.stringify({
|
|
35
|
+
hookSpecificOutput: {
|
|
36
|
+
hookEventName: "PreToolUse",
|
|
37
|
+
permissionDecision: "deny",
|
|
38
|
+
permissionDecisionReason: reason,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function shortLabel(input: HookInput): string {
|
|
44
|
+
const desc =
|
|
45
|
+
typeof input.tool_input === "object" && input.tool_input !== null
|
|
46
|
+
? input.tool_input.description ?? ""
|
|
47
|
+
: "";
|
|
48
|
+
return desc.replace(/\s+/g, " ").trim().slice(0, 60);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function main(): Promise<void> {
|
|
52
|
+
let text = "";
|
|
53
|
+
try {
|
|
54
|
+
for await (const chunk of process.stdin) text += chunk;
|
|
55
|
+
} catch {
|
|
56
|
+
process.stdout.write(ALLOW);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!text.trim()) {
|
|
60
|
+
process.stdout.write(ALLOW);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let input: HookInput;
|
|
65
|
+
try {
|
|
66
|
+
input = JSON.parse(text) as HookInput;
|
|
67
|
+
} catch {
|
|
68
|
+
process.stdout.write(ALLOW);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (input.tool_name && input.tool_name !== "Agent") {
|
|
73
|
+
process.stdout.write(ALLOW);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const label = shortLabel(input);
|
|
78
|
+
const cwd = input.cwd ?? process.cwd();
|
|
79
|
+
|
|
80
|
+
const ledger = (event: string): void => {
|
|
81
|
+
try {
|
|
82
|
+
appendLedger(ledgerPath(workersLogDir(readWorkersSection().workers)), event, {
|
|
83
|
+
cwd,
|
|
84
|
+
...(label ? { label } : {}),
|
|
85
|
+
});
|
|
86
|
+
} catch {
|
|
87
|
+
// no config / no log dir yet — the decision below still stands
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (process.env.ALLOW_ANTHROPIC_AGENTS === "1") {
|
|
92
|
+
ledger("ALLOWED-ANTHROPIC-AGENT");
|
|
93
|
+
process.stdout.write(ALLOW);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let workers: ReturnType<typeof readWorkersSection>["workers"];
|
|
98
|
+
try {
|
|
99
|
+
workers = readWorkersSection().workers;
|
|
100
|
+
} catch {
|
|
101
|
+
// broken workers config must not break the session
|
|
102
|
+
process.stdout.write(ALLOW);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (!workers.enabled || Object.keys(workers.providers).length === 0) {
|
|
106
|
+
process.stdout.write(ALLOW);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
ledger("DENIED-ANTHROPIC-AGENT");
|
|
111
|
+
process.stdout.write(
|
|
112
|
+
deny(
|
|
113
|
+
"Agent tool is disabled: subagents run on the configured worker provider, not Anthropic. " +
|
|
114
|
+
"Delegate with Bash instead, in the background:\n\n" +
|
|
115
|
+
`pai worker run --label "${label || "task"}" --role research -p '<full, self-contained task spec>' ` +
|
|
116
|
+
"--allowedTools 'Read,Edit,Write,Bash,Grep,Glob' --output-format json\n\n" +
|
|
117
|
+
"- Run it with run_in_background: true and always with a timeout.\n" +
|
|
118
|
+
"- Use --role spotcheck (or implement) as the task demands.\n" +
|
|
119
|
+
"- Web research: add WebSearch,WebFetch to --allowedTools.\n" +
|
|
120
|
+
"- The answer is in the `result` field of the JSON it prints. Review the diff yourself.\n" +
|
|
121
|
+
"- pai worker ps lists running workers; pai worker follow <id> shows one live.\n" +
|
|
122
|
+
"Keep only orchestration, review and synthesis in this session. " +
|
|
123
|
+
"To run subagents on Anthropic for one session: start it with ALLOW_ANTHROPIC_AGENTS=1."
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
main().catch(() => process.stdout.write(ALLOW));
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* initialize-session.ts, load-core-context.ts, load-project-context.ts
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
23
24
|
import { existsSync, statSync, readFileSync, writeFileSync } from 'fs';
|
|
24
25
|
import { join } from 'path';
|
|
25
26
|
import { tmpdir } from 'os';
|
|
@@ -99,6 +100,7 @@ async function testStopHook() {
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
async function main() {
|
|
103
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
102
104
|
try {
|
|
103
105
|
// Check if this is a subagent session - if so, exit silently
|
|
104
106
|
const claudeProjectDir = process.env.CLAUDE_PROJECT_DIR || '';
|
|
@@ -25,12 +25,14 @@
|
|
|
25
25
|
* - Gives your AI immediate access to your complete personal context
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
28
29
|
import { readFileSync, existsSync } from 'fs';
|
|
29
30
|
import { join } from 'path';
|
|
30
31
|
import { PAI_DIR, SKILLS_DIR } from '../lib/pai-paths';
|
|
31
32
|
import { isProbeSession } from '../lib/project-utils';
|
|
32
33
|
|
|
33
34
|
async function main() {
|
|
35
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
34
36
|
try {
|
|
35
37
|
// Check if this is a subagent session - if so, exit silently
|
|
36
38
|
const claudeProjectDir = process.env.CLAUDE_PROJECT_DIR || '';
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* - Sending ntfy.sh notifications
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
19
20
|
import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
|
|
20
21
|
import { join, basename, dirname, resolve } from 'path';
|
|
21
22
|
import { homedir } from 'os';
|
|
@@ -141,6 +142,7 @@ interface HookInput {
|
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
async function main() {
|
|
145
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
144
146
|
console.error('\nload-project-context.ts starting...');
|
|
145
147
|
|
|
146
148
|
// Skip probe/health-check sessions (e.g. CodexBar ClaudeProbe)
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
* SessionStart(compact) → THIS HOOK reads that file → stdout → context
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
19
20
|
import { readFileSync, existsSync, unlinkSync } from 'fs';
|
|
20
21
|
import { join } from 'path';
|
|
21
22
|
import { tmpdir } from 'os';
|
|
23
|
+
import { resetHandoverTriggerState } from '../../../session/context-handover-trigger.js';
|
|
22
24
|
|
|
23
25
|
interface HookInput {
|
|
24
26
|
session_id: string;
|
|
@@ -29,6 +31,7 @@ interface HookInput {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
async function main() {
|
|
34
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
32
35
|
let hookInput: HookInput | null = null;
|
|
33
36
|
|
|
34
37
|
try {
|
|
@@ -53,6 +56,16 @@ async function main() {
|
|
|
53
56
|
process.exit(0);
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
// This hook fires exactly once per compaction for this session — the
|
|
60
|
+
// right moment to reset the handover-trigger marker (confirmed/pending
|
|
61
|
+
// thresholds) so the NEXT context-fill cycle gets its own handover
|
|
62
|
+
// instead of being judged "already done" forever. Unconditional: it must
|
|
63
|
+
// run even when no compact-state digest file was found below, since a
|
|
64
|
+
// compaction still happened. Never touches the separate handover CACHE
|
|
65
|
+
// file — that is what gets injected below and must survive this call.
|
|
66
|
+
// See resetHandoverTriggerState() for the full rationale.
|
|
67
|
+
resetHandoverTriggerState(hookInput.session_id);
|
|
68
|
+
|
|
56
69
|
// Look for the state file saved by context-compression-hook during PreCompact
|
|
57
70
|
const stateFile = join(tmpdir(), `pai-compact-state-${hookInput.session_id}.txt`);
|
|
58
71
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
3
4
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs';
|
|
4
5
|
import { join, basename, dirname } from 'path';
|
|
5
6
|
import { connect } from 'net';
|
|
@@ -704,6 +705,7 @@ function debug(msg: string): void {
|
|
|
704
705
|
}
|
|
705
706
|
|
|
706
707
|
async function main() {
|
|
708
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
707
709
|
if (isProbeSession()) {
|
|
708
710
|
process.exit(0);
|
|
709
711
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
3
4
|
import { readFileSync, existsSync } from 'fs';
|
|
4
5
|
|
|
5
6
|
async function delay(ms: number): Promise<void> {
|
|
@@ -145,6 +146,7 @@ function extractCompletionMessage(taskOutput: string): { message: string | null,
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
async function main() {
|
|
149
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
148
150
|
console.error('SubagentStop hook started');
|
|
149
151
|
// Read input from stdin with timeout
|
|
150
152
|
let input = '';
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Note: All context loading handled by PAI skill system (core identity in skill description, full context in SKILL.md)
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { isWorkerSession } from "../lib/worker-session.js";
|
|
9
10
|
import { execSync } from 'child_process';
|
|
10
11
|
|
|
11
12
|
interface HookInput {
|
|
@@ -42,6 +43,7 @@ async function readStdinWithTimeout(timeout: number = 5000): Promise<string> {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
async function main() {
|
|
46
|
+
if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping
|
|
45
47
|
try {
|
|
46
48
|
// Read the hook input from stdin
|
|
47
49
|
const input = await readStdinWithTimeout();
|
package/statusline-command.sh
CHANGED
|
@@ -533,3 +533,20 @@ if [ "$context_pct" -gt 0 ] 2>/dev/null; then
|
|
|
533
533
|
else
|
|
534
534
|
printf "${LINE3_PRIMARY}${EMOJI_GEM} Context${RESET}${LINE3_PRIMARY}${SEPARATOR_COLOR}: ${RESET}${LINE3_ACCENT}...${ac_suffix}${usage_suffix}${RESET}\n"
|
|
535
535
|
fi
|
|
536
|
+
|
|
537
|
+
# Line 4: workers launched from this session (running ones with their current
|
|
538
|
+
# step, plus today's finished count). Empty when this session has none.
|
|
539
|
+
# Prefers the standalone built script (plain node, no CLI startup); falls back
|
|
540
|
+
# to the pai CLI.
|
|
541
|
+
worker_status_cmd=""
|
|
542
|
+
if [ -x "${claude_dir}/worker-status-line.mjs" ]; then
|
|
543
|
+
worker_status_cmd="${claude_dir}/worker-status-line.mjs"
|
|
544
|
+
elif command -v pai >/dev/null 2>&1; then
|
|
545
|
+
worker_status_cmd="pai worker status-line"
|
|
546
|
+
fi
|
|
547
|
+
if [ -n "$worker_status_cmd" ]; then
|
|
548
|
+
worker_line="$("$worker_status_cmd" "${ITERM_SESSION_ID:-}" "${PWD:-}" 2>/dev/null)"
|
|
549
|
+
if [ -n "$worker_line" ]; then
|
|
550
|
+
printf "${LINE3_PRIMARY}🐝 ${RESET}${LINE3_ACCENT}%s${RESET}\n" "$worker_line"
|
|
551
|
+
fi
|
|
552
|
+
fi
|
package/dist/daemon-DRdoA489.mjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import "./db-Ca5qfsMC.mjs";
|
|
2
|
-
import "./utils-9Err2RBW.mjs";
|
|
3
|
-
import "./migrate-Cjzeefn9.mjs";
|
|
4
|
-
import "./db-a1ixZQjr.mjs";
|
|
5
|
-
import "./helpers-IjZkXBhj.mjs";
|
|
6
|
-
import "./sync-BWbe8JTg.mjs";
|
|
7
|
-
import "./embeddings-DOLZnT1X.mjs";
|
|
8
|
-
import "./search-Rpk1cSBC.mjs";
|
|
9
|
-
import "./context-handover-cache-PtNvj_8D.mjs";
|
|
10
|
-
import "./indexer-D7MvSQPY.mjs";
|
|
11
|
-
import "./config-B64vFg14.mjs";
|
|
12
|
-
import "./factory-Bsp7xOpO.mjs";
|
|
13
|
-
import { t as serve } from "./daemon-BZ93KRBo.mjs";
|
|
14
|
-
import "./state-WaXhLr6R.mjs";
|
|
15
|
-
import "./router-CsDm7HvK.mjs";
|
|
16
|
-
import "./tools-ceiy7ANX.mjs";
|
|
17
|
-
import "./detector--Gg5JRN5.mjs";
|
|
18
|
-
import "./work-queue-worker-B8W8_3Rn.mjs";
|
|
19
|
-
|
|
20
|
-
export { serve };
|