khala-cli 0.4.9 → 0.4.11
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/THIRD_PARTY_NOTICES.txt +28 -0
- package/dist/{chunk-DJM23SIU.mjs → chunk-3KHLSF4A.mjs} +7 -8
- package/dist/{chunk-PDZ5Z5ZE.mjs → chunk-4CENUF7M.mjs} +24 -9
- package/dist/{chunk-QKWEZXR3.mjs → chunk-4PUNVPC3.mjs} +12 -4
- package/dist/{chunk-WDOHRV3D.mjs → chunk-7FFHZ7EI.mjs} +14 -14
- package/dist/chunk-AQX2OYEF.mjs +19 -0
- package/dist/{chunk-ITFD3JZS.mjs → chunk-BVG7PJNZ.mjs} +424 -78
- package/dist/{chunk-NGSYOVQ4.mjs → chunk-CXVANPY5.mjs} +2 -2
- package/dist/chunk-CYINL2V3.mjs +20 -0
- package/dist/{chunk-KMLXOPBQ.mjs → chunk-E5QRD6W3.mjs} +4 -2
- package/dist/{chunk-FO5GXCST.mjs → chunk-FGHPOKXG.mjs} +5 -5
- package/dist/{chunk-FZEH2IXZ.mjs → chunk-GVZD4TLW.mjs} +180 -26
- package/dist/{chunk-V4F3SYKW.mjs → chunk-H2L5GQ5W.mjs} +25 -17
- package/dist/chunk-HZH74L5T.mjs +901 -0
- package/dist/{chunk-NPSQNCOY.mjs → chunk-JNSB3E6C.mjs} +13 -4
- package/dist/{chunk-7R3EIDGR.mjs → chunk-LDW4HOQW.mjs} +1 -1
- package/dist/chunk-LNKI3CZO.mjs +15 -0
- package/dist/{chunk-TEFOQXBK.mjs → chunk-MAIMFERG.mjs} +3 -3
- package/dist/chunk-MYIZQCRW.mjs +27 -0
- package/dist/{chunk-TANCKTDK.mjs → chunk-N6UYX3SY.mjs} +72 -5
- package/dist/{chunk-6BO3JSUP.mjs → chunk-NE4S7N3R.mjs} +2 -2
- package/dist/{chunk-BLCRVRIK.mjs → chunk-NJ5ICCVZ.mjs} +456 -87
- package/dist/{chunk-3R6HFSLL.mjs → chunk-NN5RHU6C.mjs} +92 -38
- package/dist/{chunk-NIX5F4LT.mjs → chunk-NOKTVPSY.mjs} +330 -66
- package/dist/{chunk-3KDOTR54.mjs → chunk-NUQSDSVT.mjs} +1 -1
- package/dist/{chunk-BEO7WIIM.mjs → chunk-OSVXLZKJ.mjs} +28 -8
- package/dist/{chunk-SSO5RFOX.mjs → chunk-R5NDTJRA.mjs} +1 -1
- package/dist/{chunk-K7V7X6WU.mjs → chunk-TVQ4MSEI.mjs} +21 -4
- package/dist/{chunk-BV45NK7K.mjs → chunk-VWWI4S5A.mjs} +100 -6
- package/dist/{chunk-UK5GJO4Y.mjs → chunk-WA5LB5GN.mjs} +6 -5
- package/dist/chunk-X3BAMHAR.mjs +14 -0
- package/dist/{chunk-PAJ4JJPV.mjs → chunk-YDEZFVF7.mjs} +43 -6
- package/dist/{chunk-GSZADJMW.mjs → chunk-YFX5NDSL.mjs} +338 -81
- package/dist/{chunk-ANSR4IAW.mjs → chunk-YIKJI26V.mjs} +2 -2
- package/dist/{chunk-T7IL7V7Q.mjs → chunk-YNUCRFM2.mjs} +13 -10
- package/dist/iterm2_send.py +56 -0
- package/dist/khala.mjs +11 -11
- package/dist/web/assets/local-DPcMk9Xz.css +1 -0
- package/dist/web/assets/local-SDOFp8OA.js +9 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-47XNLAJG.mjs +0 -38
- package/dist/chunk-LBT47HLZ.mjs +0 -19
- package/dist/chunk-P3YIN7G7.mjs +0 -26
- package/dist/chunk-XBQDIKB5.mjs +0 -18
- package/dist/chunk-XUCWSYVS.mjs +0 -148
- package/dist/web/assets/local-CFzRHZpB.js +0 -9
- package/dist/web/assets/local-ciypUFoK.css +0 -1
|
@@ -1,43 +1,44 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
isWakeEntry
|
|
4
|
-
} from "./chunk-47XNLAJG.mjs";
|
|
5
|
-
import "./chunk-BV45NK7K.mjs";
|
|
6
2
|
import {
|
|
7
3
|
wakeDisableNotice
|
|
8
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HZH74L5T.mjs";
|
|
9
5
|
import {
|
|
10
6
|
adapterFor,
|
|
11
7
|
createCopilotCodec,
|
|
12
8
|
recordHookSession
|
|
13
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-BVG7PJNZ.mjs";
|
|
14
10
|
import {
|
|
15
11
|
capturePane
|
|
16
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-4PUNVPC3.mjs";
|
|
17
13
|
import {
|
|
18
14
|
readListeningMode
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
15
|
+
} from "./chunk-NOKTVPSY.mjs";
|
|
16
|
+
import "./chunk-CXVANPY5.mjs";
|
|
21
17
|
import {
|
|
22
18
|
advanceCursor,
|
|
19
|
+
clearUsageLimit,
|
|
20
|
+
isWakeEntry,
|
|
23
21
|
listChannels,
|
|
22
|
+
markUsageLimit,
|
|
24
23
|
readActivity,
|
|
25
24
|
unread,
|
|
25
|
+
wakesAgent,
|
|
26
26
|
writeActivity
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-N6UYX3SY.mjs";
|
|
28
|
+
import "./chunk-VWWI4S5A.mjs";
|
|
28
29
|
import {
|
|
29
30
|
StateError,
|
|
30
31
|
TERMINAL_SESSION_DETAILS,
|
|
31
32
|
readJson,
|
|
32
33
|
readStatus,
|
|
33
34
|
sessionFiles
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-JNSB3E6C.mjs";
|
|
35
36
|
import {
|
|
36
37
|
settleAttempts
|
|
37
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-TVQ4MSEI.mjs";
|
|
38
39
|
import {
|
|
39
40
|
init_define_KHALA_BUNDLE
|
|
40
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-LDW4HOQW.mjs";
|
|
41
42
|
|
|
42
43
|
// hooks/deliver.ts
|
|
43
44
|
init_define_KHALA_BUNDLE();
|
|
@@ -53,26 +54,36 @@ import * as fs from "node:fs/promises";
|
|
|
53
54
|
import { constants } from "node:fs";
|
|
54
55
|
var MAX_TAIL_BYTES = 64 * 1024;
|
|
55
56
|
var MARKERS = /* @__PURE__ */ new Set(["[Request interrupted by user]", "[Request interrupted by user for tool use]"]);
|
|
56
|
-
async function
|
|
57
|
-
if (!transcriptPath) return
|
|
57
|
+
async function tailLines(transcriptPath) {
|
|
58
|
+
if (!transcriptPath) return [];
|
|
58
59
|
let handle;
|
|
59
60
|
try {
|
|
60
61
|
handle = await fs.open(transcriptPath, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
61
62
|
const stat2 = await handle.stat();
|
|
62
|
-
if (!stat2.isFile()) return
|
|
63
|
+
if (!stat2.isFile()) return [];
|
|
63
64
|
const length = Math.min(stat2.size, MAX_TAIL_BYTES);
|
|
64
65
|
const start = stat2.size - length;
|
|
65
66
|
const buffer = Buffer.alloc(length);
|
|
66
67
|
const { bytesRead } = await handle.read(buffer, 0, length, start);
|
|
67
|
-
if (bytesRead !== length) return
|
|
68
|
+
if (bytesRead !== length) return [];
|
|
68
69
|
let tail = buffer.toString("utf8");
|
|
69
70
|
if (start > 0) {
|
|
70
71
|
const newline = tail.indexOf("\n");
|
|
71
|
-
if (newline < 0) return
|
|
72
|
+
if (newline < 0) return [];
|
|
72
73
|
tail = tail.slice(newline + 1);
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
return tail.split("\n").filter((line) => line.trim() !== "");
|
|
76
|
+
} catch {
|
|
77
|
+
return [];
|
|
78
|
+
} finally {
|
|
79
|
+
await handle?.close().catch(() => {
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function claudeTranscriptInterrupted(transcriptPath, activityAt) {
|
|
84
|
+
const lines = await tailLines(transcriptPath);
|
|
85
|
+
if (!lines.length) return false;
|
|
86
|
+
try {
|
|
76
87
|
const entry = JSON.parse(lines.at(-1));
|
|
77
88
|
if (entry?.type !== "user" || entry.message?.role !== "user" || typeof entry.timestamp !== "string" || !(Date.parse(entry.timestamp) > Date.parse(activityAt))) return false;
|
|
78
89
|
const content = entry.message.content;
|
|
@@ -80,11 +91,24 @@ async function claudeTranscriptInterrupted(transcriptPath, activityAt) {
|
|
|
80
91
|
return Array.isArray(content) && content.length === 1 && content[0]?.type === "text" && typeof content[0].text === "string" && MARKERS.has(content[0].text);
|
|
81
92
|
} catch {
|
|
82
93
|
return false;
|
|
83
|
-
} finally {
|
|
84
|
-
await handle?.close().catch(() => {
|
|
85
|
-
});
|
|
86
94
|
}
|
|
87
95
|
}
|
|
96
|
+
async function claudeTranscriptVersion(transcriptPath) {
|
|
97
|
+
for (const line of (await tailLines(transcriptPath)).reverse()) {
|
|
98
|
+
try {
|
|
99
|
+
const version = JSON.parse(line)?.version;
|
|
100
|
+
if (typeof version === "string" && /^\d+\.\d+\.\d+$/.test(version)) return version;
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
function versionAtLeast(version, floor) {
|
|
107
|
+
if (version === void 0) return false;
|
|
108
|
+
const a = version.split(".").map(Number), b = floor.split(".").map(Number);
|
|
109
|
+
for (let i = 0; i < b.length; i++) if ((a[i] ?? 0) !== b[i]) return (a[i] ?? 0) > b[i];
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
88
112
|
|
|
89
113
|
// src/harness/deliver-core.ts
|
|
90
114
|
var MAX_FRAME_BYTES = 64 * 1024;
|
|
@@ -162,7 +186,8 @@ function selectFrames(groups, budget = MAX_FRAME_BYTES) {
|
|
|
162
186
|
}
|
|
163
187
|
return selected;
|
|
164
188
|
}
|
|
165
|
-
async function channelFrames(files, steerOnly, requireWake, io) {
|
|
189
|
+
async function channelFrames(files, steerOnly, requireWake, io, quiet, shown) {
|
|
190
|
+
const wakes = (group) => group.consumed.some((entry) => quiet ? wakesAgent(entry, group.mode, group.you) : isWakeEntry(entry));
|
|
166
191
|
const groups = [];
|
|
167
192
|
for (const channel of await listChannels(files)) {
|
|
168
193
|
const mode = await readListeningMode(channel.files);
|
|
@@ -171,6 +196,7 @@ async function channelFrames(files, steerOnly, requireWake, io) {
|
|
|
171
196
|
groups.push({
|
|
172
197
|
channel: { ...channel, ...status?.channelName !== void 0 ? { channelName: status.channelName } : {} },
|
|
173
198
|
...pending,
|
|
199
|
+
mode,
|
|
174
200
|
you: typeof status?.displayName === "string" ? status.displayName : void 0
|
|
175
201
|
});
|
|
176
202
|
}
|
|
@@ -179,16 +205,16 @@ async function channelFrames(files, steerOnly, requireWake, io) {
|
|
|
179
205
|
const reserved = selected.reduce((sum, group) => sum + Buffer.byteLength(group.frame), 0) + Math.max(0, selected.length - 1);
|
|
180
206
|
let spare = MAX_FRAME_BYTES - reserved;
|
|
181
207
|
const order = selected.map((group, index) => ({ group, index }));
|
|
182
|
-
if (requireWake) order.sort((a, b) => Number(b.group
|
|
208
|
+
if (requireWake) order.sort((a, b) => Number(wakes(b.group)) - Number(wakes(a.group)));
|
|
183
209
|
let hasWake = false;
|
|
184
210
|
const failures = [];
|
|
185
211
|
for (const { group, index } of order) {
|
|
186
|
-
if (requireWake && !hasWake && !group
|
|
212
|
+
if (requireWake && !hasWake && !wakes(group)) continue;
|
|
187
213
|
try {
|
|
188
214
|
let current = group;
|
|
189
215
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
190
|
-
if (!current.frame || requireWake && !hasWake && !current
|
|
191
|
-
if (await advanceCursor(current.channel.files, current.cursor, current.consumed) === "conflict") {
|
|
216
|
+
if (!current.frame || requireWake && !hasWake && !wakes(current)) break;
|
|
217
|
+
if (await advanceCursor(current.channel.files, current.cursor, current.consumed, shown) === "conflict") {
|
|
192
218
|
if (attempt === 0) {
|
|
193
219
|
const pending = await unread(current.channel.files);
|
|
194
220
|
const retry = selectFrame(
|
|
@@ -204,7 +230,7 @@ async function channelFrames(files, steerOnly, requireWake, io) {
|
|
|
204
230
|
}
|
|
205
231
|
spare += Buffer.byteLength(group.frame) - Buffer.byteLength(current.frame);
|
|
206
232
|
emitted.set(index, current.frame);
|
|
207
|
-
hasWake ||= current
|
|
233
|
+
hasWake ||= wakes(current);
|
|
208
234
|
break;
|
|
209
235
|
}
|
|
210
236
|
} catch (error) {
|
|
@@ -241,7 +267,7 @@ async function deliverCore(stdin, adapter, io) {
|
|
|
241
267
|
if (!codec) return 0;
|
|
242
268
|
const input = codec.parse(stdin);
|
|
243
269
|
let output = codec.noop(input?.event);
|
|
244
|
-
if (input && (input.event === "prompt" || input.event === "start") && input.sessionId && adapter.sessionSources.some((source) => source.kind === "hook-map")) {
|
|
270
|
+
if (input && (input.event === "prompt" || input.event === "start" || adapter.id === "opencode" && input.event === "stop") && input.sessionId && adapter.sessionSources.some((source) => source.kind === "hook-map")) {
|
|
245
271
|
try {
|
|
246
272
|
await recordHookSession(adapter.id, input.sessionId, io.env, { now: io.now, ...io.pid !== void 0 ? { pid: io.pid } : {}, ...io.readProcess ? { readProcess: io.readProcess } : {}, ...input.workspace !== void 0 ? { workspace: input.workspace } : {} });
|
|
247
273
|
} catch (error) {
|
|
@@ -257,6 +283,18 @@ async function deliverCore(stdin, adapter, io) {
|
|
|
257
283
|
}
|
|
258
284
|
if (files) {
|
|
259
285
|
try {
|
|
286
|
+
if (input.event === "limit") {
|
|
287
|
+
await markUsageLimit(files, io.now);
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
if (input.event === "tool" || input.event === "stop") await clearUsageLimit(files);
|
|
291
|
+
if (adapter.verifyWake) {
|
|
292
|
+
try {
|
|
293
|
+
await adapter.verifyWake(stdin, files, io.now().getTime());
|
|
294
|
+
} catch {
|
|
295
|
+
diagnostic(io, "wake_verification_failed");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
260
298
|
if (adapter.emptyPrompt && (input.event === "start" || input.event === "prompt")) {
|
|
261
299
|
try {
|
|
262
300
|
await capturePane(files, io.env, {
|
|
@@ -288,18 +326,21 @@ async function deliverCore(stdin, adapter, io) {
|
|
|
288
326
|
}
|
|
289
327
|
if (input.event !== "start") {
|
|
290
328
|
let steerOnly = input.event === "tool";
|
|
291
|
-
if (input.event === "prompt") {
|
|
292
|
-
const activity = await readActivity(files);
|
|
293
|
-
steerOnly = adapter.id === "claude" && activity.state === "busy" && Date.parse(activity.updatedAt) !== 0 && io.now().getTime() - Date.parse(activity.updatedAt) < CLAUDE_STALE_BUSY_MS && !await claudeTranscriptInterrupted(input.transcriptPath, activity.updatedAt);
|
|
329
|
+
if (adapter.hookPromptText || input.event === "prompt") {
|
|
294
330
|
try {
|
|
295
331
|
await settleAttempts(files.dir, {
|
|
296
332
|
now: io.now().getTime(),
|
|
297
|
-
activity,
|
|
298
|
-
promptText: input.promptText ?? ""
|
|
333
|
+
activity: await readActivity(files),
|
|
334
|
+
promptText: adapter.hookPromptText ? await adapter.hookPromptText(input, files) : input.promptText ?? "",
|
|
335
|
+
deferVerification: Boolean(adapter.hookPromptText)
|
|
299
336
|
});
|
|
300
337
|
} catch {
|
|
301
338
|
diagnostic(io, "wake_verification_failed");
|
|
302
339
|
}
|
|
340
|
+
}
|
|
341
|
+
if (input.event === "prompt") {
|
|
342
|
+
const activity = await readActivity(files);
|
|
343
|
+
steerOnly = adapter.id === "claude" && activity.state === "busy" && Date.parse(activity.updatedAt) !== 0 && io.now().getTime() - Date.parse(activity.updatedAt) < CLAUDE_STALE_BUSY_MS && !await claudeTranscriptInterrupted(input.transcriptPath, activity.updatedAt);
|
|
303
344
|
await writeActivity(files, "busy", io.now);
|
|
304
345
|
}
|
|
305
346
|
if (adapter.id === "claude" && input.event === "tool" && (await readActivity(files)).state === "busy") await writeActivity(files, "busy", io.now);
|
|
@@ -316,16 +357,21 @@ async function deliverCore(stdin, adapter, io) {
|
|
|
316
357
|
return 0;
|
|
317
358
|
}
|
|
318
359
|
}
|
|
360
|
+
const stopContext = input.event === "stop" && codec.stopContextFloor !== void 0 && versionAtLeast(await claudeTranscriptVersion(input.transcriptPath), codec.stopContextFloor);
|
|
319
361
|
const frame = await channelFrames(
|
|
320
362
|
files,
|
|
321
363
|
steerOnly,
|
|
322
364
|
input.event !== "prompt" || !codec.promptDeliversWithoutWake,
|
|
323
|
-
io
|
|
365
|
+
io,
|
|
366
|
+
input.event === "stop",
|
|
367
|
+
// Stop shows the frame only as verified Claude context; every other Stop is a fixed nudge,
|
|
368
|
+
// which consumes the batch without showing it and so earns no read receipt.
|
|
369
|
+
input.event !== "stop" || stopContext
|
|
324
370
|
);
|
|
325
371
|
if (frame) {
|
|
326
372
|
if (!wake) await writeActivity(files, "busy", io.now);
|
|
327
373
|
const notice = await wakeDisableNotice(files.dir);
|
|
328
|
-
output = (wake ? wake + "\n" : "") + codec.render(input.event, frame + (notice ? "\n" + notice : ""));
|
|
374
|
+
output = (wake ? wake + "\n" : "") + codec.render(input.event, frame + (notice ? "\n" + notice : ""), stopContext);
|
|
329
375
|
} else if (input.event === "stop") await writeActivity(files, "idle", io.now);
|
|
330
376
|
}
|
|
331
377
|
}
|
|
@@ -357,7 +403,15 @@ async function deliver(stdin, argv, io = {
|
|
|
357
403
|
let env = io.env;
|
|
358
404
|
const museRoots = argv.length === 6 && argv[0] === "--harness" && argv[1] === "muse" && argv[2] === "--state-home" && argv[4] === "--data-home" && path.isAbsolute(argv[3]) && path.isAbsolute(argv[5]);
|
|
359
405
|
if (museRoots) env = { ...env, XDG_STATE_HOME: argv[3], XDG_DATA_HOME: argv[5] };
|
|
360
|
-
|
|
406
|
+
const antigravityEvent = argv.length === 4 && argv[0] === "--harness" && argv[1] === "antigravity" && argv[2] === "--event" && ["PreInvocation", "Stop"].includes(argv[3]) ? argv[3] : void 0;
|
|
407
|
+
let adapter = (argv.length === 2 || museRoots || antigravityEvent || argv.length === 4 && argv[1] === "copilot" && argv[2] === "--event") && argv[0] === "--harness" ? adapterFor(argv[1]) : void 0;
|
|
408
|
+
if (antigravityEvent) {
|
|
409
|
+
try {
|
|
410
|
+
stdin = JSON.stringify({ ...JSON.parse(stdin), khalaHookEvent: antigravityEvent });
|
|
411
|
+
} catch {
|
|
412
|
+
stdin = "{}";
|
|
413
|
+
}
|
|
414
|
+
}
|
|
361
415
|
if (adapter?.id === "copilot" && argv.length === 4) adapter = { ...adapter, codec: createCopilotCodec(argv[3]) };
|
|
362
416
|
if (!adapter?.codec) {
|
|
363
417
|
diagnostic(io, "invalid_harness");
|