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.
Files changed (48) hide show
  1. package/dist/THIRD_PARTY_NOTICES.txt +28 -0
  2. package/dist/{chunk-DJM23SIU.mjs → chunk-3KHLSF4A.mjs} +7 -8
  3. package/dist/{chunk-PDZ5Z5ZE.mjs → chunk-4CENUF7M.mjs} +24 -9
  4. package/dist/{chunk-QKWEZXR3.mjs → chunk-4PUNVPC3.mjs} +12 -4
  5. package/dist/{chunk-WDOHRV3D.mjs → chunk-7FFHZ7EI.mjs} +14 -14
  6. package/dist/chunk-AQX2OYEF.mjs +19 -0
  7. package/dist/{chunk-ITFD3JZS.mjs → chunk-BVG7PJNZ.mjs} +424 -78
  8. package/dist/{chunk-NGSYOVQ4.mjs → chunk-CXVANPY5.mjs} +2 -2
  9. package/dist/chunk-CYINL2V3.mjs +20 -0
  10. package/dist/{chunk-KMLXOPBQ.mjs → chunk-E5QRD6W3.mjs} +4 -2
  11. package/dist/{chunk-FO5GXCST.mjs → chunk-FGHPOKXG.mjs} +5 -5
  12. package/dist/{chunk-FZEH2IXZ.mjs → chunk-GVZD4TLW.mjs} +180 -26
  13. package/dist/{chunk-V4F3SYKW.mjs → chunk-H2L5GQ5W.mjs} +25 -17
  14. package/dist/chunk-HZH74L5T.mjs +901 -0
  15. package/dist/{chunk-NPSQNCOY.mjs → chunk-JNSB3E6C.mjs} +13 -4
  16. package/dist/{chunk-7R3EIDGR.mjs → chunk-LDW4HOQW.mjs} +1 -1
  17. package/dist/chunk-LNKI3CZO.mjs +15 -0
  18. package/dist/{chunk-TEFOQXBK.mjs → chunk-MAIMFERG.mjs} +3 -3
  19. package/dist/chunk-MYIZQCRW.mjs +27 -0
  20. package/dist/{chunk-TANCKTDK.mjs → chunk-N6UYX3SY.mjs} +72 -5
  21. package/dist/{chunk-6BO3JSUP.mjs → chunk-NE4S7N3R.mjs} +2 -2
  22. package/dist/{chunk-BLCRVRIK.mjs → chunk-NJ5ICCVZ.mjs} +456 -87
  23. package/dist/{chunk-3R6HFSLL.mjs → chunk-NN5RHU6C.mjs} +92 -38
  24. package/dist/{chunk-NIX5F4LT.mjs → chunk-NOKTVPSY.mjs} +330 -66
  25. package/dist/{chunk-3KDOTR54.mjs → chunk-NUQSDSVT.mjs} +1 -1
  26. package/dist/{chunk-BEO7WIIM.mjs → chunk-OSVXLZKJ.mjs} +28 -8
  27. package/dist/{chunk-SSO5RFOX.mjs → chunk-R5NDTJRA.mjs} +1 -1
  28. package/dist/{chunk-K7V7X6WU.mjs → chunk-TVQ4MSEI.mjs} +21 -4
  29. package/dist/{chunk-BV45NK7K.mjs → chunk-VWWI4S5A.mjs} +100 -6
  30. package/dist/{chunk-UK5GJO4Y.mjs → chunk-WA5LB5GN.mjs} +6 -5
  31. package/dist/chunk-X3BAMHAR.mjs +14 -0
  32. package/dist/{chunk-PAJ4JJPV.mjs → chunk-YDEZFVF7.mjs} +43 -6
  33. package/dist/{chunk-GSZADJMW.mjs → chunk-YFX5NDSL.mjs} +338 -81
  34. package/dist/{chunk-ANSR4IAW.mjs → chunk-YIKJI26V.mjs} +2 -2
  35. package/dist/{chunk-T7IL7V7Q.mjs → chunk-YNUCRFM2.mjs} +13 -10
  36. package/dist/iterm2_send.py +56 -0
  37. package/dist/khala.mjs +11 -11
  38. package/dist/web/assets/local-DPcMk9Xz.css +1 -0
  39. package/dist/web/assets/local-SDOFp8OA.js +9 -0
  40. package/dist/web/index.html +2 -2
  41. package/package.json +1 -1
  42. package/dist/chunk-47XNLAJG.mjs +0 -38
  43. package/dist/chunk-LBT47HLZ.mjs +0 -19
  44. package/dist/chunk-P3YIN7G7.mjs +0 -26
  45. package/dist/chunk-XBQDIKB5.mjs +0 -18
  46. package/dist/chunk-XUCWSYVS.mjs +0 -148
  47. package/dist/web/assets/local-CFzRHZpB.js +0 -9
  48. 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-XUCWSYVS.mjs";
4
+ } from "./chunk-HZH74L5T.mjs";
9
5
  import {
10
6
  adapterFor,
11
7
  createCopilotCodec,
12
8
  recordHookSession
13
- } from "./chunk-ITFD3JZS.mjs";
9
+ } from "./chunk-BVG7PJNZ.mjs";
14
10
  import {
15
11
  capturePane
16
- } from "./chunk-QKWEZXR3.mjs";
12
+ } from "./chunk-4PUNVPC3.mjs";
17
13
  import {
18
14
  readListeningMode
19
- } from "./chunk-NIX5F4LT.mjs";
20
- import "./chunk-NGSYOVQ4.mjs";
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-TANCKTDK.mjs";
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-NPSQNCOY.mjs";
35
+ } from "./chunk-JNSB3E6C.mjs";
35
36
  import {
36
37
  settleAttempts
37
- } from "./chunk-K7V7X6WU.mjs";
38
+ } from "./chunk-TVQ4MSEI.mjs";
38
39
  import {
39
40
  init_define_KHALA_BUNDLE
40
- } from "./chunk-7R3EIDGR.mjs";
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 claudeTranscriptInterrupted(transcriptPath, activityAt) {
57
- if (!transcriptPath) return false;
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 false;
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 false;
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 false;
72
+ if (newline < 0) return [];
72
73
  tail = tail.slice(newline + 1);
73
74
  }
74
- const lines = tail.split("\n").filter((line) => line.trim() !== "");
75
- if (!lines.length) return false;
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.consumed.some(isWakeEntry)) - Number(a.group.consumed.some(isWakeEntry)));
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.consumed.some(isWakeEntry)) continue;
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.consumed.some(isWakeEntry)) break;
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.consumed.some(isWakeEntry);
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
- let adapter = (argv.length === 2 || museRoots || argv.length === 4 && argv[1] === "copilot" && argv[2] === "--event") && argv[0] === "--harness" ? adapterFor(argv[1]) : void 0;
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");