khala-cli 0.4.9 → 0.4.10

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 (45) hide show
  1. package/dist/THIRD_PARTY_NOTICES.txt +28 -0
  2. package/dist/{chunk-NIX5F4LT.mjs → chunk-2AMWQ7RB.mjs} +330 -66
  3. package/dist/{chunk-PAJ4JJPV.mjs → chunk-3E5DH3Y6.mjs} +43 -6
  4. package/dist/{chunk-NGSYOVQ4.mjs → chunk-4BP7V3OP.mjs} +2 -2
  5. package/dist/{chunk-TEFOQXBK.mjs → chunk-4BZQ4GVW.mjs} +3 -3
  6. package/dist/{chunk-ITFD3JZS.mjs → chunk-5JSGA6OB.mjs} +424 -78
  7. package/dist/{chunk-QKWEZXR3.mjs → chunk-6AQVW5W7.mjs} +12 -4
  8. package/dist/{chunk-UK5GJO4Y.mjs → chunk-6J2PUM7L.mjs} +6 -5
  9. package/dist/{chunk-P3YIN7G7.mjs → chunk-6SUV3EW6.mjs} +11 -10
  10. package/dist/{chunk-GSZADJMW.mjs → chunk-ARIDZOTI.mjs} +309 -81
  11. package/dist/chunk-C2WEDMAF.mjs +15 -0
  12. package/dist/{chunk-DJM23SIU.mjs → chunk-CHC4VZF5.mjs} +7 -8
  13. package/dist/{chunk-BLCRVRIK.mjs → chunk-CKLCR5G4.mjs} +456 -87
  14. package/dist/{chunk-7R3EIDGR.mjs → chunk-EVT2DWNZ.mjs} +1 -1
  15. package/dist/{chunk-KMLXOPBQ.mjs → chunk-FJVVSKZ3.mjs} +4 -2
  16. package/dist/{chunk-3KDOTR54.mjs → chunk-G3NMB4PY.mjs} +1 -1
  17. package/dist/{chunk-SSO5RFOX.mjs → chunk-HHEZVPRR.mjs} +1 -1
  18. package/dist/{chunk-BV45NK7K.mjs → chunk-JKOJQTFT.mjs} +100 -6
  19. package/dist/{chunk-T7IL7V7Q.mjs → chunk-NDMFD5IM.mjs} +13 -10
  20. package/dist/{chunk-TANCKTDK.mjs → chunk-OEV5DFLW.mjs} +70 -5
  21. package/dist/{chunk-BEO7WIIM.mjs → chunk-OIZ4R5H7.mjs} +28 -8
  22. package/dist/{chunk-FZEH2IXZ.mjs → chunk-PJKI7SRI.mjs} +176 -26
  23. package/dist/{chunk-NPSQNCOY.mjs → chunk-PN25BUQ4.mjs} +13 -4
  24. package/dist/{chunk-3R6HFSLL.mjs → chunk-QDPJ4VT2.mjs} +92 -38
  25. package/dist/{chunk-FO5GXCST.mjs → chunk-SRZTHCXP.mjs} +5 -5
  26. package/dist/{chunk-LBT47HLZ.mjs → chunk-TSK3HJEG.mjs} +10 -9
  27. package/dist/{chunk-6BO3JSUP.mjs → chunk-UX34IVN5.mjs} +2 -2
  28. package/dist/{chunk-V4F3SYKW.mjs → chunk-VAC3JTAV.mjs} +25 -17
  29. package/dist/{chunk-PDZ5Z5ZE.mjs → chunk-VDZK4LW3.mjs} +24 -9
  30. package/dist/chunk-W6HM6F2U.mjs +901 -0
  31. package/dist/{chunk-WDOHRV3D.mjs → chunk-WPNMWNCL.mjs} +14 -14
  32. package/dist/chunk-WUDXHF2Q.mjs +14 -0
  33. package/dist/{chunk-XBQDIKB5.mjs → chunk-WZJ6DQJV.mjs} +9 -8
  34. package/dist/{chunk-ANSR4IAW.mjs → chunk-XR625T3I.mjs} +2 -2
  35. package/dist/{chunk-K7V7X6WU.mjs → chunk-ZSKLNZWZ.mjs} +21 -4
  36. package/dist/iterm2_send.py +56 -0
  37. package/dist/khala.mjs +11 -11
  38. package/dist/web/assets/local-CSPuGnH2.css +1 -0
  39. package/dist/web/assets/local-Dtyxaacx.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-XUCWSYVS.mjs +0 -148
  44. package/dist/web/assets/local-CFzRHZpB.js +0 -9
  45. package/dist/web/assets/local-ciypUFoK.css +0 -1
@@ -1,25 +1,31 @@
1
1
  import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
2
2
  import {
3
3
  readPane
4
- } from "./chunk-QKWEZXR3.mjs";
4
+ } from "./chunk-6AQVW5W7.mjs";
5
5
  import {
6
6
  CURSOR_DEFAULT_SESSION,
7
7
  CURSOR_WORKSPACE_ENV,
8
8
  createMuseMonitorDriver,
9
+ createQwenSocketDriver,
9
10
  cursorSessionId,
10
11
  driverAllowed,
11
12
  museCliPath,
12
13
  museMonitorInstruction,
13
14
  museStopWakeText,
14
15
  museWatchCommand,
15
- readListeningMode
16
- } from "./chunk-NIX5F4LT.mjs";
16
+ qwenInboundAllowed,
17
+ qwenSession,
18
+ readListeningMode,
19
+ readWakeSettings,
20
+ verifyQwenTranscript
21
+ } from "./chunk-2AMWQ7RB.mjs";
17
22
  import {
18
23
  listChannels,
19
24
  readActivity,
20
25
  readCursor,
21
- unread
22
- } from "./chunk-TANCKTDK.mjs";
26
+ unread,
27
+ wakesAgent
28
+ } from "./chunk-OEV5DFLW.mjs";
23
29
  import {
24
30
  HARNESS_REGISTRY,
25
31
  SESSION_ID_PATTERN,
@@ -27,25 +33,29 @@ import {
27
33
  ensureStateDir,
28
34
  isHarnessId,
29
35
  nearestNonShellAncestor,
36
+ openSessionDir,
30
37
  readJson,
31
38
  readProcess,
39
+ readProcessArguments,
32
40
  stateRoot,
33
41
  walkAncestors,
34
42
  writeJsonAtomic
35
- } from "./chunk-NPSQNCOY.mjs";
43
+ } from "./chunk-PN25BUQ4.mjs";
36
44
  import {
45
+ CODEX_IDLE_WAKE_NOTICE,
37
46
  cancelAttempt,
38
47
  failAttempt,
39
48
  newNonce,
40
49
  readWakeState,
41
50
  recordAttempt,
51
+ resetWakeDriver,
42
52
  settleAttempts,
43
53
  wakeLine,
44
54
  withWakeLock
45
- } from "./chunk-K7V7X6WU.mjs";
55
+ } from "./chunk-ZSKLNZWZ.mjs";
46
56
  import {
47
57
  init_define_KHALA_BUNDLE
48
- } from "./chunk-7R3EIDGR.mjs";
58
+ } from "./chunk-EVT2DWNZ.mjs";
49
59
 
50
60
  // src/harness/index.ts
51
61
  init_define_KHALA_BUNDLE();
@@ -55,7 +65,14 @@ init_define_KHALA_BUNDLE();
55
65
 
56
66
  // src/harness/codecs/claude-style.ts
57
67
  init_define_KHALA_BUNDLE();
58
- var events = { SessionStart: "start", UserPromptSubmit: "prompt", PostToolUse: "tool", Stop: "stop" };
68
+
69
+ // src/harness/adapter.ts
70
+ init_define_KHALA_BUNDLE();
71
+ var STOP_NUDGE = "Khala: channel messages are waiting. Call khala_read.";
72
+
73
+ // src/harness/codecs/claude-style.ts
74
+ var events = { SessionStart: "start", UserPromptSubmit: "prompt", PostToolUse: "tool", Stop: "stop", StopFailure: "limit" };
75
+ var LIMIT_ERRORS = ["rate_limit", "billing_error"];
59
76
  var names = { prompt: "UserPromptSubmit", tool: "PostToolUse", stop: "Stop" };
60
77
  var claudeStyleCodec = {
61
78
  promptDeliversWithoutWake: true,
@@ -64,7 +81,7 @@ var claudeStyleCodec = {
64
81
  parse(stdin) {
65
82
  try {
66
83
  const input = JSON.parse(stdin);
67
- if (!input || typeof input.session_id !== "string" || typeof input.hook_event_name !== "string" || !Object.hasOwn(events, input.hook_event_name) || input.stop_hook_active !== void 0 && typeof input.stop_hook_active !== "boolean") return null;
84
+ if (!input || typeof input.session_id !== "string" || typeof input.hook_event_name !== "string" || !Object.hasOwn(events, input.hook_event_name) || input.stop_hook_active !== void 0 && typeof input.stop_hook_active !== "boolean" || input.hook_event_name === "StopFailure" && !LIMIT_ERRORS.includes(input.error)) return null;
68
85
  return {
69
86
  sessionId: input.session_id,
70
87
  event: events[input.hook_event_name],
@@ -78,10 +95,11 @@ var claudeStyleCodec = {
78
95
  }
79
96
  },
80
97
  noop: () => "",
81
- render(kind, frame) {
82
- return JSON.stringify(kind === "stop" ? { decision: "block", reason: frame } : { hookSpecificOutput: { hookEventName: names[kind], additionalContext: frame } }) + "\n";
98
+ render(kind, frame, stopContext = false) {
99
+ return JSON.stringify(kind === "stop" && !stopContext ? { decision: "block", reason: STOP_NUDGE } : { hookSpecificOutput: { hookEventName: names[kind], additionalContext: frame } }) + "\n";
83
100
  }
84
101
  };
102
+ var claudeCodec = { ...claudeStyleCodec, stopContextFloor: "2.1.307" };
85
103
 
86
104
  // src/wake/terminal/driver.ts
87
105
  init_define_KHALA_BUNDLE();
@@ -145,7 +163,7 @@ var runTerminalCommand = async (command, argv, env, signal) => {
145
163
  const { stdout } = await exec(command, [...argv], { env, signal, timeout: 5e3, maxBuffer: 1024 * 1024, shell: false });
146
164
  return stdout;
147
165
  };
148
- async function ownsTerminal(pid, tty, env, signal, run = runTerminalCommand, platform = process.platform) {
166
+ async function ownsTerminal(pid, tty, env, signal, run2 = runTerminalCommand, platform = process.platform) {
149
167
  if (!Number.isSafeInteger(pid) || pid <= 0 || !/^\/dev\/(?:pts\/\d+|tty[^\s/]+)$/.test(tty)) return false;
150
168
  try {
151
169
  if (platform === "linux") {
@@ -160,7 +178,7 @@ async function ownsTerminal(pid, tty, env, signal, run = runTerminalCommand, pla
160
178
  return ttyNr !== 0 && ttyNr === encoded && pgid > 0 && pgid === tpgid;
161
179
  }
162
180
  if (platform === "darwin") {
163
- const output = await run("ps", ["-o", "tty=,pgid=,tpgid=", "-p", String(pid)], { ...env, LC_ALL: "C" }, signal);
181
+ const output = await run2("ps", ["-o", "tty=,pgid=,tpgid=", "-p", String(pid)], { ...env, LC_ALL: "C" }, signal);
164
182
  const match = /^\s*(\S+)\s+(\d+)\s+(\d+)\s*$/.exec(output);
165
183
  return !!match && `/dev/${match[1]}` === tty && Number(match[2]) > 0 && match[2] === match[3];
166
184
  }
@@ -174,8 +192,8 @@ init_define_KHALA_BUNDLE();
174
192
  function tmuxArgv(pane, argv) {
175
193
  return [...pane.socket ? ["-S", pane.socket] : [], ...argv];
176
194
  }
177
- async function inspectTmux(pane, run, env, signal, readProcess2) {
178
- const query = await run("tmux", tmuxArgv(pane, [
195
+ async function inspectTmux(pane, run2, env, signal, readProcess2) {
196
+ const query = await run2("tmux", tmuxArgv(pane, [
179
197
  "display-message",
180
198
  "-p",
181
199
  "-t",
@@ -187,7 +205,7 @@ async function inspectTmux(pane, run, env, signal, readProcess2) {
187
205
  const pid = Number(fields[0]), cursorX = Number(fields[3]), cursorY = Number(fields[4]);
188
206
  if (!Number.isSafeInteger(pid) || pid <= 0 || !Number.isSafeInteger(cursorX) || cursorX < 0 || !Number.isSafeInteger(cursorY) || cursorY < 0 || !fields[5]) return null;
189
207
  if (pid !== pane.agentPid && !(await ancestors(pane.agentPid, readProcess2)).some((parent) => parent.pid === pid)) return null;
190
- const line = await run("tmux", tmuxArgv(pane, [
208
+ const line = await run2("tmux", tmuxArgv(pane, [
191
209
  "capture-pane",
192
210
  "-p",
193
211
  "-e",
@@ -200,21 +218,21 @@ async function inspectTmux(pane, run, env, signal, readProcess2) {
200
218
  ]), env, signal);
201
219
  return { tty: fields[5], cursorX, cursorY, line };
202
220
  }
203
- async function sendTmux(pane, text, enter, run, env, signal) {
204
- await run("tmux", tmuxArgv(pane, ["send-keys", "-t", pane.paneId, ...enter ? ["Enter"] : ["-l", text]]), env, signal);
221
+ async function sendTmux(pane, text, enter, run2, env, signal) {
222
+ await run2("tmux", tmuxArgv(pane, ["send-keys", "-t", pane.paneId, ...enter ? ["Enter"] : ["-l", text]]), env, signal);
205
223
  }
206
224
 
207
225
  // src/wake/terminal/wezterm.ts
208
226
  init_define_KHALA_BUNDLE();
209
- async function inspectWezterm(pane, run, env, signal) {
210
- const rows = JSON.parse(await run("wezterm", ["cli", "list", "--format", "json"], env, signal));
227
+ async function inspectWezterm(pane, run2, env, signal) {
228
+ const rows = JSON.parse(await run2("wezterm", ["cli", "list", "--format", "json"], env, signal));
211
229
  if (!Array.isArray(rows)) return { reason: "wezterm_pane_missing" };
212
230
  const row = rows.find((value) => value && typeof value === "object" && String(value.pane_id) === pane.paneId);
213
231
  if (!row) return { reason: "wezterm_pane_missing" };
214
232
  if (typeof row.tty_name !== "string" || !row.tty_name) return { reason: "wezterm_tty_unavailable" };
215
233
  if (!Number.isSafeInteger(row.cursor_x) || row.cursor_x < 0 || !Number.isSafeInteger(row.cursor_y) || row.cursor_y < 0)
216
234
  return { reason: "wezterm_cursor_unavailable" };
217
- const line = await run("wezterm", [
235
+ const line = await run2("wezterm", [
218
236
  "cli",
219
237
  "get-text",
220
238
  "--pane-id",
@@ -228,8 +246,67 @@ async function inspectWezterm(pane, run, env, signal) {
228
246
  const captured = line.replace(/\r\n(?:\x1b(?:\[[0-9;:]*m|\(B))*\n$/, "").replace(/\x1b\(B/g, "");
229
247
  return { view: { tty: row.tty_name, cursorX: row.cursor_x, cursorY: row.cursor_y, line: captured } };
230
248
  }
231
- async function sendWezterm(pane, text, enter, run, env, signal) {
232
- await run("wezterm", ["cli", "send-text", "--pane-id", pane.paneId, "--no-paste", enter ? "\r" : text], env, signal);
249
+ async function sendWezterm(pane, text, enter, run2, env, signal) {
250
+ await run2("wezterm", ["cli", "send-text", "--pane-id", pane.paneId, "--no-paste", enter ? "\r" : text], env, signal);
251
+ }
252
+
253
+ // src/wake/terminal/kitty.ts
254
+ init_define_KHALA_BUNDLE();
255
+ function kittyArgv(pane, args) {
256
+ if (!pane.socket?.startsWith("unix:") || pane.socket.length <= 5 || pane.socket.includes("\0")) throw new Error("kitty_unix_socket_required");
257
+ return ["@", "--to", pane.socket, ...args];
258
+ }
259
+ async function inspectKitty(pane, run2, env, signal, read) {
260
+ try {
261
+ const windows = JSON.parse(await run2("kitten", kittyArgv(pane, ["ls"]), env, signal));
262
+ const matches = windows.flatMap((os2) => os2.tabs.flatMap((tab) => tab.windows)).filter((window) => String(window.id) === pane.paneId);
263
+ if (matches.length !== 1) return { reason: "kitty_window_missing" };
264
+ const foreground = matches[0].foreground_processes;
265
+ if (!Array.isArray(foreground) || foreground.length === 0) return { reason: "kitty_foreground_unavailable" };
266
+ for (const process2 of foreground) {
267
+ if (!Number.isSafeInteger(process2.pid) || process2.pid <= 0 || process2.pid !== pane.agentPid && !(await ancestors(process2.pid, read)).some((parent) => parent.pid === pane.agentPid)) return { reason: "kitty_window_not_owned" };
268
+ }
269
+ const ttyName = (await run2("ps", ["-o", "tty=", "-p", String(foreground[0].pid)], { ...env, LC_ALL: "C" }, signal)).trim();
270
+ if (!/^(?:pts\/\d+|tty[^\s/]+)$/.test(ttyName)) return { reason: "kitty_tty_unavailable" };
271
+ const screen = await run2("kitten", kittyArgv(pane, ["get-text", "--match", `id:${pane.paneId}`, "--extent", "screen", "--ansi", "--add-cursor"]), env, signal);
272
+ const cursor2 = /\x1b\[\?25[hl]\x1b\[(\d+);(\d+)H(?:\x1b\[\?12[hl]|\x1b\[[1-6] q)\n?$/.exec(screen);
273
+ if (!cursor2) return { reason: "kitty_cursor_unavailable" };
274
+ const cursorY = Number(cursor2[1]) - 1, cursorX = Number(cursor2[2]) - 1;
275
+ const rows = screen.slice(0, cursor2.index).split("\n");
276
+ if (!Number.isSafeInteger(cursorX) || cursorX < 0 || !Number.isSafeInteger(cursorY) || cursorY < 0 || cursorY >= rows.length)
277
+ return { reason: "kitty_cursor_unavailable" };
278
+ const styling = rows.slice(0, cursorY).join("\n").match(/\x1b\[[0-9;:]*m/g)?.join("") ?? "";
279
+ return { view: { tty: `/dev/${ttyName}`, cursorX, cursorY, line: styling + rows[cursorY] } };
280
+ } catch (error) {
281
+ return { reason: error instanceof Error && error.message === "kitty_unix_socket_required" ? error.message : "kitty_probe_failed" };
282
+ }
283
+ }
284
+ async function sendKitty(pane, text, enter, run2, env, signal) {
285
+ await run2("kitten", kittyArgv(pane, ["send-text", "--match", `id:${pane.paneId}`, "--", enter ? "\\r" : text.replace(/\\/g, "\\\\")]), env, signal);
286
+ }
287
+
288
+ // src/wake/terminal/iterm2.ts
289
+ init_define_KHALA_BUNDLE();
290
+ import { fileURLToPath } from "node:url";
291
+ function iterm2ScriptPath() {
292
+ return fileURLToPath(new URL("./iterm2_send.py", import.meta.url));
293
+ }
294
+ async function inspectIterm2(pane, run2, env, signal) {
295
+ try {
296
+ const view = JSON.parse(await run2("python3", [iterm2ScriptPath(), pane.paneId], env, signal));
297
+ if (view.reason) return { reason: view.reason };
298
+ if (typeof view.tty !== "string" || !/^\/dev\/tty[^\s/]+$/.test(view.tty)) return { reason: "iterm2_tty_unavailable" };
299
+ if (typeof view.line !== "string" || !Number.isSafeInteger(view.cursorX) || view.cursorX < 0 || !Number.isSafeInteger(view.cursorY) || view.cursorY < 0) return { reason: "iterm2_cursor_unavailable" };
300
+ return { view };
301
+ } catch {
302
+ return { reason: "iterm2_python_api_unavailable" };
303
+ }
304
+ }
305
+ async function sendIterm2(pane, text, enter, run2, env, signal, expected) {
306
+ if (!expected) return "skipped";
307
+ const result = JSON.parse(await run2("python3", [iterm2ScriptPath(), pane.paneId, enter ? "\r" : text, JSON.stringify(expected)], env, signal));
308
+ if (result.status === "not_empty") return "skipped";
309
+ if (result.status !== "sent") throw new Error("iterm2_send_failed");
233
310
  }
234
311
 
235
312
  // src/wake/terminal/driver.ts
@@ -237,7 +314,7 @@ function promptPrefix(captured, cursorColumn) {
237
314
  return captured.replace(/\x1b\[[0-9;]*m/g, "").replace(/\r?\n$/, "").slice(0, cursorColumn).padEnd(cursorColumn, " ");
238
315
  }
239
316
  function createTerminalWakeDriver(guard, deps = {}) {
240
- const run = deps.run ?? runTerminalCommand;
317
+ const run2 = deps.run ?? runTerminalCommand;
241
318
  const read = deps.readProcess ?? readProcess;
242
319
  const platform = deps.platform ?? process.platform;
243
320
  const probe = async (ctx, empty, cleanup = false) => {
@@ -247,15 +324,16 @@ function createTerminalWakeDriver(guard, deps = {}) {
247
324
  if (!cleanup && !await driverAllowed(stateRoot(ctx.env), ctx.harness, "terminal", true)) return { reason: "terminal_consent_required" };
248
325
  if (!cleanup && (await readWakeState(ctx.files.dir)).terminal?.disabled) return { reason: "nonce_timeout" };
249
326
  const pane = await readPane(ctx.files);
250
- if (!pane) return { reason: ctx.harness === "codex" && (ctx.env.TMUX || ctx.env.WEZTERM_PANE) ? "terminal_capture_pending_prompt" : "no remote-control API" };
327
+ if (!pane) return { reason: ctx.env.ITERM_SESSION_ID && !/^w\d+t\d+p\d+:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(ctx.env.ITERM_SESSION_ID) ? "iterm2_session_id_invalid" : ctx.harness === "codex" && (ctx.env.TMUX || ctx.env.WEZTERM_PANE || ctx.env.KITTY_WINDOW_ID || ctx.env.ITERM_SESSION_ID) ? "terminal_capture_pending_prompt" : ctx.env.ITERM_SESSION_ID ? "iterm2_session_id_invalid" : "no remote-control API" };
251
328
  const activity = await readActivity(ctx.files);
252
329
  if (!cleanup && (activity.state !== "idle" || ctx.now - Date.parse(activity.updatedAt) < 3e4)) return { reason: "terminal_not_idle" };
253
330
  const agent = await read(pane.agentPid);
254
331
  if (!agent || pane.agentStartTime !== void 0 && pane.agentStartTime !== agent.startTime) return { reason: "terminal_agent_exited" };
255
- const inspection = pane.kind === "tmux" ? { view: await inspectTmux(pane, run, ctx.env, ctx.signal, read) ?? void 0 } : await inspectWezterm(pane, run, ctx.env, ctx.signal);
332
+ if (pane.kind === "iterm2" && platform !== "darwin") return { reason: "iterm2_requires_macos" };
333
+ const inspection = pane.kind === "tmux" ? { view: await inspectTmux(pane, run2, ctx.env, ctx.signal, read) ?? void 0 } : pane.kind === "kitty" ? await inspectKitty(pane, run2, ctx.env, ctx.signal, read) : pane.kind === "iterm2" ? await inspectIterm2(pane, run2, ctx.env, ctx.signal) : await inspectWezterm(pane, run2, ctx.env, ctx.signal);
256
334
  const view = inspection.view;
257
335
  if (!view) return { reason: inspection.reason ?? "terminal_pane_unsafe" };
258
- if (!await (deps.ownsTerminal ?? ownsTerminal)(pane.agentPid, view.tty, ctx.env, ctx.signal, run, platform))
336
+ if (!await (deps.ownsTerminal ?? ownsTerminal)(pane.agentPid, view.tty, ctx.env, ctx.signal, run2, platform))
259
337
  return { reason: "terminal_pane_not_owned" };
260
338
  if (empty && !isEmptyPrompt(view.line, view.cursorX, guard)) return { reason: "terminal_prompt_not_empty" };
261
339
  return { pane, view };
@@ -284,8 +362,8 @@ function createTerminalWakeDriver(guard, deps = {}) {
284
362
  if (!ready.pane || ctx.signal.aborted) return "skipped";
285
363
  const beforeSend = await readActivity(ctx.files);
286
364
  if (beforeSend.state !== "idle" || beforeSend.updatedAt !== activity.updatedAt) return "skipped";
287
- const send = ready.pane.kind === "tmux" ? sendTmux : sendWezterm;
288
- await send(ready.pane, line, false, run, ctx.env, ctx.signal);
365
+ const send = ready.pane.kind === "tmux" ? sendTmux : ready.pane.kind === "kitty" ? sendKitty : ready.pane.kind === "iterm2" ? sendIterm2 : sendWezterm;
366
+ if (await send(ready.pane, line, false, run2, ctx.env, ctx.signal, ready.view) === "skipped") return "skipped";
289
367
  const sameComposer = (final2) => {
290
368
  if (!final2.pane || !final2.view || final2.pane.agentPid !== ready.pane.agentPid || final2.pane.agentStartTime !== ready.pane.agentStartTime || final2.pane.capturedAt !== ready.pane.capturedAt || final2.view.tty !== ready.view.tty || final2.pane.kind !== ready.pane.kind || final2.pane.paneId !== ready.pane.paneId || final2.pane.socket !== ready.pane.socket) return false;
291
369
  const text = final2.view.line.replace(/\x1b\[[0-9;]*m/g, "").replace(/[ \r\n]+$/, "");
@@ -296,7 +374,7 @@ function createTerminalWakeDriver(guard, deps = {}) {
296
374
  const current2 = await safeProbe(cleanupCtx, false, true);
297
375
  if (!sameComposer(current2)) return;
298
376
  if (current2.pane.kind === "tmux") {
299
- await run("tmux", tmuxArgv(current2.pane, [
377
+ await run2("tmux", tmuxArgv(current2.pane, [
300
378
  "send-keys",
301
379
  "-t",
302
380
  current2.pane.paneId,
@@ -305,7 +383,7 @@ function createTerminalWakeDriver(guard, deps = {}) {
305
383
  "BSpace"
306
384
  ]), ctx.env, cleanupCtx.signal);
307
385
  } else {
308
- await sendWezterm(current2.pane, "\x7F".repeat(line.length), false, run, ctx.env, cleanupCtx.signal);
386
+ await send(current2.pane, "\x7F".repeat(line.length), false, run2, ctx.env, cleanupCtx.signal, current2.view);
309
387
  }
310
388
  };
311
389
  try {
@@ -328,7 +406,13 @@ function createTerminalWakeDriver(guard, deps = {}) {
328
406
  }
329
407
  return;
330
408
  }
331
- await send(final.pane, line, true, run, ctx.env, ctx.signal);
409
+ if (await send(final.pane, line, true, run2, ctx.env, ctx.signal, final.view) === "skipped") {
410
+ try {
411
+ await cleanup();
412
+ } finally {
413
+ await failAttempt(ctx.files.dir, nonce, ctx.now + 500);
414
+ }
415
+ }
332
416
  }
333
417
  };
334
418
  }
@@ -354,7 +438,7 @@ var emptyPrompt = { pattern: /^❯[\u00a0 ]?(Try ".*")?$/u, cursorColumn: 2 };
354
438
  var claude = {
355
439
  id: "claude",
356
440
  sessionSources: [{ kind: "env", resolve: (_meta, env) => env.CLAUDE_CODE_SESSION_ID, rejoinable: () => true }],
357
- codec: claudeStyleCodec,
441
+ codec: claudeCodec,
358
442
  restoreAtStartup: true,
359
443
  watcherStatus: true,
360
444
  emptyPrompt,
@@ -444,9 +528,10 @@ function createCodexQueueProcessPort(deps) {
444
528
  // src/wake/codex-probe.ts
445
529
  init_define_KHALA_BUNDLE();
446
530
  import { execFile as execFile2 } from "node:child_process";
447
- import { access, realpath, stat as stat2 } from "node:fs/promises";
531
+ import { access, mkdtemp, realpath, rm, stat as stat2 } from "node:fs/promises";
448
532
  import { constants } from "node:fs";
449
533
  import path2 from "node:path";
534
+ import os from "node:os";
450
535
  import { promisify as promisify2 } from "node:util";
451
536
  var execute = promisify2(execFile2);
452
537
  var probes = /* @__PURE__ */ new Map();
@@ -473,9 +558,18 @@ async function probeCodexQueue(env) {
473
558
  if (cached?.mtime === mtime) return cached.result;
474
559
  const binary = command;
475
560
  const result = (async () => {
561
+ let probeHome;
476
562
  try {
563
+ probeHome = await mkdtemp(path2.join(os.tmpdir(), "khala-codex-probe-"));
477
564
  const { stdout } = await execute(binary, ["queue", "--help"], {
478
- env: scrubbedQueueEnv(env),
565
+ env: {
566
+ ...scrubbedQueueEnv(env),
567
+ HOME: probeHome,
568
+ CODEX_HOME: probeHome,
569
+ XDG_CONFIG_HOME: probeHome,
570
+ XDG_DATA_HOME: probeHome,
571
+ XDG_STATE_HOME: probeHome
572
+ },
479
573
  timeout: 5e3,
480
574
  killSignal: "SIGKILL",
481
575
  maxBuffer: 64 * 1024,
@@ -484,6 +578,8 @@ async function probeCodexQueue(env) {
484
578
  return /--thread\b/.test(stdout) && /--message\b/.test(stdout) ? { available: true, command: binary } : { available: false, reason: "codex_queue_unavailable" };
485
579
  } catch {
486
580
  return { available: false, reason: "codex_queue_unavailable" };
581
+ } finally {
582
+ if (probeHome) await rm(probeHome, { recursive: true, force: true });
487
583
  }
488
584
  })();
489
585
  probes.set(binary, { mtime, result });
@@ -515,14 +611,8 @@ function createWakeLadder(deps) {
515
611
  };
516
612
  const evaluate = async () => {
517
613
  try {
518
- for (const driver2 of deps.drivers) await driver2.verify?.({
519
- files: deps.files,
520
- harness: deps.harness,
521
- sessionId: deps.sessionId,
522
- env: deps.env ?? process.env,
523
- signal: controller.signal,
524
- now: now()
525
- });
614
+ const verificationCtx = { files: deps.files, harness: deps.harness, sessionId: deps.sessionId, env: deps.env ?? process.env, signal: controller.signal, now: now() };
615
+ for (const driver2 of deps.drivers) await driver2.verify?.(verificationCtx);
526
616
  if (deps.drivers.some((driver2) => driver2.verification !== "none")) await settleAttempts(deps.files.dir, { now: now(), activity: await readActivity(deps.files) });
527
617
  const driverStates = await readWakeState(deps.files.dir);
528
618
  for (const [id, state] of Object.entries(driverStates)) {
@@ -539,10 +629,12 @@ function createWakeLadder(deps) {
539
629
  const session = await readJson(channel.files.session);
540
630
  const metadata = channel.legacy ? null : await readJson(path3.join(channel.files.dir, "channel.json"));
541
631
  currentIdentities.set(channel.key, JSON.stringify([session?.roomId ?? channel.roomId, session?.userId, metadata?.joinedAt]));
542
- if (await readListeningMode(channel.files) === "async") continue;
632
+ const mode = await readListeningMode(channel.files);
633
+ if (mode === "async") continue;
634
+ const you = (await readJson(channel.files.status))?.displayName;
543
635
  eligible.push({
544
636
  channel,
545
- counts: { messages: (await unread(channel.files)).entries.filter((entry) => entry.kind === "message" && entry.sender !== session?.userId).length },
637
+ counts: { messages: (await unread(channel.files)).entries.filter((entry) => entry.sender !== session?.userId && wakesAgent(entry, mode, you)).length },
546
638
  cursor: await readCursor(channel.files)
547
639
  });
548
640
  }
@@ -608,9 +700,9 @@ function createWakeLadder(deps) {
608
700
  const attemptAt = now();
609
701
  const nonce = newNonce();
610
702
  if (driver.verification !== "none") {
611
- await recordAttempt(deps.files.dir, { nonce, driver: driver.id, at: attemptAt, deadline: attemptAt + driver.deadlineMs, activityUpdatedAt: activity.updatedAt, ...driver.startsActivity ? { startsActivity: true } : {} });
703
+ await recordAttempt(deps.files.dir, { nonce, driver: driver.id, at: attemptAt, deadline: attemptAt + driver.deadlineMs, activityUpdatedAt: activity.updatedAt, ...driver.startsActivity ? { startsActivity: true } : {}, ...driver.verification === "transcript" ? { verification: "transcript" } : {} });
612
704
  if (!await stillEligible()) {
613
- await settleAttempts(deps.files.dir, { now: now(), activity: await readActivity(deps.files), promptText: "" });
705
+ await cancelAttempt(deps.files.dir, nonce);
614
706
  return;
615
707
  }
616
708
  }
@@ -707,7 +799,7 @@ var codexWakeDriver = createCodexWakeDriver();
707
799
 
708
800
  // src/harness/codex.ts
709
801
  var emptyPrompt2 = { pattern: /^› ?(Ask Codex to do anything)?$/u, cursorColumn: 2 };
710
- var install = async (flags, deps) => (await import("./chunk-BLCRVRIK.mjs")).runCodexInstall(flags, deps);
802
+ var install = async (flags, deps) => (await import("./chunk-CKLCR5G4.mjs")).runCodexInstall(flags, deps);
711
803
  var codex = {
712
804
  id: "codex",
713
805
  // A string metadata id wins even when empty or invalid; validation happens after selection.
@@ -739,11 +831,11 @@ async function recordHookSession(harness, sessionId, env, options = {}) {
739
831
  if (!parent) return;
740
832
  const dir = mappingDir(harness, env);
741
833
  await ensureStateDir(dir);
742
- const file2 = path4.join(dir, `${parent.pid}.json`);
834
+ const file3 = path4.join(dir, `${parent.pid}.json`);
743
835
  const at = (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
744
- const previous = await readJson(file2);
836
+ const previous = await readJson(file3);
745
837
  if (previous?.startTime === parent.startTime && typeof previous.at === "string" && previous.at > at) return;
746
- await writeJsonAtomic(file2, {
838
+ await writeJsonAtomic(file3, {
747
839
  sessionId,
748
840
  startTime: parent.startTime,
749
841
  at,
@@ -789,7 +881,7 @@ async function resolveSources(sources, meta, env, context) {
789
881
  }
790
882
 
791
883
  // src/harness/muse.ts
792
- var install2 = async (flags, deps) => (await import("./chunk-BLCRVRIK.mjs")).runMuseInstall(flags, deps);
884
+ var install2 = async (flags, deps) => (await import("./chunk-CKLCR5G4.mjs")).runMuseInstall(flags, deps);
793
885
  var muse = {
794
886
  id: "muse",
795
887
  sessionSources: [{ kind: "env", resolve: (_meta, env) => env.MUSE_SESSION_ID, rejoinable: () => true }, hookMapSource],
@@ -844,7 +936,7 @@ function createCopilotCodec(event) {
844
936
  },
845
937
  noop: () => "{}\n",
846
938
  render(kind, frame) {
847
- return JSON.stringify(kind === "stop" ? { decision: "block", reason: frame } : kind === "tool" ? { additionalContext: frame } : {}) + "\n";
939
+ return JSON.stringify(kind === "stop" ? { decision: "block", reason: STOP_NUDGE } : kind === "tool" ? { additionalContext: frame } : {}) + "\n";
848
940
  }
849
941
  };
850
942
  }
@@ -852,10 +944,10 @@ var copilotCodec = createCopilotCodec();
852
944
 
853
945
  // src/harness/copilot.ts
854
946
  var emptyPrompt3 = { pattern: /^❯ ?$/u, cursorColumn: 2 };
855
- var install3 = async (flags, deps) => (await import("./chunk-BLCRVRIK.mjs")).runCopilotInstall(flags, deps);
947
+ var install3 = async (flags, deps) => (await import("./chunk-CKLCR5G4.mjs")).runCopilotInstall(flags, deps);
856
948
  var copilot = {
857
949
  id: "copilot",
858
- sessionSources: [hookMapSource],
950
+ sessionSources: [{ kind: "env", resolve: (_meta, env) => env.COPILOT_AGENT_SESSION_ID, rejoinable: () => true }, hookMapSource],
859
951
  codec: copilotCodec,
860
952
  restoreAtStartup: true,
861
953
  install: install3,
@@ -895,12 +987,12 @@ var cursorCodec = {
895
987
  noop: (kind) => JSON.stringify(kind === "prompt" ? { continue: true } : {}) + "\n",
896
988
  render(kind, frame) {
897
989
  if (kind === "prompt") return cursorCodec.noop(kind);
898
- return JSON.stringify(kind === "stop" ? { followup_message: frame } : { additional_context: frame }) + "\n";
990
+ return JSON.stringify(kind === "stop" ? { followup_message: STOP_NUDGE } : { additional_context: frame }) + "\n";
899
991
  }
900
992
  };
901
993
 
902
994
  // src/harness/cursor.ts
903
- var install4 = async (flags, deps) => (await import("./chunk-BLCRVRIK.mjs")).runCursorInstall(flags, deps);
995
+ var install4 = async (flags, deps) => (await import("./chunk-CKLCR5G4.mjs")).runCursorInstall(flags, deps);
904
996
  var cursor = {
905
997
  id: "cursor",
906
998
  sessionSources: [{
@@ -914,6 +1006,56 @@ var cursor = {
914
1006
  uninstall: (flags, deps) => install4([...flags, "--uninstall"], deps)
915
1007
  };
916
1008
 
1009
+ // src/harness/qwen.ts
1010
+ init_define_KHALA_BUNDLE();
1011
+ import path5 from "node:path";
1012
+ var qwenCodec = {
1013
+ ...claudeStyleCodec,
1014
+ parse(stdin) {
1015
+ try {
1016
+ if (JSON.parse(stdin)?.agent_id !== void 0) return null;
1017
+ } catch {
1018
+ return null;
1019
+ }
1020
+ return claudeStyleCodec.parse(stdin);
1021
+ }
1022
+ };
1023
+ function createQwenBackgroundDriver(platform = process.platform) {
1024
+ return {
1025
+ id: "background-shell",
1026
+ rung: 2,
1027
+ optIn: false,
1028
+ minIdleMs: 0,
1029
+ deadlineMs: 3e4,
1030
+ verification: "none",
1031
+ async available(ctx) {
1032
+ const settings = await readWakeSettings(stateRoot(ctx.env));
1033
+ return platform === "win32" && !settings.off["qwen/socket"] && !settings.off["qwen/background-shell"] && await qwenInboundAllowed(ctx.env) && await (await import("./chunk-CHC4VZF5.mjs")).monitorArmed(ctx.files);
1034
+ },
1035
+ async unavailableReason(ctx) {
1036
+ if (!await qwenInboundAllowed(ctx.env)) return "qwen_held";
1037
+ return platform === "win32" ? "qwen_watcher_missing" : "driver_missing";
1038
+ },
1039
+ // Qwen itself owns the background tool. Khala never launches or injects input.
1040
+ wake: () => "skipped"
1041
+ };
1042
+ }
1043
+ var qwen = {
1044
+ id: "qwen",
1045
+ codec: qwenCodec,
1046
+ restoreAtStartup: true,
1047
+ sessionSources: [{ kind: "meta", resolve: (meta) => meta?.khala_session, rejoinable: () => true }, { kind: "env", resolve: (_meta, env) => qwenSession(env), rejoinable: () => true }, hookMapSource],
1048
+ install: async (flags, deps) => (await import("./chunk-CKLCR5G4.mjs")).runQwenInstall(flags, deps),
1049
+ uninstall: async (flags, deps) => (await import("./chunk-CKLCR5G4.mjs")).runQwenInstall([...flags, "--uninstall"], deps),
1050
+ wakeLadder: [createQwenSocketDriver(), createQwenBackgroundDriver()],
1051
+ async verifyWake(stdin, files, now) {
1052
+ const input = JSON.parse(stdin);
1053
+ if (typeof input.transcript_path !== "string") return;
1054
+ await writeJsonAtomic(path5.join(files.dir, "qwen-transcript.json"), { path: input.transcript_path });
1055
+ if (input.hook_event_name === "Stop") await verifyQwenTranscript(files.dir, input.transcript_path, now);
1056
+ }
1057
+ };
1058
+
917
1059
  // src/harness/gemini.ts
918
1060
  init_define_KHALA_BUNDLE();
919
1061
 
@@ -942,13 +1084,13 @@ var geminiCodec = {
942
1084
  },
943
1085
  noop: (kind) => JSON.stringify(kind === "stop" ? { decision: "allow" } : {}) + "\n",
944
1086
  render(kind, frame) {
945
- return JSON.stringify(kind === "stop" ? { decision: "deny", reason: frame } : { hookSpecificOutput: { hookEventName: names2[kind], additionalContext: frame } }) + "\n";
1087
+ return JSON.stringify(kind === "stop" ? { decision: "deny", reason: STOP_NUDGE } : { hookSpecificOutput: { hookEventName: names2[kind], additionalContext: frame } }) + "\n";
946
1088
  }
947
1089
  };
948
1090
 
949
1091
  // src/harness/gemini.ts
950
1092
  var emptyPrompt4 = { pattern: /^ >(?: {3}Type your message or @path\/to\/file)?$/u, cursorColumn: 3 };
951
- var install5 = async (flags, deps) => (await import("./chunk-BLCRVRIK.mjs")).runGeminiInstall(flags, deps);
1093
+ var install5 = async (flags, deps) => (await import("./chunk-CKLCR5G4.mjs")).runGeminiInstall(flags, deps);
952
1094
  var gemini = {
953
1095
  id: "gemini",
954
1096
  sessionSources: [{ kind: "env", resolve: (_meta, env) => env.GEMINI_SESSION_ID, rejoinable: () => true }, hookMapSource],
@@ -961,6 +1103,207 @@ var gemini = {
961
1103
  wakeWarningName: "gemini"
962
1104
  };
963
1105
 
1106
+ // src/harness/antigravity.ts
1107
+ init_define_KHALA_BUNDLE();
1108
+
1109
+ // src/harness/codecs/antigravity.ts
1110
+ init_define_KHALA_BUNDLE();
1111
+ var antigravityCodec = {
1112
+ promptDeliversWithoutWake: false,
1113
+ promptAcceptsContext: false,
1114
+ suppressOutputErrors: false,
1115
+ parse(stdin) {
1116
+ try {
1117
+ const input = JSON.parse(stdin.replace(/^\uFEFF/u, ""));
1118
+ if (!input || typeof input.conversationId !== "string") return null;
1119
+ let event;
1120
+ if (input.khalaHookEvent === "PreInvocation" && Number.isInteger(input.invocationNum) && input.invocationNum >= 0)
1121
+ event = input.invocationNum === 0 ? "prompt" : "tool";
1122
+ else if (input.khalaHookEvent === "Stop") event = "stop";
1123
+ else return null;
1124
+ return {
1125
+ sessionId: input.conversationId,
1126
+ event,
1127
+ continuation: false,
1128
+ ...typeof input.transcriptPath === "string" ? { transcriptPath: input.transcriptPath } : {},
1129
+ ...typeof input.workspacePaths?.[0] === "string" ? { workspace: input.workspacePaths[0] } : {}
1130
+ };
1131
+ } catch {
1132
+ return null;
1133
+ }
1134
+ },
1135
+ noop: (kind) => JSON.stringify(kind === "stop" ? { decision: "stop" } : {}) + "\n",
1136
+ render: (kind, frame) => JSON.stringify(kind === "stop" ? { decision: "continue", reason: STOP_NUDGE } : { injectSteps: [{ ephemeralMessage: frame }] }) + "\n"
1137
+ };
1138
+
1139
+ // src/wake/antigravity.ts
1140
+ init_define_KHALA_BUNDLE();
1141
+ import * as fs from "node:fs/promises";
1142
+ import { constants as constants2 } from "node:fs";
1143
+ import path6 from "node:path";
1144
+ import { execFile as execFile3 } from "node:child_process";
1145
+ var file = "antigravity-wake.json";
1146
+ var localAddress = (value) => typeof value === "string" && /^(?:localhost|127\.0\.0\.1|\[::1\]):([0-9]{1,5})$/u.test(value) && Number(value.slice(value.lastIndexOf(":") + 1)) > 0 && Number(value.slice(value.lastIndexOf(":") + 1)) <= 65535;
1147
+ async function credentials(files, sessionId) {
1148
+ let value;
1149
+ try {
1150
+ await ensureStateDir(files.dir);
1151
+ const handle = await fs.open(path6.join(files.dir, file), constants2.O_RDONLY | constants2.O_NONBLOCK | constants2.O_NOFOLLOW);
1152
+ try {
1153
+ const stat4 = await handle.stat();
1154
+ if (!stat4.isFile() || stat4.size > 16 * 1024 || process.platform !== "win32" && (stat4.mode & 63) !== 0 || typeof process.getuid === "function" && stat4.uid !== process.getuid()) return null;
1155
+ value = JSON.parse(await handle.readFile("utf8"));
1156
+ } finally {
1157
+ await handle.close();
1158
+ }
1159
+ } catch {
1160
+ return null;
1161
+ }
1162
+ return value && value.sessionId === sessionId && localAddress(value.address) && typeof value.token === "string" && value.token.length > 0 && !/[\r\n\0]/u.test(value.token) && typeof value.command === "string" && (path6.isAbsolute(value.command) || value.command === "agy") ? value : null;
1163
+ }
1164
+ async function stableAgyCommand(env) {
1165
+ for (const dir of (env.PATH ?? "").split(path6.delimiter).filter(Boolean)) {
1166
+ for (const name of process.platform === "win32" ? ["agy.exe", "agy.cmd", "agy"] : ["agy"]) {
1167
+ const candidate = path6.resolve(dir, name);
1168
+ try {
1169
+ await fs.access(candidate, constants2.X_OK);
1170
+ if ((await fs.stat(candidate)).isFile()) return candidate;
1171
+ } catch {
1172
+ }
1173
+ }
1174
+ }
1175
+ return env.ANTIGRAVITY_AGENTAPI_EXE || "agy";
1176
+ }
1177
+ async function registerAntigravityWake(env) {
1178
+ const sessionId = env.ANTIGRAVITY_CONVERSATION_ID;
1179
+ const address = env.ANTIGRAVITY_LS_ADDRESS;
1180
+ const token = env.ANTIGRAVITY_CSRF_TOKEN;
1181
+ const command = await stableAgyCommand(env);
1182
+ if (!sessionId || !SESSION_ID_PATTERN.test(sessionId) || !localAddress(address) || !token || /[\r\n\0]/u.test(token) || !(path6.isAbsolute(command) || command === "agy")) throw new Error("antigravity_registration_unavailable");
1183
+ const files = await openSessionDir("antigravity", sessionId, env);
1184
+ await withWakeLock(files.dir, "antigravity-credentials.lock", async () => {
1185
+ await writeJsonAtomic(path6.join(files.dir, file), { sessionId, address, token, command });
1186
+ await resetWakeDriver(files.dir, "antigravity-native");
1187
+ });
1188
+ }
1189
+ var run = (command, argv, env, signal) => new Promise((resolve) => {
1190
+ execFile3(command, [...argv], { env, signal, timeout: 1e4, maxBuffer: 64 * 1024, windowsHide: true }, (error) => resolve(!error));
1191
+ });
1192
+ function createAntigravityWakeDriver(execute2 = run) {
1193
+ return {
1194
+ id: "antigravity-native",
1195
+ rung: 1,
1196
+ optIn: true,
1197
+ minIdleMs: 0,
1198
+ deadlineMs: 3e4,
1199
+ verification: "nonce",
1200
+ async available(ctx) {
1201
+ const value = await credentials(ctx.files, ctx.sessionId);
1202
+ return !!value && !value.rejected;
1203
+ },
1204
+ async unavailableReason(ctx) {
1205
+ const value = await credentials(ctx.files, ctx.sessionId);
1206
+ return value?.rejected ? "antigravity_credentials_rejected" : "antigravity_credentials_missing";
1207
+ },
1208
+ async wake(ctx, line) {
1209
+ if (!line.startsWith(CODEX_IDLE_WAKE_NOTICE + " ") || !/^\(k-[0-9a-f]{8}\)$/u.test(line.slice(CODEX_IDLE_WAKE_NOTICE.length + 1))) throw new Error("invalid_wake_line");
1210
+ const value = await credentials(ctx.files, ctx.sessionId);
1211
+ if (!value || value.rejected || ctx.signal.aborted || (await readActivity(ctx.files)).state !== "idle") return "skipped";
1212
+ let accepted = false;
1213
+ try {
1214
+ accepted = await execute2(
1215
+ value.command,
1216
+ ["agentapi", "send-message", ctx.sessionId, line],
1217
+ { ...ctx.env, ANTIGRAVITY_LS_ADDRESS: value.address, ANTIGRAVITY_CSRF_TOKEN: value.token },
1218
+ ctx.signal
1219
+ );
1220
+ } catch {
1221
+ }
1222
+ if (!accepted && !ctx.signal.aborted) {
1223
+ await withWakeLock(ctx.files.dir, "antigravity-credentials.lock", async () => {
1224
+ const current = await credentials(ctx.files, ctx.sessionId);
1225
+ if (current?.token === value.token && current.address === value.address) await writeJsonAtomic(path6.join(ctx.files.dir, file), { ...value, rejected: true });
1226
+ });
1227
+ return "skipped";
1228
+ }
1229
+ }
1230
+ };
1231
+ }
1232
+ async function antigravityPromptText(input) {
1233
+ if (!input.transcriptPath) return void 0;
1234
+ let handle;
1235
+ try {
1236
+ handle = await fs.open(input.transcriptPath, constants2.O_RDONLY | constants2.O_NONBLOCK);
1237
+ const stat4 = await handle.stat();
1238
+ if (!stat4.isFile()) return void 0;
1239
+ const length = Math.min(stat4.size, 64 * 1024), start = stat4.size - length;
1240
+ const buffer = Buffer.alloc(length);
1241
+ const { bytesRead } = await handle.read(buffer, 0, length, start);
1242
+ let tail = buffer.subarray(0, bytesRead).toString("utf8");
1243
+ if (start > 0) tail = tail.slice(tail.indexOf("\n") + 1);
1244
+ return tail.split("\n").flatMap((line) => {
1245
+ try {
1246
+ const step = JSON.parse(line);
1247
+ if (typeof step.content !== "string") return [];
1248
+ if (step.source === "USER_EXPLICIT" && step.type === "USER_INPUT") {
1249
+ const nonce = step.content.match(/\(k-([0-9a-f]{8})\)$/u)?.[1];
1250
+ return nonce && wakeLine(nonce) === step.content ? [step.content] : [];
1251
+ }
1252
+ if (step.source !== "SYSTEM" || step.type !== "SYSTEM_MESSAGE") return [];
1253
+ return step.content.split(CODEX_IDLE_WAKE_NOTICE + " ").slice(1).flatMap((suffix) => {
1254
+ const nonce = suffix.match(/^\(k-[0-9a-f]{8}\)/u)?.[0];
1255
+ return nonce ? [CODEX_IDLE_WAKE_NOTICE + " " + nonce] : [];
1256
+ });
1257
+ } catch {
1258
+ return [];
1259
+ }
1260
+ }).join("\n") || void 0;
1261
+ } catch {
1262
+ return void 0;
1263
+ } finally {
1264
+ await handle?.close().catch(() => {
1265
+ });
1266
+ }
1267
+ }
1268
+
1269
+ // src/harness/antigravity.ts
1270
+ var emptyPrompt5 = { pattern: /^>$/u, cursorColumn: 2 };
1271
+ var install6 = async (flags, deps) => (await import("./chunk-CKLCR5G4.mjs")).runAntigravityInstall(flags, deps);
1272
+ var antigravity = {
1273
+ id: "antigravity",
1274
+ restoreAtStartup: true,
1275
+ codec: antigravityCodec,
1276
+ sessionSources: [
1277
+ { kind: "meta", resolve: (meta) => meta?.["antigravity.google/conversation_id"], rejoinable: () => true },
1278
+ { kind: "env", resolve: (_meta, env) => env.ANTIGRAVITY_CONVERSATION_ID, rejoinable: () => true },
1279
+ {
1280
+ kind: "process",
1281
+ rejoinable: () => true,
1282
+ async resolve(_meta, _env, context) {
1283
+ const read = context.readProcess ?? readProcess;
1284
+ for await (const parent of walkAncestors(context.pid ?? process.pid, read)) {
1285
+ if (!/^agy(?:\.exe)?$/iu.test(parent.command.split(/[\\/]/u).pop() ?? "")) continue;
1286
+ const args = await (context.readArguments ?? readProcessArguments)(parent.pid);
1287
+ if (!args || (await read(parent.pid))?.startTime !== parent.startTime) return null;
1288
+ for (let i = 1; i < args.length; i++) {
1289
+ if (args[i] === "--") break;
1290
+ if (args[i] === "--conversation") return args[i + 1] ?? "";
1291
+ if (args[i]?.startsWith("--conversation=")) return args[i].slice("--conversation=".length);
1292
+ }
1293
+ return null;
1294
+ }
1295
+ return null;
1296
+ }
1297
+ }
1298
+ ],
1299
+ install: install6,
1300
+ uninstall: (flags, deps) => install6([...flags, "--uninstall"], deps),
1301
+ emptyPrompt: emptyPrompt5,
1302
+ wakeLadder: [createAntigravityWakeDriver(), createTerminalWakeDriver(emptyPrompt5)],
1303
+ wakeWarningName: "antigravity",
1304
+ hookPromptText: antigravityPromptText
1305
+ };
1306
+
964
1307
  // src/harness/opencode.ts
965
1308
  init_define_KHALA_BUNDLE();
966
1309
 
@@ -987,13 +1330,13 @@ var opencodeCodec = {
987
1330
  }
988
1331
  },
989
1332
  noop: () => "",
990
- render: (_kind, frame) => frame
1333
+ render: (kind, frame) => kind === "stop" ? STOP_NUDGE : frame
991
1334
  };
992
1335
 
993
1336
  // src/wake/opencode.ts
994
1337
  init_define_KHALA_BUNDLE();
995
- import path5 from "node:path";
996
- var file = "opencode-wake.json";
1338
+ import path7 from "node:path";
1339
+ var file2 = "opencode-wake.json";
997
1340
  async function budget(files) {
998
1341
  const pairs = await Promise.all((await listChannels(files)).map(async (channel) => [channel.key, (await readCursor(channel.files)).deliveredCount]));
999
1342
  return JSON.stringify(pairs.sort(([a], [b]) => a.localeCompare(b)));
@@ -1007,20 +1350,20 @@ var opencodeWakeDriver = {
1007
1350
  deadlineMs: 3e3,
1008
1351
  verification: "nonce",
1009
1352
  async available(ctx) {
1010
- const prior = await readJson(path5.join(ctx.files.dir, file));
1353
+ const prior = await readJson(path7.join(ctx.files.dir, file2));
1011
1354
  return canAttempt(prior, await budget(ctx.files), ctx.now);
1012
1355
  },
1013
1356
  async wake(ctx, line) {
1014
1357
  return withWakeLock(ctx.files.dir, "opencode-native.lock", async () => {
1015
1358
  if (ctx.signal.aborted || (await readActivity(ctx.files)).state !== "idle") return "skipped";
1016
1359
  const key = await budget(ctx.files);
1017
- const prior = await readJson(path5.join(ctx.files.dir, file));
1360
+ const prior = await readJson(path7.join(ctx.files.dir, file2));
1018
1361
  if (!canAttempt(prior, key, ctx.now)) return "skipped";
1019
1362
  const channels = [];
1020
1363
  for (const channel of await listChannels(ctx.files)) {
1021
1364
  if (await readListeningMode(channel.files) !== "async") channels.push(channel.key);
1022
1365
  }
1023
- await writeJsonAtomic(path5.join(ctx.files.dir, file), {
1366
+ await writeJsonAtomic(path7.join(ctx.files.dir, file2), {
1024
1367
  line,
1025
1368
  at: ctx.now,
1026
1369
  budget: key,
@@ -1033,7 +1376,7 @@ var opencodeWakeDriver = {
1033
1376
  };
1034
1377
  async function pollOpenCodeWake(files, io, replay = false) {
1035
1378
  if (replay) return withWakeLock(files.dir, "opencode-native.lock", async () => {
1036
- const pending = await readJson(path5.join(files.dir, file));
1379
+ const pending = await readJson(path7.join(files.dir, file2));
1037
1380
  const activity = await readActivity(files);
1038
1381
  await settleAttempts(files.dir, { now: io.now().getTime(), activity });
1039
1382
  if (!pending || activity.state !== "idle" || !await driverAllowed(stateRoot(io.env), "opencode", opencodeWakeDriver.id, false) || (await readWakeState(files.dir))[opencodeWakeDriver.id]?.disabled) return;
@@ -1049,13 +1392,13 @@ async function pollOpenCodeWake(files, io, replay = false) {
1049
1392
  activityUpdatedAt: activity.updatedAt
1050
1393
  });
1051
1394
  const line = wakeLine(nonce);
1052
- await writeJsonAtomic(path5.join(files.dir, file), { ...pending, line, at });
1395
+ await writeJsonAtomic(path7.join(files.dir, file2), { ...pending, line, at });
1053
1396
  return line;
1054
1397
  });
1055
1398
  const ladder = createWakeLadder({
1056
1399
  files,
1057
1400
  harness: "opencode",
1058
- sessionId: path5.basename(files.dir),
1401
+ sessionId: path7.basename(files.dir),
1059
1402
  drivers: [opencodeWakeDriver],
1060
1403
  env: io.env,
1061
1404
  now: () => io.now().getTime(),
@@ -1067,31 +1410,33 @@ async function pollOpenCodeWake(files, io, replay = false) {
1067
1410
  await ladder.stop();
1068
1411
  }
1069
1412
  return withWakeLock(files.dir, "opencode-native.lock", async () => {
1070
- const pending = await readJson(path5.join(files.dir, file));
1413
+ const pending = await readJson(path7.join(files.dir, file2));
1071
1414
  if (!pending || pending.delivered || io.now().getTime() > pending.at + opencodeWakeDriver.deadlineMs || pending.budget !== await budget(files) || (await readActivity(files)).state !== "idle" || !await driverAllowed(stateRoot(io.env), "opencode", opencodeWakeDriver.id, false) || (await readWakeState(files.dir))[opencodeWakeDriver.id]?.disabled) return;
1072
1415
  let eligible = false;
1073
1416
  for (const channel of await listChannels(files)) {
1074
- if (await readListeningMode(channel.files) === "async") continue;
1417
+ const mode = await readListeningMode(channel.files);
1418
+ if (mode === "async") continue;
1075
1419
  const session = await readJson(channel.files.session);
1076
- if ((await unread(channel.files)).entries.some((entry) => entry.kind === "message" && entry.sender !== session?.userId)) eligible = true;
1420
+ const you = (await readJson(channel.files.status))?.displayName;
1421
+ if ((await unread(channel.files)).entries.some((entry) => entry.sender !== session?.userId && wakesAgent(entry, mode, you))) eligible = true;
1077
1422
  }
1078
1423
  if (!eligible) return;
1079
- await writeJsonAtomic(path5.join(files.dir, file), { ...pending, delivered: true });
1424
+ await writeJsonAtomic(path7.join(files.dir, file2), { ...pending, delivered: true });
1080
1425
  return pending.line;
1081
1426
  });
1082
1427
  }
1083
1428
 
1084
1429
  // src/harness/opencode.ts
1085
- var install6 = async (flags, deps) => (await import("./chunk-BLCRVRIK.mjs")).runOpenCodeInstall(flags, deps);
1430
+ var install7 = async (flags, deps) => (await import("./chunk-CKLCR5G4.mjs")).runOpenCodeInstall(flags, deps);
1086
1431
  var opencode = {
1087
1432
  id: "opencode",
1088
1433
  sessionSources: [{ kind: "meta", resolve: (meta) => meta?.khala_session, rejoinable: () => true }, hookMapSource],
1089
1434
  codec: opencodeCodec,
1090
- restoreAtStartup: false,
1435
+ restoreAtStartup: true,
1091
1436
  wakeLadder: [opencodeWakeDriver],
1092
1437
  pollIdleWake: pollOpenCodeWake,
1093
- install: install6,
1094
- uninstall: (flags, deps) => install6([...flags, "--uninstall"], deps)
1438
+ install: install7,
1439
+ uninstall: (flags, deps) => install7([...flags, "--uninstall"], deps)
1095
1440
  };
1096
1441
 
1097
1442
  // src/harness/generic.ts
@@ -1119,7 +1464,7 @@ function createAdapterRegistry(adapters) {
1119
1464
  }
1120
1465
  return Object.freeze({ resolve: (id) => byId.get(id) });
1121
1466
  }
1122
- var ADAPTERS = Object.freeze([claude, codex, cursor, opencode, muse, gemini, copilot, generic]);
1467
+ var ADAPTERS = Object.freeze([claude, codex, cursor, opencode, muse, gemini, antigravity, copilot, qwen, generic]);
1123
1468
  var registry = createAdapterRegistry(ADAPTERS);
1124
1469
  function adapterFor(id) {
1125
1470
  return registry.resolve(id) ?? (isHarnessId(id) ? genericAdapter(id) : void 0);
@@ -1128,6 +1473,7 @@ function adapterFor(id) {
1128
1473
  export {
1129
1474
  createCopilotCodec,
1130
1475
  createWakeLadder,
1476
+ registerAntigravityWake,
1131
1477
  SESSION_UNKNOWN_HINT,
1132
1478
  recordHookSession,
1133
1479
  resolveSources,