sessionpipe-conformance 0.9.0 → 0.11.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/cli.js +1010 -755
- package/dist/cli.js.map +4 -4
- package/dist/index.js +1007 -752
- package/dist/index.js.map +4 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,25 +1,19 @@
|
|
|
1
|
-
// sessionpipe-conformance 0.
|
|
1
|
+
// sessionpipe-conformance 0.11.0 · Apache-2.0 · https://sessionpipe.org
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
5
5
|
|
|
6
|
-
// ../core/src/adapters/antigravity.ts
|
|
7
|
-
import {
|
|
8
|
-
existsSync as existsSync2,
|
|
9
|
-
mkdirSync as mkdirSync2,
|
|
10
|
-
readdirSync as readdirSync2,
|
|
11
|
-
readFileSync as readFileSync2,
|
|
12
|
-
realpathSync,
|
|
13
|
-
statSync as statSync2,
|
|
14
|
-
unlinkSync as unlinkSync2,
|
|
15
|
-
writeFileSync as writeFileSync2
|
|
16
|
-
} from "node:fs";
|
|
17
|
-
import path3 from "node:path";
|
|
18
|
-
|
|
19
6
|
// ../core/src/paths.ts
|
|
20
7
|
import os from "node:os";
|
|
21
8
|
var homeDir = () => os.homedir();
|
|
22
9
|
|
|
10
|
+
// ../core/src/adapters/claude-code.ts
|
|
11
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
14
|
+
import { userInfo } from "node:os";
|
|
15
|
+
import path3 from "node:path";
|
|
16
|
+
|
|
23
17
|
// ../core/src/readers/files.ts
|
|
24
18
|
import { closeSync, fstatSync, openSync, readdirSync, readSync, statSync } from "node:fs";
|
|
25
19
|
import path from "node:path";
|
|
@@ -110,6 +104,38 @@ function recentFiles(root, depth, sinceMs, match) {
|
|
|
110
104
|
return out;
|
|
111
105
|
}
|
|
112
106
|
|
|
107
|
+
// ../core/src/readers/git.ts
|
|
108
|
+
import { execFileSync } from "node:child_process";
|
|
109
|
+
var cache = /* @__PURE__ */ new Map();
|
|
110
|
+
function gitFacts(cwd) {
|
|
111
|
+
if (!cwd) return {};
|
|
112
|
+
const hit = cache.get(cwd);
|
|
113
|
+
if (hit) return hit;
|
|
114
|
+
const run = (args) => {
|
|
115
|
+
try {
|
|
116
|
+
return execFileSync("git", ["-C", cwd, ...args], {
|
|
117
|
+
encoding: "utf8",
|
|
118
|
+
timeout: 1500,
|
|
119
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
120
|
+
}).trim();
|
|
121
|
+
} catch {
|
|
122
|
+
return "";
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const out = {};
|
|
126
|
+
const repo = run(["remote", "get-url", "origin"]);
|
|
127
|
+
if (repo) out.repo = stripCredentials(repo);
|
|
128
|
+
const branch = run(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
129
|
+
if (branch && branch !== "HEAD") out.branch = branch;
|
|
130
|
+
cache.set(cwd, out);
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
function stripCredentials(url) {
|
|
134
|
+
const ssh = /^(?:ssh:\/\/)?git@([^:/]+)[:/](.+?)(?:\.git)?$/.exec(url);
|
|
135
|
+
if (ssh) return `${ssh[1]}/${ssh[2]}`;
|
|
136
|
+
return url.replace(/\/\/[^/@]*@/, "//").replace(/^https?:\/\//, "").replace(/\.git$/, "");
|
|
137
|
+
}
|
|
138
|
+
|
|
113
139
|
// ../core/src/adapters/codex-rollout.ts
|
|
114
140
|
function codexSubagent(meta) {
|
|
115
141
|
if (meta?.type !== "session_meta") return false;
|
|
@@ -269,8 +295,10 @@ function readJsonFile(file) {
|
|
|
269
295
|
try {
|
|
270
296
|
raw = readFileSync(file, "utf8");
|
|
271
297
|
} catch {
|
|
272
|
-
return { ok: true, existed: false, value: {}, raw: "", indent: " ", eol: "\n", finalNewline: true };
|
|
298
|
+
return { ok: true, existed: false, value: {}, raw: "", indent: " ", eol: "\n", finalNewline: true, bom: false };
|
|
273
299
|
}
|
|
300
|
+
const bom = raw.charCodeAt(0) === 65279;
|
|
301
|
+
if (bom) raw = raw.slice(1);
|
|
274
302
|
let value;
|
|
275
303
|
try {
|
|
276
304
|
value = JSON.parse(raw);
|
|
@@ -290,12 +318,13 @@ function readJsonFile(file) {
|
|
|
290
318
|
raw,
|
|
291
319
|
indent,
|
|
292
320
|
eol,
|
|
293
|
-
finalNewline: /\r?\n$/.test(raw)
|
|
321
|
+
finalNewline: /\r?\n$/.test(raw),
|
|
322
|
+
bom
|
|
294
323
|
};
|
|
295
324
|
}
|
|
296
325
|
function formatJson(value, f) {
|
|
297
326
|
const text = JSON.stringify(value, null, f.indent).replace(/\n/g, f.eol);
|
|
298
|
-
return f.finalNewline ? text + f.eol : text;
|
|
327
|
+
return (f.bom ? "\uFEFF" : "") + (f.finalNewline ? text + f.eol : text);
|
|
299
328
|
}
|
|
300
329
|
function readJson(file) {
|
|
301
330
|
const f = readJsonFile(file);
|
|
@@ -391,74 +420,79 @@ function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
|
391
420
|
return current === events.length ? "current" : "stale";
|
|
392
421
|
}
|
|
393
422
|
|
|
394
|
-
// ../core/src/adapters/
|
|
395
|
-
var NAME = "
|
|
396
|
-
var
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
423
|
+
// ../core/src/adapters/claude-code.ts
|
|
424
|
+
var NAME = "claude-code";
|
|
425
|
+
var CLAUDE_EVENTS = [
|
|
426
|
+
"SessionStart",
|
|
427
|
+
"UserPromptSubmit",
|
|
428
|
+
"PreToolUse",
|
|
429
|
+
"PostToolUse",
|
|
430
|
+
"PostToolUseFailure",
|
|
431
|
+
"PermissionRequest",
|
|
432
|
+
"Notification",
|
|
433
|
+
"Stop",
|
|
434
|
+
"SubagentStart",
|
|
435
|
+
"SubagentStop",
|
|
436
|
+
"PreCompact",
|
|
437
|
+
"PostCompact",
|
|
438
|
+
"SessionEnd",
|
|
439
|
+
"StopFailure"
|
|
440
|
+
];
|
|
441
|
+
var LEAN_SKIP = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUseFailure"]);
|
|
442
|
+
var eventsFor = (lean) => lean ? CLAUDE_EVENTS.filter((e) => !LEAN_SKIP.has(e)) : [...CLAUDE_EVENTS];
|
|
443
|
+
var OPTS = {
|
|
444
|
+
matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0,
|
|
445
|
+
timeout: 5,
|
|
446
|
+
// The hook waits up to 125 s for a verified control answer, and only when control
|
|
447
|
+
// is paired on this machine; everywhere else it exits in milliseconds as before.
|
|
448
|
+
timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
|
|
449
|
+
};
|
|
450
|
+
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
451
|
+
function claudeDirs(env = process.env, home = env.HOME || homeDir()) {
|
|
452
|
+
const out = [];
|
|
453
|
+
const add = (d) => {
|
|
454
|
+
if (!d) return;
|
|
455
|
+
const r = path3.resolve(d);
|
|
456
|
+
if (!out.includes(r)) out.push(r);
|
|
457
|
+
};
|
|
458
|
+
add(path3.join(home, ".claude"));
|
|
459
|
+
add(env.CLAUDE_CONFIG_DIR);
|
|
460
|
+
try {
|
|
461
|
+
for (const e of readdirSync2(home, { withFileTypes: true })) {
|
|
462
|
+
if (!e.isDirectory() || !/^\.claude[-_.]/.test(e.name)) continue;
|
|
463
|
+
const d = path3.join(home, e.name);
|
|
464
|
+
if (existsSync2(path3.join(d, "projects")) || existsSync2(path3.join(d, "settings.json"))) add(d);
|
|
465
|
+
}
|
|
466
|
+
} catch {
|
|
467
|
+
}
|
|
468
|
+
return out.filter((d, i) => i === 0 || existsSync2(d));
|
|
411
469
|
}
|
|
412
|
-
var
|
|
470
|
+
var settingsIn = (dir) => path3.join(dir, "settings.json");
|
|
471
|
+
var configDirOf = (transcript) => path3.dirname(path3.dirname(path3.dirname(transcript)));
|
|
472
|
+
var validTool = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
473
|
+
var endReason = (r) => r === "clear" || r === "logout" || r === "exit" ? r : r === "prompt_input_exit" ? "exit" : "other";
|
|
474
|
+
var notifKind = (t) => t === "permission_prompt" ? "permission" : t === "idle_prompt" ? "idle" : t === "elicitation_dialog" ? "elicitation" : "question";
|
|
475
|
+
var claudeCode = {
|
|
413
476
|
name: NAME,
|
|
414
|
-
events:
|
|
415
|
-
detect: (env = process.env) =>
|
|
416
|
-
configFiles: (env = process.env) =>
|
|
417
|
-
install(cmd, env = process.env) {
|
|
418
|
-
const
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
hooks
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
return
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
434
|
-
];
|
|
435
|
-
},
|
|
436
|
-
uninstall(env = process.env, opts = {}) {
|
|
437
|
-
const file = hooksFile(env);
|
|
438
|
-
if (!existsSync2(file)) return [{ file, changed: false }];
|
|
439
|
-
const f = readJsonFile(file);
|
|
440
|
-
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
441
|
-
const hooks = f.value;
|
|
442
|
-
if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];
|
|
443
|
-
delete hooks[HOOK_NAME];
|
|
444
|
-
if (opts.created?.includes(file) && !Object.keys(hooks).length) {
|
|
445
|
-
unlinkSync2(file);
|
|
446
|
-
return [{ file, changed: true, note: "removed (sessionpipe created it)" }];
|
|
447
|
-
}
|
|
448
|
-
writeFileSync2(file, formatJson(hooks, f));
|
|
449
|
-
return [{ file, changed: true }];
|
|
450
|
-
},
|
|
451
|
-
installed(cmd, env = process.env) {
|
|
452
|
-
const file = hooksFile(env);
|
|
453
|
-
const f = readJsonFile(file);
|
|
454
|
-
const have = f.ok ? f.value[HOOK_NAME] : void 0;
|
|
455
|
-
return [
|
|
456
|
-
{
|
|
457
|
-
file,
|
|
458
|
-
state: !have ? "missing" : JSON.stringify(have) === JSON.stringify(entry(cmd)) ? "current" : "stale"
|
|
459
|
-
}
|
|
460
|
-
];
|
|
461
|
-
},
|
|
477
|
+
events: CLAUDE_EVENTS,
|
|
478
|
+
detect: (env = process.env) => claudeDirs(env).some((d) => existsSync2(d)),
|
|
479
|
+
configFiles: (env = process.env) => claudeDirs(env).map(settingsIn),
|
|
480
|
+
install: (cmd, env = process.env, opts = {}) => claudeDirs(env).flatMap((d) => {
|
|
481
|
+
const hooks = installClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS);
|
|
482
|
+
const line = opts.statusLine === void 0 ? null : statusLine(settingsIn(d), opts.statusLine, cmd);
|
|
483
|
+
return line ? [hooks, line] : [hooks];
|
|
484
|
+
}),
|
|
485
|
+
uninstall: (env = process.env, opts = {}) => claudeDirs(env).map((d) => {
|
|
486
|
+
const line = statusLine(settingsIn(d), false);
|
|
487
|
+
const hooks = uninstallClaudeShaped(settingsIn(d), CLAUDE_EVENTS, {
|
|
488
|
+
created: opts.created?.includes(settingsIn(d))
|
|
489
|
+
});
|
|
490
|
+
return line && !hooks.changed ? line : hooks;
|
|
491
|
+
}),
|
|
492
|
+
installed: (cmd, env = process.env, opts = {}) => claudeDirs(env).map((d) => ({
|
|
493
|
+
file: settingsIn(d),
|
|
494
|
+
state: installedClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS)
|
|
495
|
+
})),
|
|
462
496
|
fromHook(input) {
|
|
463
497
|
const event = input.argv[1] ?? "";
|
|
464
498
|
let s = {};
|
|
@@ -467,310 +501,735 @@ var antigravity = {
|
|
|
467
501
|
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
468
502
|
} catch {
|
|
469
503
|
}
|
|
470
|
-
const id = s.
|
|
471
|
-
|
|
472
|
-
if (
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
if (typeof s.
|
|
476
|
-
|
|
477
|
-
const
|
|
504
|
+
const id = s.session_id ?? s.sessionId;
|
|
505
|
+
if (!id) return null;
|
|
506
|
+
if (notClaudeCode(String(id), s)) return null;
|
|
507
|
+
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
508
|
+
if (typeof s.model === "string") session.model = s.model;
|
|
509
|
+
if (typeof s.agent_id === "string" && event.startsWith("Subagent") === false && s.agent_id)
|
|
510
|
+
session.parent_id = null;
|
|
511
|
+
const turn_id = typeof s.prompt_id === "string" ? s.prompt_id : void 0;
|
|
512
|
+
const tool = validTool(s.tool_name) ? s.tool_name : void 0;
|
|
513
|
+
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
514
|
+
const ms = typeof s.duration_ms === "number" ? Math.round(s.duration_ms) : void 0;
|
|
515
|
+
const agent_id = typeof s.agent_id === "string" ? s.agent_id : void 0;
|
|
516
|
+
const permissionId = () => `perm-${createHash("sha256").update(`${turn_id ?? ""}|${tool ?? ""}|${JSON.stringify(s.tool_input ?? null)}`).digest("hex").slice(0, 16)}`;
|
|
478
517
|
const ev = (type, data) => ({ type, data: prune(data), harnessEvent: event });
|
|
479
518
|
const events = [];
|
|
480
519
|
switch (event) {
|
|
481
|
-
case "
|
|
482
|
-
|
|
483
|
-
events.push(ev("session.started", { source: "unknown" }));
|
|
484
|
-
events.push(ev("turn.started", {}));
|
|
485
|
-
} else events.push(ev("session.heartbeat", {}));
|
|
520
|
+
case "SessionStart":
|
|
521
|
+
events.push(ev("session.started", { source: pickSource(s.source ?? s.trigger) }));
|
|
486
522
|
break;
|
|
487
|
-
case "
|
|
523
|
+
case "UserPromptSubmit": {
|
|
524
|
+
const prompt = typeof s.prompt === "string" ? s.prompt : void 0;
|
|
525
|
+
events.push(
|
|
526
|
+
ev("turn.started", {
|
|
527
|
+
turn_id,
|
|
528
|
+
prompt_chars: prompt === void 0 ? void 0 : [...prompt].length,
|
|
529
|
+
// Claude Code's hook schema documents `source`
|
|
530
|
+
// (user | sdk | system | loop_wakeup | schedule_wakeup | poll_event) and says a
|
|
531
|
+
// payload may omit it while the field rolls out; 2.1.304's payload has no such
|
|
532
|
+
// key at all. Forwarded verbatim, shape-guarded only: the list is the harness's
|
|
533
|
+
// to extend.
|
|
534
|
+
submitted_by: harnessWord(s.source),
|
|
535
|
+
// One of Claude Code's own prompt wrappers: the prompt is a block the harness
|
|
536
|
+
// wrote, so it is not the person's words. Absent is unknown, never "the person" —
|
|
537
|
+
// and `true` is never "no person caused this turn", because a Talk or
|
|
538
|
+
// `sessionpipe wait` delivery hands the person's own message over inside a
|
|
539
|
+
// <task-notification> (PROTOCOL.md §4).
|
|
540
|
+
injected: prompt !== void 0 && injectedBlock(prompt) ? true : void 0
|
|
541
|
+
})
|
|
542
|
+
);
|
|
488
543
|
break;
|
|
544
|
+
}
|
|
489
545
|
case "PreToolUse":
|
|
490
|
-
if (tool) events.push(ev("tool.started", { tool, input:
|
|
546
|
+
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, agent_id, input: s.tool_input }));
|
|
491
547
|
break;
|
|
492
548
|
case "PostToolUse":
|
|
493
|
-
if (
|
|
494
|
-
else
|
|
549
|
+
if (tool)
|
|
495
550
|
events.push(
|
|
496
551
|
ev("tool.ended", {
|
|
497
552
|
tool,
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
553
|
+
call_id,
|
|
554
|
+
turn_id,
|
|
555
|
+
agent_id,
|
|
556
|
+
ms,
|
|
557
|
+
ok: true,
|
|
558
|
+
input: s.tool_input,
|
|
559
|
+
output: s.tool_response
|
|
560
|
+
})
|
|
561
|
+
);
|
|
562
|
+
break;
|
|
563
|
+
case "PostToolUseFailure":
|
|
564
|
+
if (tool)
|
|
565
|
+
events.push(
|
|
566
|
+
ev("tool.ended", {
|
|
567
|
+
tool,
|
|
568
|
+
call_id,
|
|
569
|
+
turn_id,
|
|
570
|
+
agent_id,
|
|
571
|
+
ms,
|
|
572
|
+
ok: false,
|
|
573
|
+
error: errText(s.error ?? s.tool_response),
|
|
574
|
+
input: s.tool_input,
|
|
575
|
+
output: s.tool_response
|
|
501
576
|
})
|
|
502
577
|
);
|
|
503
578
|
break;
|
|
579
|
+
case "PermissionRequest":
|
|
580
|
+
events.push(
|
|
581
|
+
ev("attention.needed", {
|
|
582
|
+
attention_id: call_id ?? permissionId(),
|
|
583
|
+
kind: "permission",
|
|
584
|
+
tool,
|
|
585
|
+
message: tool ? `${tool}${describe(s.tool_input)}` : void 0
|
|
586
|
+
})
|
|
587
|
+
);
|
|
588
|
+
break;
|
|
589
|
+
case "Notification":
|
|
590
|
+
events.push(
|
|
591
|
+
ev("attention.needed", {
|
|
592
|
+
attention_id: `notif-${Date.now()}`,
|
|
593
|
+
kind: notifKind(s.notification_type),
|
|
594
|
+
message: typeof s.message === "string" ? s.message.slice(0, 200) : void 0
|
|
595
|
+
})
|
|
596
|
+
);
|
|
597
|
+
break;
|
|
504
598
|
case "Stop":
|
|
505
|
-
events.push(
|
|
599
|
+
events.push(
|
|
600
|
+
ev("turn.ended", {
|
|
601
|
+
turn_id,
|
|
602
|
+
reason: "stop",
|
|
603
|
+
background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
|
|
604
|
+
})
|
|
605
|
+
);
|
|
506
606
|
break;
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
607
|
+
case "StopFailure":
|
|
608
|
+
events.push(
|
|
609
|
+
ev("turn.ended", {
|
|
610
|
+
turn_id,
|
|
611
|
+
reason: "error",
|
|
612
|
+
error: typeof s.error === "string" ? s.error.slice(0, 200) : void 0
|
|
613
|
+
})
|
|
614
|
+
);
|
|
615
|
+
break;
|
|
616
|
+
case "SubagentStart":
|
|
617
|
+
events.push(
|
|
618
|
+
ev("subagent.started", {
|
|
619
|
+
agent_id: String(s.agent_id ?? ""),
|
|
620
|
+
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
621
|
+
})
|
|
622
|
+
);
|
|
623
|
+
break;
|
|
624
|
+
case "SubagentStop":
|
|
625
|
+
events.push(
|
|
626
|
+
ev("subagent.ended", {
|
|
627
|
+
agent_id: String(s.agent_id ?? ""),
|
|
628
|
+
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
629
|
+
})
|
|
630
|
+
);
|
|
631
|
+
break;
|
|
632
|
+
case "PreCompact":
|
|
633
|
+
events.push(ev("context.compacted", { trigger: s.trigger === "manual" ? "manual" : "auto" }));
|
|
634
|
+
break;
|
|
635
|
+
case "PostCompact":
|
|
636
|
+
break;
|
|
637
|
+
// PreCompact already said it; a second event would double-count
|
|
638
|
+
case "SessionEnd":
|
|
639
|
+
events.push(ev("session.ended", { reason: endReason(s.reason) }));
|
|
640
|
+
break;
|
|
641
|
+
case "StatusLine": {
|
|
642
|
+
const limits = claudeLimits(s.rate_limits);
|
|
643
|
+
if (!limits) return null;
|
|
644
|
+
events.push(ev("limits.observed", limits));
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
default:
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
const hints = {};
|
|
651
|
+
if (event === "UserPromptSubmit" && typeof s.prompt === "string") hints.prompt = s.prompt;
|
|
652
|
+
const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
|
|
653
|
+
if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
|
|
654
|
+
const attended = input.env.CLAUDE_CODE_SESSION_ATTENDED;
|
|
655
|
+
if (attended === "1" || attended === "0") hints.attended = attended === "1" || !!input.env.SESSIONPIPE_CONTROL_JOB;
|
|
656
|
+
if (event !== "PreToolUse" && event !== "PostToolUse") hints.claude_pid = claudePid(input.env);
|
|
657
|
+
const r = { session, events, hints };
|
|
658
|
+
if (typeof s.transcript_path === "string") r.transcript = s.transcript_path;
|
|
659
|
+
return r;
|
|
660
|
+
},
|
|
661
|
+
readTranscript(file, fromLine) {
|
|
662
|
+
let text = "";
|
|
663
|
+
try {
|
|
664
|
+
text = readFileSync2(file, "utf8");
|
|
665
|
+
} catch {
|
|
666
|
+
return { turns: [], line: fromLine };
|
|
667
|
+
}
|
|
668
|
+
return readClaude(text.split("\n"), fromLine);
|
|
669
|
+
},
|
|
670
|
+
facts(session, transcript, hints, state) {
|
|
671
|
+
const out = {};
|
|
672
|
+
if (transcript) Object.assign(out, claudeFacts(transcript, session.id, hints, state));
|
|
673
|
+
const cwd = out.cwd ?? session.cwd;
|
|
674
|
+
Object.assign(out, gitFacts(cwd), out.branch ? { branch: out.branch } : {});
|
|
675
|
+
if (!out.url && typeof hints.desktop_id === "string") out.url = `claude://code/continue/${hints.desktop_id}`;
|
|
676
|
+
if (typeof hints.attended === "boolean") out.attended = hints.attended || !!out.url;
|
|
677
|
+
return out;
|
|
530
678
|
},
|
|
679
|
+
remoteFacts: (session, local, transcript, state, env = process.env) => claudeRemoteFacts(session.id, local, transcript, state, env),
|
|
531
680
|
backfill(sinceMs, env = process.env) {
|
|
532
681
|
const rows2 = [];
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
}
|
|
682
|
+
const files = [];
|
|
683
|
+
for (const d of claudeDirs(env))
|
|
684
|
+
files.push(...recentFiles(path3.join(d, "projects"), 1, sinceMs, (n) => /^[0-9a-f-]{36}\.jsonl$/.test(n)));
|
|
685
|
+
const noState = { get: () => ({}), save: () => {
|
|
686
|
+
} };
|
|
687
|
+
for (const f of files) {
|
|
688
|
+
const id = path3.basename(f, ".jsonl");
|
|
689
|
+
const facts = claudeFacts(f, id, {}, noState);
|
|
690
|
+
if (!facts.started_at || !facts.last_at) continue;
|
|
691
|
+
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
692
|
+
const cwd = rest.cwd;
|
|
693
|
+
rows2.push({
|
|
694
|
+
session: { id, ...rest, ...gitFacts(cwd), ...rest.branch ? { branch: rest.branch } : {} },
|
|
695
|
+
started_at,
|
|
696
|
+
last_at,
|
|
697
|
+
transcript: f
|
|
698
|
+
});
|
|
551
699
|
}
|
|
552
700
|
return rows2;
|
|
553
701
|
}
|
|
554
702
|
};
|
|
703
|
+
var LINE_REFRESH_S = 60;
|
|
704
|
+
function statusLine(file, want, cmd) {
|
|
705
|
+
const f = readJsonFile(file);
|
|
706
|
+
if (!f.ok) return null;
|
|
707
|
+
const cur = f.value.statusLine;
|
|
708
|
+
const ours = typeof cur?.command === "string" && OURS.test(cur.command);
|
|
709
|
+
const command = cmd?.([NAME, "StatusLine"]).command;
|
|
710
|
+
if (want && cur && !ours)
|
|
711
|
+
return { file, changed: false, skipped: true, note: "a status line of your own is set; limits are not read" };
|
|
712
|
+
if (want ? ours && cur?.command === command && cur?.refreshInterval === LINE_REFRESH_S : !ours) return null;
|
|
713
|
+
if (want) f.value.statusLine = { type: "command", command, refreshInterval: LINE_REFRESH_S };
|
|
714
|
+
else delete f.value.statusLine;
|
|
715
|
+
writeFileSync2(file, formatJson(f.value, f));
|
|
716
|
+
return { file, changed: true, note: want ? "status line written (account limits)" : "status line removed" };
|
|
717
|
+
}
|
|
718
|
+
var epochIso = (t) => typeof t === "number" && t > 0 && t < 1e11 ? new Date(t * 1e3).toISOString() : void 0;
|
|
719
|
+
var WINDOW_MINUTES = { five_hour: 300, seven_day: 10080 };
|
|
720
|
+
function claudeLimits(rl) {
|
|
721
|
+
if (!rl || typeof rl !== "object" || Array.isArray(rl)) return null;
|
|
722
|
+
const windows = [];
|
|
723
|
+
for (const [id, w] of Object.entries(rl)) {
|
|
724
|
+
if (!/^[a-z0-9_]{1,64}$/.test(id) || typeof w?.used_percentage !== "number" || w.used_percentage < 0) continue;
|
|
725
|
+
windows.push(
|
|
726
|
+
prune({
|
|
727
|
+
id,
|
|
728
|
+
used_percent: w.used_percentage,
|
|
729
|
+
window_minutes: WINDOW_MINUTES[id],
|
|
730
|
+
resets_at: epochIso(w.resets_at)
|
|
731
|
+
})
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
return windows.length ? { windows: windows.slice(0, 8) } : null;
|
|
735
|
+
}
|
|
555
736
|
function prune(o) {
|
|
556
737
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
557
738
|
return o;
|
|
558
739
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
if (/^file:/i.test(s)) {
|
|
563
|
-
try {
|
|
564
|
-
s = decodeURIComponent(new URL(s).pathname);
|
|
565
|
-
} catch {
|
|
566
|
-
return null;
|
|
567
|
-
}
|
|
568
|
-
if (process.platform === "win32" && /^\/[A-Za-z]:/.test(s)) s = s.slice(1);
|
|
569
|
-
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(s)) return null;
|
|
570
|
-
if (!path3.isAbsolute(s)) return null;
|
|
571
|
-
return path3.resolve(s);
|
|
740
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
741
|
+
function notClaudeCode(id, s) {
|
|
742
|
+
return typeof s.transcript_path !== "string" && !UUID.test(id);
|
|
572
743
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
744
|
+
function pickSource(v) {
|
|
745
|
+
return typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
746
|
+
}
|
|
747
|
+
var harnessWord = (v) => typeof v === "string" && /^[A-Za-z0-9][A-Za-z0-9_.-]{0,39}$/.test(v) ? v : void 0;
|
|
748
|
+
var WRAPPER = /^<(?:task-notification|cross-session-message|system-reminder|local-command-stdout|command-[a-z-]+)[\s/>]/i;
|
|
749
|
+
var injectedBlock = (prompt) => WRAPPER.test(prompt.trim());
|
|
750
|
+
function errText(v) {
|
|
751
|
+
if (typeof v === "string") return v.slice(0, 2e3);
|
|
752
|
+
if (v && typeof v === "object") {
|
|
753
|
+
const e = v;
|
|
754
|
+
if (typeof e.error === "string") return e.error.slice(0, 2e3);
|
|
755
|
+
if (typeof e.message === "string") return e.message.slice(0, 2e3);
|
|
580
756
|
}
|
|
581
|
-
return
|
|
757
|
+
return void 0;
|
|
582
758
|
}
|
|
583
|
-
function
|
|
584
|
-
|
|
759
|
+
function describe(input) {
|
|
760
|
+
if (!input || typeof input !== "object") return "";
|
|
761
|
+
const i = input;
|
|
762
|
+
const v = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern;
|
|
763
|
+
return typeof v === "string" ? ` ${v.slice(0, 120)}` : "";
|
|
764
|
+
}
|
|
765
|
+
var NAME_RANK = {
|
|
766
|
+
user: "custom",
|
|
767
|
+
auto: "harness",
|
|
768
|
+
hook: "harness",
|
|
769
|
+
peer: "harness"
|
|
770
|
+
};
|
|
771
|
+
function registryEntry(j) {
|
|
772
|
+
if (!j || typeof j !== "object") return null;
|
|
773
|
+
const r = j;
|
|
774
|
+
const source = r.nameSource ? NAME_RANK[r.nameSource] : void 0;
|
|
775
|
+
return {
|
|
776
|
+
sessionId: r.sessionId ? String(r.sessionId) : null,
|
|
777
|
+
name: source && typeof r.name === "string" && r.name.trim() ? r.name.trim() : null,
|
|
778
|
+
name_source: source ?? null,
|
|
779
|
+
bridge: typeof r.bridgeSessionId === "string" ? r.bridgeSessionId : null
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
var liveCache = /* @__PURE__ */ new Map();
|
|
783
|
+
function claudeLive(dir) {
|
|
784
|
+
const hit = liveCache.get(dir);
|
|
785
|
+
if (hit) return hit;
|
|
786
|
+
const map = /* @__PURE__ */ new Map();
|
|
585
787
|
let names = [];
|
|
586
788
|
try {
|
|
587
|
-
names = readdirSync2(dir).filter((n) => n.endsWith(".json"));
|
|
789
|
+
names = readdirSync2(path3.join(dir, "sessions")).filter((n) => n.endsWith(".json"));
|
|
588
790
|
} catch {
|
|
589
|
-
return out;
|
|
590
791
|
}
|
|
591
792
|
for (const n of names) {
|
|
592
|
-
const id = n.slice(0, -5);
|
|
593
|
-
if (!ID_RE.test(id)) continue;
|
|
594
|
-
let j;
|
|
595
793
|
try {
|
|
596
|
-
|
|
794
|
+
const e = registryEntry(JSON.parse(readFileSync2(path3.join(dir, "sessions", n), "utf8")));
|
|
795
|
+
if (e?.sessionId) map.set(e.sessionId, e);
|
|
597
796
|
} catch {
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
const folders = [];
|
|
601
|
-
for (const r of j?.projectResources?.resources ?? []) {
|
|
602
|
-
const f = agPath(r?.folderUri ?? r?.gitFolder?.folderUri);
|
|
603
|
-
if (f) folders.push(...agVariants(f));
|
|
604
797
|
}
|
|
605
|
-
if (folders.length) out.push({ id, folders });
|
|
606
798
|
}
|
|
607
|
-
|
|
799
|
+
liveCache.set(dir, map);
|
|
800
|
+
return map;
|
|
608
801
|
}
|
|
609
|
-
function
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
802
|
+
function claudeLiveByPid(dir, pid) {
|
|
803
|
+
if (!pid) return null;
|
|
804
|
+
try {
|
|
805
|
+
return registryEntry(JSON.parse(readFileSync2(path3.join(dir, "sessions", `${pid}.json`), "utf8")));
|
|
806
|
+
} catch {
|
|
807
|
+
return null;
|
|
614
808
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
809
|
+
}
|
|
810
|
+
function claudePid(env = process.env) {
|
|
811
|
+
try {
|
|
812
|
+
const dirs = claudeDirs(env);
|
|
813
|
+
const has = (pid2) => dirs.some((d) => existsSync2(path3.join(d, "sessions", `${pid2}.json`)));
|
|
814
|
+
let pid = process.ppid;
|
|
815
|
+
if (has(pid)) return pid;
|
|
816
|
+
const parent = parentOf();
|
|
817
|
+
for (let hop = 0; hop < 6 && pid > 1; hop++) {
|
|
818
|
+
pid = parent(pid);
|
|
819
|
+
if (pid > 1 && has(pid)) return pid;
|
|
820
|
+
}
|
|
821
|
+
} catch {
|
|
822
|
+
}
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
function parentOf() {
|
|
826
|
+
if (process.platform === "linux") {
|
|
827
|
+
return (pid) => {
|
|
828
|
+
try {
|
|
829
|
+
const st = readFileSync2(`/proc/${pid}/stat`, "utf8");
|
|
830
|
+
return Number(st.slice(st.lastIndexOf(")") + 2).split(" ")[1]) || 0;
|
|
831
|
+
} catch {
|
|
832
|
+
return 0;
|
|
623
833
|
}
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
const table = /* @__PURE__ */ new Map();
|
|
837
|
+
try {
|
|
838
|
+
const out = execFileSync2("ps", ["-axo", "pid=,ppid="], {
|
|
839
|
+
encoding: "utf8",
|
|
840
|
+
timeout: 2e3,
|
|
841
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
842
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
843
|
+
});
|
|
844
|
+
for (const l of out.split("\n")) {
|
|
845
|
+
const m = /^\s*(\d+)\s+(\d+)/.exec(l);
|
|
846
|
+
if (m) table.set(Number(m[1]), Number(m[2]));
|
|
624
847
|
}
|
|
625
|
-
|
|
848
|
+
} catch {
|
|
849
|
+
}
|
|
850
|
+
return (pid) => table.get(pid) || 0;
|
|
626
851
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const
|
|
630
|
-
|
|
852
|
+
var EMAIL_RE = /^[^@\s]{1,64}@[^@\s]{1,189}$/;
|
|
853
|
+
function claudeLogin(dir, home = homeDir()) {
|
|
854
|
+
const files = [path3.join(dir, ".claude.json")];
|
|
855
|
+
if (path3.resolve(dir) === path3.join(home, ".claude")) files.unshift(path3.join(home, ".claude.json"));
|
|
856
|
+
for (const f of files) {
|
|
631
857
|
try {
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const conv = opts.conversation ?? "";
|
|
638
|
-
if (!ID_RE.test(conv) || !opts.workspaces?.length) return base;
|
|
639
|
-
const project = agMatchProject(agProjects(opts.projectsDir ?? projectsDir(opts.env)), opts.workspaces);
|
|
640
|
-
return project ? `${base}?p=${encodeURIComponent(`c/${conv}?section=${project}`)}` : base;
|
|
858
|
+
const a = JSON.parse(readFileSync2(f, "utf8")).oauthAccount;
|
|
859
|
+
if (typeof a?.accountUuid === "string" && a.accountUuid) {
|
|
860
|
+
const email = typeof a.emailAddress === "string" && EMAIL_RE.test(a.emailAddress) ? a.emailAddress : void 0;
|
|
861
|
+
return { id: a.accountUuid.slice(0, 128), ...email ? { email } : {} };
|
|
862
|
+
}
|
|
641
863
|
} catch {
|
|
642
864
|
}
|
|
643
865
|
}
|
|
644
|
-
return
|
|
866
|
+
return apiAccount(dir);
|
|
645
867
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
const
|
|
868
|
+
var ANTHROPIC_HOST = /(^|\.)anthropic\.com$/i;
|
|
869
|
+
function apiAccount(dir) {
|
|
870
|
+
const base = readJson(settingsIn(dir)).env?.ANTHROPIC_BASE_URL;
|
|
871
|
+
if (typeof base !== "string") return null;
|
|
872
|
+
let u;
|
|
873
|
+
try {
|
|
874
|
+
u = new URL(base.trim());
|
|
875
|
+
} catch {
|
|
876
|
+
return null;
|
|
877
|
+
}
|
|
878
|
+
if (u.protocol !== "https:" && u.protocol !== "http:" || !u.hostname || ANTHROPIC_HOST.test(u.hostname))
|
|
879
|
+
return null;
|
|
880
|
+
const origin = `${u.protocol}//${u.host}`;
|
|
881
|
+
return { id: `api-${createHash("sha256").update(origin).digest("hex").slice(0, 16)}`, name: u.host };
|
|
882
|
+
}
|
|
883
|
+
var accountCache = /* @__PURE__ */ new Map();
|
|
884
|
+
function claudeAccount(dir) {
|
|
885
|
+
const hit = accountCache.get(dir);
|
|
886
|
+
if (hit !== void 0) return hit;
|
|
887
|
+
const acct = claudeLogin(dir)?.id ?? null;
|
|
888
|
+
accountCache.set(dir, acct);
|
|
889
|
+
return acct;
|
|
890
|
+
}
|
|
891
|
+
var LINK_RE = /^https:\/\/claude\.ai\/code\/session_[A-Za-z0-9]+$/;
|
|
892
|
+
function transcriptLink(records) {
|
|
893
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
894
|
+
const j = records[i];
|
|
895
|
+
if (j.type === "system" && j.subtype === "bridge_status" && typeof j.url === "string" && LINK_RE.test(j.url))
|
|
896
|
+
return j.url;
|
|
897
|
+
if (j.type === "bridge-session" && typeof j.bridgeSessionId === "string") {
|
|
898
|
+
const m = /^(?:cse|session)_([A-Za-z0-9]+)$/.exec(j.bridgeSessionId);
|
|
899
|
+
if (m) return `https://claude.ai/code/session_${m[1]}`;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
function appTitle(text) {
|
|
905
|
+
const t = String(text).replace(/\s+/g, " ").trim();
|
|
906
|
+
return t.length > 50 ? `${t.slice(0, 50)}...` : t;
|
|
907
|
+
}
|
|
908
|
+
function askText(j) {
|
|
909
|
+
if (!j || j.type !== "user" || j.isMeta || j.isSidechain) return null;
|
|
910
|
+
const origin = j.origin;
|
|
911
|
+
if (origin?.kind && origin.kind !== "human") return null;
|
|
912
|
+
const c = j.message?.content;
|
|
913
|
+
const text = (typeof c === "string" ? c : Array.isArray(c) ? c.filter((b) => b && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n") : "").trim();
|
|
914
|
+
if (!text || isInjected(text)) return null;
|
|
915
|
+
return text;
|
|
916
|
+
}
|
|
917
|
+
function firstAsk(records) {
|
|
918
|
+
for (const j of records) {
|
|
919
|
+
const t = askText(j);
|
|
920
|
+
if (t) return appTitle(t);
|
|
921
|
+
}
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
function scanTitles(file, from, found) {
|
|
925
|
+
const out = { custom: found.custom ?? null, ai: found.ai ?? null, upto: from };
|
|
926
|
+
let size = 0;
|
|
927
|
+
try {
|
|
928
|
+
size = statSync2(file).size;
|
|
929
|
+
} catch {
|
|
930
|
+
return out;
|
|
931
|
+
}
|
|
932
|
+
if (size < from) {
|
|
933
|
+
out.upto = 0;
|
|
934
|
+
out.custom = out.ai = null;
|
|
935
|
+
from = 0;
|
|
936
|
+
}
|
|
937
|
+
let pos = 0;
|
|
938
|
+
scanLines(
|
|
939
|
+
file,
|
|
940
|
+
(l) => {
|
|
941
|
+
pos += Buffer.byteLength(l, "utf8") + 1;
|
|
942
|
+
if (pos <= from || l.indexOf('-title"') < 0) return null;
|
|
943
|
+
try {
|
|
944
|
+
const j = JSON.parse(l);
|
|
945
|
+
if (j.type === "custom-title" && j.customTitle) out.custom = j.customTitle;
|
|
946
|
+
if (j.type === "ai-title" && j.aiTitle) out.ai = j.aiTitle;
|
|
947
|
+
} catch {
|
|
948
|
+
}
|
|
949
|
+
return null;
|
|
950
|
+
},
|
|
951
|
+
512 * 1024 * 1024
|
|
952
|
+
);
|
|
953
|
+
out.upto = size;
|
|
954
|
+
return out;
|
|
955
|
+
}
|
|
956
|
+
function claudeFacts(file, sessionId, hints, state) {
|
|
957
|
+
const { head, tail } = ends(file, 64 * 1024, 256 * 1024);
|
|
958
|
+
const H = parseLines(head);
|
|
649
959
|
const T = parseLines(tail);
|
|
650
960
|
const out = {};
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
961
|
+
const id = sessionId || path3.basename(file, ".jsonl");
|
|
962
|
+
const mem = state.get(NAME, id);
|
|
963
|
+
let custom = null;
|
|
964
|
+
let ai = null;
|
|
965
|
+
for (const j of H.concat(T)) {
|
|
966
|
+
if (j.type === "custom-title" && typeof j.customTitle === "string") custom = j.customTitle;
|
|
967
|
+
if (j.type === "ai-title" && typeof j.aiTitle === "string") ai = j.aiTitle;
|
|
968
|
+
}
|
|
969
|
+
let size = 0;
|
|
970
|
+
try {
|
|
971
|
+
size = statSync2(file).size;
|
|
972
|
+
} catch {
|
|
973
|
+
}
|
|
974
|
+
if (!custom && !ai && size > 64 * 1024 + 256 * 1024) {
|
|
975
|
+
const s = scanTitles(file, Number(mem.scan_upto) || 0, {
|
|
976
|
+
custom: mem.scan_custom,
|
|
977
|
+
ai: mem.scan_ai
|
|
978
|
+
});
|
|
979
|
+
custom = s.custom;
|
|
980
|
+
ai = s.ai;
|
|
981
|
+
state.save(NAME, id, { scan_upto: s.upto, scan_custom: s.custom, scan_ai: s.ai });
|
|
982
|
+
}
|
|
983
|
+
const dir = configDirOf(file);
|
|
984
|
+
const live = claudeLive(dir).get(id) ?? claudeLiveByPid(dir, hints.claude_pid);
|
|
985
|
+
if (live?.name) state.save(NAME, id, { name: live.name, name_source: live.name_source });
|
|
986
|
+
const bridge = live?.bridge && /^session_[A-Za-z0-9]+$/.test(live.bridge) ? `https://claude.ai/code/${live.bridge}` : null;
|
|
987
|
+
const url = bridge ?? transcriptLink(T) ?? transcriptLink(H) ?? (typeof mem.url === "string" ? mem.url : null);
|
|
988
|
+
if (url) {
|
|
989
|
+
out.url = url;
|
|
990
|
+
state.save(NAME, id, { url });
|
|
991
|
+
}
|
|
992
|
+
const name = live?.name ? { title: live.name, source: live.name_source } : typeof mem.name === "string" ? { title: mem.name, source: mem.name_source } : null;
|
|
993
|
+
const pick = name && name.source === "custom" && name || custom && { title: custom, source: "custom" } || name || ai && { title: ai, source: "harness" };
|
|
994
|
+
let ask = typeof mem.first_ask === "string" ? mem.first_ask : firstAsk(H);
|
|
995
|
+
if (!ask && size > 64 * 1024 && !mem.first_ask_none) {
|
|
996
|
+
ask = scanLines(file, (l) => {
|
|
997
|
+
try {
|
|
998
|
+
const t = askText(JSON.parse(l));
|
|
999
|
+
return t ? appTitle(t) : null;
|
|
1000
|
+
} catch {
|
|
1001
|
+
return null;
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
if (!ask && size > 16 * 1024 * 1024) state.save(NAME, id, { first_ask_none: true });
|
|
1005
|
+
}
|
|
1006
|
+
if (ask) {
|
|
1007
|
+
out.first_ask = ask;
|
|
1008
|
+
if (!mem.first_ask) state.save(NAME, id, { first_ask: ask });
|
|
1009
|
+
}
|
|
1010
|
+
if (pick) {
|
|
1011
|
+
out.title = String(pick.title).slice(0, 120);
|
|
1012
|
+
out.title_source = pick.source;
|
|
1013
|
+
} else if (ask) {
|
|
1014
|
+
out.title = ask;
|
|
660
1015
|
out.title_source = "first-ask";
|
|
661
|
-
break;
|
|
662
1016
|
}
|
|
663
|
-
const
|
|
1017
|
+
const acct = claudeAccount(dir);
|
|
1018
|
+
if (acct) out.account_id = acct;
|
|
1019
|
+
for (let i = T.length - 1; i >= 0; i--) {
|
|
1020
|
+
const m = T[i].message?.model;
|
|
1021
|
+
if (T[i]?.type === "assistant" && m && m !== "<synthetic>") {
|
|
1022
|
+
out.model = m;
|
|
1023
|
+
break;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
const meta = [...T].reverse().find((j) => j.version) ?? H.find((j) => j.version);
|
|
1027
|
+
if (meta?.version) out.harness_version = String(meta.version);
|
|
1028
|
+
const times = H.concat(T).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
664
1029
|
if (times.length) {
|
|
665
1030
|
out.started_at = Math.min(...times);
|
|
666
1031
|
out.last_at = Math.max(...times);
|
|
667
1032
|
}
|
|
1033
|
+
const withCwd = H.find((j) => typeof j.cwd === "string");
|
|
1034
|
+
if (withCwd) {
|
|
1035
|
+
out.cwd = withCwd.cwd;
|
|
1036
|
+
if (typeof withCwd.gitBranch === "string" && withCwd.gitBranch !== "HEAD") out.branch = withCwd.gitBranch;
|
|
1037
|
+
}
|
|
668
1038
|
return out;
|
|
669
1039
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
1040
|
+
var REMOTE_RETRY_MS = 6e4;
|
|
1041
|
+
var REMOTE_REFRESH_MS = 30 * 6e4;
|
|
1042
|
+
var CLAUDE_API = "https://api.anthropic.com";
|
|
1043
|
+
var KEYCHAIN = "Claude Code-credentials";
|
|
1044
|
+
var REMOTE_TIMEOUT_MS = 3e3;
|
|
1045
|
+
var KEYCHAIN_TIMEOUT_MS = 2e3;
|
|
1046
|
+
var REMOTE_MAX_BYTES = 1 << 20;
|
|
1047
|
+
var cleanName = (s) => s.replace(/[\p{Cc}\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/gu, " ").replace(/\s+/g, " ").trim();
|
|
1048
|
+
async function claudeRemoteFacts(id, local, transcript, state, env, now = Date.now()) {
|
|
1049
|
+
if (!transcript || !local.url || !LINK_RE.test(local.url) || local.title_source === "custom") return {};
|
|
1050
|
+
const mem = state.get(NAME, id);
|
|
1051
|
+
let title = typeof mem.remote_title === "string" ? mem.remote_title : null;
|
|
1052
|
+
if (now - (Number(mem.remote_at) || 0) >= (title ? REMOTE_REFRESH_MS : REMOTE_RETRY_MS)) {
|
|
1053
|
+
state.save(NAME, id, { remote_at: now });
|
|
1054
|
+
const token = claudeAiToken(configDirOf(transcript), env, now);
|
|
1055
|
+
const got = token ? await claudeAiTitle(local.url.slice(local.url.lastIndexOf("/") + 1), token) : void 0;
|
|
1056
|
+
if (got !== void 0) {
|
|
1057
|
+
title = got?.slice(0, 200) ?? null;
|
|
1058
|
+
state.save(NAME, id, { remote_title: title });
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return title ? { title: title.slice(0, 120), title_source: "harness" } : {};
|
|
1062
|
+
}
|
|
1063
|
+
function keychainServices(dir, env) {
|
|
1064
|
+
const named = (p) => `${KEYCHAIN}-${createHash("sha256").update(p.normalize("NFC")).digest("hex").slice(0, 8)}`;
|
|
1065
|
+
const out = /* @__PURE__ */ new Set();
|
|
1066
|
+
if (env.CLAUDE_CONFIG_DIR && path3.resolve(env.CLAUDE_CONFIG_DIR) === dir) out.add(named(env.CLAUDE_CONFIG_DIR));
|
|
1067
|
+
if (dir === path3.join(env.HOME || homeDir(), ".claude")) out.add(KEYCHAIN);
|
|
1068
|
+
out.add(named(dir));
|
|
1069
|
+
return [...out];
|
|
1070
|
+
}
|
|
1071
|
+
var tokenCache = /* @__PURE__ */ new Map();
|
|
1072
|
+
function claudeAiToken(dir, env, now) {
|
|
1073
|
+
const hit = tokenCache.get(dir);
|
|
1074
|
+
if (hit && hit.expires > now + 6e4) return hit.token;
|
|
1075
|
+
const raw = [];
|
|
1076
|
+
if (process.platform === "darwin")
|
|
1077
|
+
for (const service of keychainServices(dir, env)) {
|
|
1078
|
+
try {
|
|
1079
|
+
const user = env.USER || userInfo().username;
|
|
1080
|
+
const account = /^[\w.-]+$/.test(user) ? user : "claude-code-user";
|
|
1081
|
+
raw.push(
|
|
1082
|
+
execFileSync2("security", ["find-generic-password", "-a", account, "-w", "-s", service], {
|
|
1083
|
+
encoding: "utf8",
|
|
1084
|
+
timeout: KEYCHAIN_TIMEOUT_MS,
|
|
1085
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1086
|
+
})
|
|
1087
|
+
);
|
|
1088
|
+
break;
|
|
1089
|
+
} catch {
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
try {
|
|
1093
|
+
raw.push(readFileSync2(path3.join(dir, ".credentials.json"), "utf8"));
|
|
1094
|
+
} catch {
|
|
1095
|
+
}
|
|
1096
|
+
for (const r of raw) {
|
|
685
1097
|
try {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
1098
|
+
const t = r.trim();
|
|
1099
|
+
const o = JSON.parse(/^(?:[0-9a-f]{2})+$/i.test(t) ? Buffer.from(t, "hex").toString("utf8") : t).claudeAiOauth;
|
|
1100
|
+
if (typeof o?.accessToken !== "string" || !o.accessToken) continue;
|
|
1101
|
+
const expires = typeof o.expiresAt === "number" ? o.expiresAt : Number.POSITIVE_INFINITY;
|
|
1102
|
+
if (expires <= now + 6e4) continue;
|
|
1103
|
+
tokenCache.set(dir, { token: o.accessToken, expires });
|
|
1104
|
+
return o.accessToken;
|
|
691
1105
|
} catch {
|
|
692
|
-
return "";
|
|
693
1106
|
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const repo = run(["remote", "get-url", "origin"]);
|
|
697
|
-
if (repo) out.repo = stripCredentials(repo);
|
|
698
|
-
const branch = run(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
699
|
-
if (branch && branch !== "HEAD") out.branch = branch;
|
|
700
|
-
cache.set(cwd, out);
|
|
701
|
-
return out;
|
|
1107
|
+
}
|
|
1108
|
+
return null;
|
|
702
1109
|
}
|
|
703
|
-
function
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
1110
|
+
async function claudeAiTitle(bridge, token) {
|
|
1111
|
+
try {
|
|
1112
|
+
const r = await fetch(`${CLAUDE_API}/v1/code/sessions/${bridge}`, {
|
|
1113
|
+
headers: { Authorization: `Bearer ${token}`, "anthropic-version": "2023-06-01", "User-Agent": "sessionpipe" },
|
|
1114
|
+
redirect: "error",
|
|
1115
|
+
signal: AbortSignal.timeout(REMOTE_TIMEOUT_MS)
|
|
1116
|
+
});
|
|
1117
|
+
if (r.status !== 200 || Number(r.headers.get("content-length")) > REMOTE_MAX_BYTES) return void 0;
|
|
1118
|
+
const text = await r.text();
|
|
1119
|
+
if (text.length > REMOTE_MAX_BYTES) return void 0;
|
|
1120
|
+
const j = JSON.parse(text);
|
|
1121
|
+
const s = j?.response_shape ?? j?.session ?? j;
|
|
1122
|
+
const bare = (x) => x.replace(/^(?:session|cse)_/, "");
|
|
1123
|
+
if (typeof s?.id !== "string" || bare(s.id) !== bare(bridge)) return void 0;
|
|
1124
|
+
return typeof s.title === "string" && cleanName(s.title) || null;
|
|
1125
|
+
} catch {
|
|
1126
|
+
return void 0;
|
|
1127
|
+
}
|
|
707
1128
|
}
|
|
708
1129
|
|
|
709
|
-
// ../core/src/adapters/
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
"PostToolUse",
|
|
716
|
-
"PostToolUseFailure",
|
|
717
|
-
"PermissionRequest",
|
|
718
|
-
"Notification",
|
|
719
|
-
"Stop",
|
|
720
|
-
"SubagentStart",
|
|
721
|
-
"SubagentStop",
|
|
722
|
-
"PreCompact",
|
|
723
|
-
"PostCompact",
|
|
724
|
-
"SessionEnd",
|
|
725
|
-
"StopFailure"
|
|
726
|
-
];
|
|
727
|
-
var LEAN_SKIP = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUseFailure"]);
|
|
728
|
-
var eventsFor = (lean) => lean ? CLAUDE_EVENTS.filter((e) => !LEAN_SKIP.has(e)) : [...CLAUDE_EVENTS];
|
|
729
|
-
var OPTS = {
|
|
730
|
-
matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0,
|
|
731
|
-
timeout: 5,
|
|
732
|
-
// The hook waits up to 125 s for a verified control answer, and only when control
|
|
733
|
-
// is paired on this machine; everywhere else it exits in milliseconds as before.
|
|
734
|
-
timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
|
|
735
|
-
};
|
|
736
|
-
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
737
|
-
function claudeDirs(env = process.env, home = env.HOME || homeDir()) {
|
|
738
|
-
const out = [];
|
|
739
|
-
const add = (d) => {
|
|
740
|
-
if (!d) return;
|
|
741
|
-
const r = path4.resolve(d);
|
|
742
|
-
if (!out.includes(r)) out.push(r);
|
|
743
|
-
};
|
|
744
|
-
add(path4.join(home, ".claude"));
|
|
745
|
-
add(env.CLAUDE_CONFIG_DIR);
|
|
1130
|
+
// ../core/src/adapters/headless.ts
|
|
1131
|
+
import { accessSync, constants, statSync as statSync3 } from "node:fs";
|
|
1132
|
+
import path4 from "node:path";
|
|
1133
|
+
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
1134
|
+
var OUT_CAP = 1024 * 1024;
|
|
1135
|
+
function isExe(p) {
|
|
746
1136
|
try {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
if (existsSync3(path4.join(d, "projects")) || existsSync3(path4.join(d, "settings.json"))) add(d);
|
|
751
|
-
}
|
|
1137
|
+
if (!statSync3(p).isFile()) return false;
|
|
1138
|
+
accessSync(p, constants.X_OK);
|
|
1139
|
+
return true;
|
|
752
1140
|
} catch {
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
function findBinary(o) {
|
|
1145
|
+
if (o.override && isExe(o.override)) return o.override;
|
|
1146
|
+
for (const d of String(o.env?.PATH || "").split(path4.delimiter)) {
|
|
1147
|
+
if (!d || !path4.isAbsolute(d)) continue;
|
|
1148
|
+
for (const n of o.names) if (isExe(path4.join(d, n))) return path4.join(d, n);
|
|
753
1149
|
}
|
|
754
|
-
|
|
1150
|
+
for (const d of o.dirs) for (const n of o.names) if (isExe(path4.join(d, n))) return path4.join(d, n);
|
|
1151
|
+
return null;
|
|
755
1152
|
}
|
|
756
|
-
|
|
757
|
-
|
|
1153
|
+
|
|
1154
|
+
// ../core/src/adapters/antigravity.ts
|
|
1155
|
+
import {
|
|
1156
|
+
existsSync as existsSync3,
|
|
1157
|
+
mkdirSync as mkdirSync2,
|
|
1158
|
+
readdirSync as readdirSync3,
|
|
1159
|
+
readFileSync as readFileSync3,
|
|
1160
|
+
realpathSync,
|
|
1161
|
+
statSync as statSync4,
|
|
1162
|
+
unlinkSync as unlinkSync2,
|
|
1163
|
+
writeFileSync as writeFileSync3
|
|
1164
|
+
} from "node:fs";
|
|
1165
|
+
import path5 from "node:path";
|
|
1166
|
+
var NAME2 = "antigravity";
|
|
1167
|
+
var HOOK_NAME = "sessionpipe";
|
|
1168
|
+
var AG_EVENTS = ["PreInvocation", "PostInvocation", "PostToolUse", "Stop"];
|
|
1169
|
+
var AG_STALE_PRETOOL = '{"decision":"ask","reason":"sessionpipe: an outdated PreToolUse hook is still loaded. Restart this conversation (or run sessionpipe install) to drop it."}\n';
|
|
1170
|
+
var antigravityAnswer = (event) => event === "PreToolUse" ? AG_STALE_PRETOOL : "{}\n";
|
|
1171
|
+
var gemini = (env) => env.GEMINI_CLI_HOME || path5.join(homeDir(), ".gemini");
|
|
1172
|
+
var hooksFile = (env) => path5.join(gemini(env), "config", "hooks.json");
|
|
1173
|
+
var projectsDir = (env) => path5.join(gemini(env), "config", "projects");
|
|
1174
|
+
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) => path5.join(gemini(env), d));
|
|
1175
|
+
var ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
758
1176
|
var validTool2 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
1177
|
+
function entry(cmd) {
|
|
1178
|
+
const handler = (e) => ({ type: "command", command: cmd([NAME2, e]).command, timeout: 5 });
|
|
1179
|
+
const out = { enabled: true };
|
|
1180
|
+
for (const e of AG_EVENTS) out[e] = /Tool/.test(e) ? [{ matcher: "*", hooks: [handler(e)] }] : [handler(e)];
|
|
1181
|
+
return out;
|
|
1182
|
+
}
|
|
1183
|
+
var antigravity = {
|
|
762
1184
|
name: NAME2,
|
|
763
|
-
events:
|
|
764
|
-
detect: (env = process.env) =>
|
|
765
|
-
configFiles: (env = process.env) =>
|
|
766
|
-
install
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1185
|
+
events: AG_EVENTS,
|
|
1186
|
+
detect: (env = process.env) => existsSync3(path5.join(gemini(env), "config")) || dataDirs(env).some((d) => existsSync3(d)),
|
|
1187
|
+
configFiles: (env = process.env) => [hooksFile(env)],
|
|
1188
|
+
install(cmd, env = process.env) {
|
|
1189
|
+
const file = hooksFile(env);
|
|
1190
|
+
const f = readJsonFile(file);
|
|
1191
|
+
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
1192
|
+
const hooks = f.value;
|
|
1193
|
+
const e = entry(cmd);
|
|
1194
|
+
if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];
|
|
1195
|
+
hooks[HOOK_NAME] = e;
|
|
1196
|
+
mkdirSync2(path5.dirname(file), { recursive: true });
|
|
1197
|
+
writeFileSync3(file, formatJson(hooks, f));
|
|
1198
|
+
return [
|
|
1199
|
+
{
|
|
1200
|
+
file,
|
|
1201
|
+
changed: true,
|
|
1202
|
+
created: !f.existed,
|
|
1203
|
+
note: "hooks.json is read when a conversation starts; open ones report after a restart"
|
|
1204
|
+
}
|
|
1205
|
+
];
|
|
1206
|
+
},
|
|
1207
|
+
uninstall(env = process.env, opts = {}) {
|
|
1208
|
+
const file = hooksFile(env);
|
|
1209
|
+
if (!existsSync3(file)) return [{ file, changed: false }];
|
|
1210
|
+
const f = readJsonFile(file);
|
|
1211
|
+
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
1212
|
+
const hooks = f.value;
|
|
1213
|
+
if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];
|
|
1214
|
+
delete hooks[HOOK_NAME];
|
|
1215
|
+
if (opts.created?.includes(file) && !Object.keys(hooks).length) {
|
|
1216
|
+
unlinkSync2(file);
|
|
1217
|
+
return [{ file, changed: true, note: "removed (sessionpipe created it)" }];
|
|
1218
|
+
}
|
|
1219
|
+
writeFileSync3(file, formatJson(hooks, f));
|
|
1220
|
+
return [{ file, changed: true }];
|
|
1221
|
+
},
|
|
1222
|
+
installed(cmd, env = process.env) {
|
|
1223
|
+
const file = hooksFile(env);
|
|
1224
|
+
const f = readJsonFile(file);
|
|
1225
|
+
const have = f.ok ? f.value[HOOK_NAME] : void 0;
|
|
1226
|
+
return [
|
|
1227
|
+
{
|
|
1228
|
+
file,
|
|
1229
|
+
state: !have ? "missing" : JSON.stringify(have) === JSON.stringify(entry(cmd)) ? "current" : "stale"
|
|
1230
|
+
}
|
|
1231
|
+
];
|
|
1232
|
+
},
|
|
774
1233
|
fromHook(input) {
|
|
775
1234
|
const event = input.argv[1] ?? "";
|
|
776
1235
|
let s = {};
|
|
@@ -779,133 +1238,48 @@ var claudeCode = {
|
|
|
779
1238
|
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
780
1239
|
} catch {
|
|
781
1240
|
}
|
|
782
|
-
const id = s.session_id ?? s.sessionId;
|
|
783
|
-
|
|
784
|
-
if (
|
|
785
|
-
const
|
|
786
|
-
|
|
787
|
-
if (typeof s.
|
|
788
|
-
|
|
789
|
-
const
|
|
790
|
-
const tool = validTool2(s.tool_name) ? s.tool_name : void 0;
|
|
791
|
-
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
792
|
-
const ms = typeof s.duration_ms === "number" ? Math.round(s.duration_ms) : void 0;
|
|
793
|
-
const agent_id = typeof s.agent_id === "string" ? s.agent_id : void 0;
|
|
794
|
-
const permissionId = () => `perm-${createHash("sha256").update(`${turn_id ?? ""}|${tool ?? ""}|${JSON.stringify(s.tool_input ?? null)}`).digest("hex").slice(0, 16)}`;
|
|
1241
|
+
const id = s.conversationId ?? s.session_id ?? s.sessionId;
|
|
1242
|
+
const stdout = antigravityAnswer(event);
|
|
1243
|
+
if (!id) return { session: { id: "" }, events: [], stdout };
|
|
1244
|
+
const workspaces = agWorkspaces(s.workspacePaths);
|
|
1245
|
+
const session = { id: String(id), cwd: workspaces[0] ?? input.cwd };
|
|
1246
|
+
if (typeof s.modelName === "string" && s.modelName !== "auto") session.model = s.modelName;
|
|
1247
|
+
const toolCall = s.toolCall;
|
|
1248
|
+
const tool = validTool2(toolCall?.name) ? toolCall?.name : validTool2(s.toolName) ? s.toolName : void 0;
|
|
795
1249
|
const ev = (type, data) => ({ type, data: prune2(data), harnessEvent: event });
|
|
796
1250
|
const events = [];
|
|
797
1251
|
switch (event) {
|
|
798
|
-
case "
|
|
799
|
-
|
|
1252
|
+
case "PreInvocation":
|
|
1253
|
+
if (Number(s.invocationNum) === 0 || s.invocationNum === void 0) {
|
|
1254
|
+
events.push(ev("session.started", { source: "unknown" }));
|
|
1255
|
+
events.push(ev("turn.started", {}));
|
|
1256
|
+
} else events.push(ev("session.heartbeat", {}));
|
|
800
1257
|
break;
|
|
801
|
-
case "
|
|
802
|
-
events.push(
|
|
803
|
-
ev("turn.started", {
|
|
804
|
-
turn_id,
|
|
805
|
-
prompt_chars: typeof s.prompt === "string" ? [...s.prompt].length : void 0
|
|
806
|
-
})
|
|
807
|
-
);
|
|
1258
|
+
case "PostInvocation":
|
|
808
1259
|
break;
|
|
809
1260
|
case "PreToolUse":
|
|
810
|
-
if (tool) events.push(ev("tool.started", { tool,
|
|
1261
|
+
if (tool) events.push(ev("tool.started", { tool, input: toolCall?.args }));
|
|
811
1262
|
break;
|
|
812
1263
|
case "PostToolUse":
|
|
813
|
-
if (tool)
|
|
814
|
-
|
|
815
|
-
ev("tool.ended", {
|
|
816
|
-
tool,
|
|
817
|
-
call_id,
|
|
818
|
-
turn_id,
|
|
819
|
-
agent_id,
|
|
820
|
-
ms,
|
|
821
|
-
ok: true,
|
|
822
|
-
input: s.tool_input,
|
|
823
|
-
output: s.tool_response
|
|
824
|
-
})
|
|
825
|
-
);
|
|
826
|
-
break;
|
|
827
|
-
case "PostToolUseFailure":
|
|
828
|
-
if (tool)
|
|
1264
|
+
if (!tool) events.push(ev("session.heartbeat", {}));
|
|
1265
|
+
else
|
|
829
1266
|
events.push(
|
|
830
1267
|
ev("tool.ended", {
|
|
831
1268
|
tool,
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
ms,
|
|
836
|
-
ok: false,
|
|
837
|
-
error: errText(s.error ?? s.tool_response),
|
|
838
|
-
input: s.tool_input,
|
|
839
|
-
output: s.tool_response
|
|
1269
|
+
ok: !s.error,
|
|
1270
|
+
error: typeof s.error === "string" ? s.error.slice(0, 2e3) : void 0,
|
|
1271
|
+
input: toolCall?.args
|
|
840
1272
|
})
|
|
841
1273
|
);
|
|
842
1274
|
break;
|
|
843
|
-
case "PermissionRequest":
|
|
844
|
-
events.push(
|
|
845
|
-
ev("attention.needed", {
|
|
846
|
-
attention_id: call_id ?? permissionId(),
|
|
847
|
-
kind: "permission",
|
|
848
|
-
tool,
|
|
849
|
-
message: tool ? `${tool}${describe(s.tool_input)}` : void 0
|
|
850
|
-
})
|
|
851
|
-
);
|
|
852
|
-
break;
|
|
853
|
-
case "Notification":
|
|
854
|
-
events.push(
|
|
855
|
-
ev("attention.needed", {
|
|
856
|
-
attention_id: `notif-${Date.now()}`,
|
|
857
|
-
kind: notifKind(s.notification_type),
|
|
858
|
-
message: typeof s.message === "string" ? s.message.slice(0, 200) : void 0
|
|
859
|
-
})
|
|
860
|
-
);
|
|
861
|
-
break;
|
|
862
1275
|
case "Stop":
|
|
863
|
-
events.push(ev("turn.ended", {
|
|
864
|
-
break;
|
|
865
|
-
case "StopFailure":
|
|
866
|
-
events.push(
|
|
867
|
-
ev("turn.ended", {
|
|
868
|
-
turn_id,
|
|
869
|
-
reason: "error",
|
|
870
|
-
error: typeof s.error === "string" ? s.error.slice(0, 200) : void 0
|
|
871
|
-
})
|
|
872
|
-
);
|
|
873
|
-
break;
|
|
874
|
-
case "SubagentStart":
|
|
875
|
-
events.push(
|
|
876
|
-
ev("subagent.started", {
|
|
877
|
-
agent_id: String(s.agent_id ?? ""),
|
|
878
|
-
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
879
|
-
})
|
|
880
|
-
);
|
|
881
|
-
break;
|
|
882
|
-
case "SubagentStop":
|
|
883
|
-
events.push(
|
|
884
|
-
ev("subagent.ended", {
|
|
885
|
-
agent_id: String(s.agent_id ?? ""),
|
|
886
|
-
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
887
|
-
})
|
|
888
|
-
);
|
|
889
|
-
break;
|
|
890
|
-
case "PreCompact":
|
|
891
|
-
events.push(ev("context.compacted", { trigger: s.trigger === "manual" ? "manual" : "auto" }));
|
|
892
|
-
break;
|
|
893
|
-
case "PostCompact":
|
|
894
|
-
break;
|
|
895
|
-
// PreCompact already said it; a second event would double-count
|
|
896
|
-
case "SessionEnd":
|
|
897
|
-
events.push(ev("session.ended", { reason: endReason(s.reason) }));
|
|
1276
|
+
events.push(ev("turn.ended", { reason: s.error ? "error" : "stop" }));
|
|
898
1277
|
break;
|
|
899
1278
|
default:
|
|
900
|
-
return
|
|
1279
|
+
return { session, events: [], stdout };
|
|
901
1280
|
}
|
|
902
|
-
const
|
|
903
|
-
if (
|
|
904
|
-
const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
|
|
905
|
-
if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
|
|
906
|
-
if (event !== "PreToolUse" && event !== "PostToolUse") hints.claude_pid = claudePid(input.env);
|
|
907
|
-
const r = { session, events, hints };
|
|
908
|
-
if (typeof s.transcript_path === "string") r.transcript = s.transcript_path;
|
|
1281
|
+
const r = { session, events, stdout, hints: { workspaces } };
|
|
1282
|
+
if (typeof s.transcriptPath === "string") r.transcript = s.transcriptPath;
|
|
909
1283
|
return r;
|
|
910
1284
|
},
|
|
911
1285
|
readTranscript(file, fromLine) {
|
|
@@ -915,331 +1289,162 @@ var claudeCode = {
|
|
|
915
1289
|
} catch {
|
|
916
1290
|
return { turns: [], line: fromLine };
|
|
917
1291
|
}
|
|
918
|
-
return
|
|
1292
|
+
return readAntigravity(text.split("\n"), fromLine);
|
|
919
1293
|
},
|
|
920
|
-
facts(session, transcript, hints
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1294
|
+
facts(session, transcript, hints) {
|
|
1295
|
+
if (!transcript) return {};
|
|
1296
|
+
return antigravityFacts(transcript, {
|
|
1297
|
+
conversation: session.id,
|
|
1298
|
+
workspaces: hints.workspaces ?? (session.cwd ? [session.cwd] : []),
|
|
1299
|
+
env: process.env
|
|
1300
|
+
});
|
|
927
1301
|
},
|
|
928
1302
|
backfill(sinceMs, env = process.env) {
|
|
929
1303
|
const rows2 = [];
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
files.push(...recentFiles(path4.join(d, "projects"), 1, sinceMs, (n) => /^[0-9a-f-]{36}\.jsonl$/.test(n)));
|
|
933
|
-
const noState = { get: () => ({}), save: () => {
|
|
934
|
-
} };
|
|
935
|
-
for (const f of files) {
|
|
936
|
-
const id = path4.basename(f, ".jsonl");
|
|
937
|
-
const facts = claudeFacts(f, id, {}, noState);
|
|
938
|
-
if (!facts.started_at || !facts.last_at) continue;
|
|
939
|
-
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
940
|
-
const cwd = rest.cwd;
|
|
941
|
-
rows2.push({
|
|
942
|
-
session: { id, ...rest, ...gitFacts(cwd), ...rest.branch ? { branch: rest.branch } : {} },
|
|
943
|
-
started_at,
|
|
944
|
-
last_at
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
return rows2;
|
|
948
|
-
}
|
|
949
|
-
};
|
|
950
|
-
function prune2(o) {
|
|
951
|
-
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
952
|
-
return o;
|
|
953
|
-
}
|
|
954
|
-
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
955
|
-
function notClaudeCode(id, s) {
|
|
956
|
-
return typeof s.transcript_path !== "string" && !UUID.test(id);
|
|
957
|
-
}
|
|
958
|
-
function pickSource(v) {
|
|
959
|
-
return typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
960
|
-
}
|
|
961
|
-
function errText(v) {
|
|
962
|
-
if (typeof v === "string") return v.slice(0, 2e3);
|
|
963
|
-
if (v && typeof v === "object") {
|
|
964
|
-
const e = v;
|
|
965
|
-
if (typeof e.error === "string") return e.error.slice(0, 2e3);
|
|
966
|
-
if (typeof e.message === "string") return e.message.slice(0, 2e3);
|
|
967
|
-
}
|
|
968
|
-
return void 0;
|
|
969
|
-
}
|
|
970
|
-
function describe(input) {
|
|
971
|
-
if (!input || typeof input !== "object") return "";
|
|
972
|
-
const i = input;
|
|
973
|
-
const v = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern;
|
|
974
|
-
return typeof v === "string" ? ` ${v.slice(0, 120)}` : "";
|
|
975
|
-
}
|
|
976
|
-
var NAME_RANK = {
|
|
977
|
-
user: "custom",
|
|
978
|
-
auto: "harness",
|
|
979
|
-
hook: "harness",
|
|
980
|
-
peer: "harness"
|
|
981
|
-
};
|
|
982
|
-
function registryEntry(j) {
|
|
983
|
-
if (!j || typeof j !== "object") return null;
|
|
984
|
-
const r = j;
|
|
985
|
-
const source = r.nameSource ? NAME_RANK[r.nameSource] : void 0;
|
|
986
|
-
return {
|
|
987
|
-
sessionId: r.sessionId ? String(r.sessionId) : null,
|
|
988
|
-
name: source && typeof r.name === "string" && r.name.trim() ? r.name.trim() : null,
|
|
989
|
-
name_source: source ?? null,
|
|
990
|
-
bridge: typeof r.bridgeSessionId === "string" ? r.bridgeSessionId : null
|
|
991
|
-
};
|
|
992
|
-
}
|
|
993
|
-
var liveCache = /* @__PURE__ */ new Map();
|
|
994
|
-
function claudeLive(dir) {
|
|
995
|
-
const hit = liveCache.get(dir);
|
|
996
|
-
if (hit) return hit;
|
|
997
|
-
const map = /* @__PURE__ */ new Map();
|
|
998
|
-
let names = [];
|
|
999
|
-
try {
|
|
1000
|
-
names = readdirSync3(path4.join(dir, "sessions")).filter((n) => n.endsWith(".json"));
|
|
1001
|
-
} catch {
|
|
1002
|
-
}
|
|
1003
|
-
for (const n of names) {
|
|
1004
|
-
try {
|
|
1005
|
-
const e = registryEntry(JSON.parse(readFileSync3(path4.join(dir, "sessions", n), "utf8")));
|
|
1006
|
-
if (e?.sessionId) map.set(e.sessionId, e);
|
|
1007
|
-
} catch {
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
liveCache.set(dir, map);
|
|
1011
|
-
return map;
|
|
1012
|
-
}
|
|
1013
|
-
function claudeLiveByPid(dir, pid) {
|
|
1014
|
-
if (!pid) return null;
|
|
1015
|
-
try {
|
|
1016
|
-
return registryEntry(JSON.parse(readFileSync3(path4.join(dir, "sessions", `${pid}.json`), "utf8")));
|
|
1017
|
-
} catch {
|
|
1018
|
-
return null;
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
function claudePid(env = process.env) {
|
|
1022
|
-
try {
|
|
1023
|
-
const dirs = claudeDirs(env);
|
|
1024
|
-
const has = (pid2) => dirs.some((d) => existsSync3(path4.join(d, "sessions", `${pid2}.json`)));
|
|
1025
|
-
let pid = process.ppid;
|
|
1026
|
-
if (has(pid)) return pid;
|
|
1027
|
-
const parent = parentOf();
|
|
1028
|
-
for (let hop = 0; hop < 6 && pid > 1; hop++) {
|
|
1029
|
-
pid = parent(pid);
|
|
1030
|
-
if (pid > 1 && has(pid)) return pid;
|
|
1031
|
-
}
|
|
1032
|
-
} catch {
|
|
1033
|
-
}
|
|
1034
|
-
return null;
|
|
1035
|
-
}
|
|
1036
|
-
function parentOf() {
|
|
1037
|
-
if (process.platform === "linux") {
|
|
1038
|
-
return (pid) => {
|
|
1304
|
+
for (const data of dataDirs(env)) {
|
|
1305
|
+
let ids = [];
|
|
1039
1306
|
try {
|
|
1040
|
-
|
|
1041
|
-
return Number(st.slice(st.lastIndexOf(")") + 2).split(" ")[1]) || 0;
|
|
1307
|
+
ids = readdirSync3(path5.join(data, "brain")).filter((n) => /^[0-9a-f-]{36}$/i.test(n));
|
|
1042
1308
|
} catch {
|
|
1043
|
-
return 0;
|
|
1044
1309
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
if (m) table.set(Number(m[1]), Number(m[2]));
|
|
1310
|
+
for (const id of ids) {
|
|
1311
|
+
const f = path5.join(data, "brain", id, ".system_generated", "logs", "transcript.jsonl");
|
|
1312
|
+
try {
|
|
1313
|
+
if (statSync4(f).mtimeMs < sinceMs) continue;
|
|
1314
|
+
} catch {
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
const facts = antigravityFacts(f, { conversation: id, workspaces: [], env });
|
|
1318
|
+
if (!facts.started_at || !facts.last_at) continue;
|
|
1319
|
+
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
1320
|
+
rows2.push({ session: { id, ...rest }, started_at, last_at });
|
|
1321
|
+
}
|
|
1058
1322
|
}
|
|
1059
|
-
|
|
1323
|
+
return rows2;
|
|
1060
1324
|
}
|
|
1061
|
-
|
|
1325
|
+
};
|
|
1326
|
+
function prune2(o) {
|
|
1327
|
+
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
1328
|
+
return o;
|
|
1062
1329
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
if (
|
|
1067
|
-
for (const f of files) {
|
|
1330
|
+
function agPath(p) {
|
|
1331
|
+
if (typeof p !== "string" || !p) return null;
|
|
1332
|
+
let s = p;
|
|
1333
|
+
if (/^file:/i.test(s)) {
|
|
1068
1334
|
try {
|
|
1069
|
-
|
|
1070
|
-
if (typeof a?.accountUuid === "string" && a.accountUuid) {
|
|
1071
|
-
const email = typeof a.emailAddress === "string" && EMAIL_RE.test(a.emailAddress) ? a.emailAddress : void 0;
|
|
1072
|
-
return { id: a.accountUuid.slice(0, 128), ...email ? { email } : {} };
|
|
1073
|
-
}
|
|
1335
|
+
s = decodeURIComponent(new URL(s).pathname);
|
|
1074
1336
|
} catch {
|
|
1337
|
+
return null;
|
|
1075
1338
|
}
|
|
1076
|
-
|
|
1077
|
-
return null;
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
function claudeAccount(dir) {
|
|
1081
|
-
const hit = accountCache.get(dir);
|
|
1082
|
-
if (hit !== void 0) return hit;
|
|
1083
|
-
const acct = claudeLogin(dir)?.id ?? null;
|
|
1084
|
-
accountCache.set(dir, acct);
|
|
1085
|
-
return acct;
|
|
1086
|
-
}
|
|
1087
|
-
var LINK_RE = /^https:\/\/claude\.ai\/code\/session_[A-Za-z0-9]+$/;
|
|
1088
|
-
function transcriptLink(records) {
|
|
1089
|
-
for (let i = records.length - 1; i >= 0; i--) {
|
|
1090
|
-
const j = records[i];
|
|
1091
|
-
if (j.type === "system" && j.subtype === "bridge_status" && typeof j.url === "string" && LINK_RE.test(j.url))
|
|
1092
|
-
return j.url;
|
|
1093
|
-
if (j.type === "bridge-session" && typeof j.bridgeSessionId === "string") {
|
|
1094
|
-
const m = /^(?:cse|session)_([A-Za-z0-9]+)$/.exec(j.bridgeSessionId);
|
|
1095
|
-
if (m) return `https://claude.ai/code/session_${m[1]}`;
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
return null;
|
|
1099
|
-
}
|
|
1100
|
-
function appTitle(text) {
|
|
1101
|
-
const t = String(text).replace(/\s+/g, " ").trim();
|
|
1102
|
-
return t.length > 50 ? `${t.slice(0, 50)}...` : t;
|
|
1103
|
-
}
|
|
1104
|
-
function askText(j) {
|
|
1105
|
-
if (!j || j.type !== "user" || j.isMeta || j.isSidechain) return null;
|
|
1106
|
-
const origin = j.origin;
|
|
1107
|
-
if (origin?.kind && origin.kind !== "human") return null;
|
|
1108
|
-
const c = j.message?.content;
|
|
1109
|
-
const text = (typeof c === "string" ? c : Array.isArray(c) ? c.filter((b) => b && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n") : "").trim();
|
|
1110
|
-
if (!text || isInjected(text)) return null;
|
|
1111
|
-
return text;
|
|
1112
|
-
}
|
|
1113
|
-
function firstAsk(records) {
|
|
1114
|
-
for (const j of records) {
|
|
1115
|
-
const t = askText(j);
|
|
1116
|
-
if (t) return appTitle(t);
|
|
1117
|
-
}
|
|
1118
|
-
return null;
|
|
1339
|
+
if (process.platform === "win32" && /^\/[A-Za-z]:/.test(s)) s = s.slice(1);
|
|
1340
|
+
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(s)) return null;
|
|
1341
|
+
if (!path5.isAbsolute(s)) return null;
|
|
1342
|
+
return path5.resolve(s);
|
|
1119
1343
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1344
|
+
var agWorkspaces = (paths) => (Array.isArray(paths) ? paths : []).map(agPath).filter((p) => !!p).filter((p, i, a) => a.indexOf(p) === i);
|
|
1345
|
+
function agVariants(p) {
|
|
1346
|
+
const out = [p];
|
|
1123
1347
|
try {
|
|
1124
|
-
|
|
1348
|
+
const r = realpathSync.native(p);
|
|
1349
|
+
if (r !== p) out.push(r);
|
|
1125
1350
|
} catch {
|
|
1126
|
-
return out;
|
|
1127
|
-
}
|
|
1128
|
-
if (size < from) {
|
|
1129
|
-
out.upto = 0;
|
|
1130
|
-
out.custom = out.ai = null;
|
|
1131
|
-
from = 0;
|
|
1132
1351
|
}
|
|
1133
|
-
let pos = 0;
|
|
1134
|
-
scanLines(
|
|
1135
|
-
file,
|
|
1136
|
-
(l) => {
|
|
1137
|
-
pos += Buffer.byteLength(l, "utf8") + 1;
|
|
1138
|
-
if (pos <= from || l.indexOf('-title"') < 0) return null;
|
|
1139
|
-
try {
|
|
1140
|
-
const j = JSON.parse(l);
|
|
1141
|
-
if (j.type === "custom-title" && j.customTitle) out.custom = j.customTitle;
|
|
1142
|
-
if (j.type === "ai-title" && j.aiTitle) out.ai = j.aiTitle;
|
|
1143
|
-
} catch {
|
|
1144
|
-
}
|
|
1145
|
-
return null;
|
|
1146
|
-
},
|
|
1147
|
-
512 * 1024 * 1024
|
|
1148
|
-
);
|
|
1149
|
-
out.upto = size;
|
|
1150
1352
|
return out;
|
|
1151
1353
|
}
|
|
1152
|
-
function
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1155
|
-
const T = parseLines(tail);
|
|
1156
|
-
const out = {};
|
|
1157
|
-
const id = sessionId || path4.basename(file, ".jsonl");
|
|
1158
|
-
const mem = state.get(NAME2, id);
|
|
1159
|
-
let custom = null;
|
|
1160
|
-
let ai = null;
|
|
1161
|
-
for (const j of H.concat(T)) {
|
|
1162
|
-
if (j.type === "custom-title" && typeof j.customTitle === "string") custom = j.customTitle;
|
|
1163
|
-
if (j.type === "ai-title" && typeof j.aiTitle === "string") ai = j.aiTitle;
|
|
1164
|
-
}
|
|
1165
|
-
let size = 0;
|
|
1354
|
+
function agProjects(dir) {
|
|
1355
|
+
const out = [];
|
|
1356
|
+
let names = [];
|
|
1166
1357
|
try {
|
|
1167
|
-
|
|
1358
|
+
names = readdirSync3(dir).filter((n) => n.endsWith(".json"));
|
|
1168
1359
|
} catch {
|
|
1360
|
+
return out;
|
|
1169
1361
|
}
|
|
1170
|
-
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1362
|
+
for (const n of names) {
|
|
1363
|
+
const id = n.slice(0, -5);
|
|
1364
|
+
if (!ID_RE.test(id)) continue;
|
|
1365
|
+
let j;
|
|
1366
|
+
try {
|
|
1367
|
+
j = JSON.parse(readFileSync3(path5.join(dir, n), "utf8"));
|
|
1368
|
+
} catch {
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1371
|
+
const folders = [];
|
|
1372
|
+
for (const r of j?.projectResources?.resources ?? []) {
|
|
1373
|
+
const f = agPath(r?.folderUri ?? r?.gitFolder?.folderUri);
|
|
1374
|
+
if (f) folders.push(...agVariants(f));
|
|
1375
|
+
}
|
|
1376
|
+
if (folders.length) out.push({ id, folders });
|
|
1178
1377
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
const
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
state.save(NAME2, id, { url });
|
|
1378
|
+
return out;
|
|
1379
|
+
}
|
|
1380
|
+
function agMatchProject(projects, workspaces) {
|
|
1381
|
+
const ws = [];
|
|
1382
|
+
for (const w of workspaces) {
|
|
1383
|
+
const p = agPath(w);
|
|
1384
|
+
if (p) ws.push(...agVariants(p));
|
|
1187
1385
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
} catch {
|
|
1197
|
-
return null;
|
|
1386
|
+
let best = null;
|
|
1387
|
+
let bestLen = -1;
|
|
1388
|
+
for (const pr of projects)
|
|
1389
|
+
for (const f of pr.folders) {
|
|
1390
|
+
const within = ws.some((w) => w === f || w.startsWith(f.endsWith(path5.sep) ? f : f + path5.sep));
|
|
1391
|
+
if (within && f.length > bestLen) {
|
|
1392
|
+
best = pr.id;
|
|
1393
|
+
bestLen = f.length;
|
|
1198
1394
|
}
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1395
|
+
}
|
|
1396
|
+
return best;
|
|
1397
|
+
}
|
|
1398
|
+
function antigravityRemoteUrl(transcript, opts) {
|
|
1399
|
+
const i = transcript.indexOf(`${path5.sep}brain${path5.sep}`);
|
|
1400
|
+
const dirs = (i > 0 ? [transcript.slice(0, i)] : []).concat(dataDirs(opts.env));
|
|
1401
|
+
for (const d of dirs) {
|
|
1402
|
+
try {
|
|
1403
|
+
const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
|
|
1404
|
+
readFileSync3(path5.join(d, "antigravity_state.pbtxt"), "utf8")
|
|
1405
|
+
);
|
|
1406
|
+
if (!m) continue;
|
|
1407
|
+
const base = `https://antigravity.google.com/r/${m[1]}-v2`;
|
|
1408
|
+
const conv = opts.conversation ?? "";
|
|
1409
|
+
if (!ID_RE.test(conv) || !opts.workspaces?.length) return base;
|
|
1410
|
+
const project = agMatchProject(agProjects(opts.projectsDir ?? projectsDir(opts.env)), opts.workspaces);
|
|
1411
|
+
return project ? `${base}?p=${encodeURIComponent(`c/${conv}?section=${project}`)}` : base;
|
|
1412
|
+
} catch {
|
|
1413
|
+
}
|
|
1205
1414
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
function antigravityFacts(file, opts) {
|
|
1418
|
+
const { head, tail } = ends(file, 128 * 1024, 64 * 1024);
|
|
1419
|
+
const H = parseLines(head);
|
|
1420
|
+
const T = parseLines(tail);
|
|
1421
|
+
const out = {};
|
|
1422
|
+
const url = antigravityRemoteUrl(file, opts);
|
|
1423
|
+
if (url) out.url = url;
|
|
1424
|
+
for (const j of H) {
|
|
1425
|
+
if (j.type !== "USER_INPUT" || typeof j.content !== "string") continue;
|
|
1426
|
+
const m = /<USER_REQUEST>([\s\S]*?)(?:<\/USER_REQUEST>|$)/.exec(j.content);
|
|
1427
|
+
const text = (m ? m[1] : j.content).trim();
|
|
1428
|
+
const line = (text.split("\n").find((l) => l.trim()) ?? "").trim();
|
|
1429
|
+
if (!line || /^<[a-z_-]+>/i.test(line)) continue;
|
|
1430
|
+
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
1211
1431
|
out.title_source = "first-ask";
|
|
1432
|
+
break;
|
|
1212
1433
|
}
|
|
1213
|
-
const
|
|
1214
|
-
if (acct) out.account_id = acct;
|
|
1215
|
-
for (let i = T.length - 1; i >= 0; i--) {
|
|
1216
|
-
const m = T[i].message?.model;
|
|
1217
|
-
if (T[i]?.type === "assistant" && m && m !== "<synthetic>") {
|
|
1218
|
-
out.model = m;
|
|
1219
|
-
break;
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
const meta = [...T].reverse().find((j) => j.version) ?? H.find((j) => j.version);
|
|
1223
|
-
if (meta?.version) out.harness_version = String(meta.version);
|
|
1224
|
-
const times = H.concat(T).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
1434
|
+
const times = H.concat(T).map((j) => Date.parse(String(j.created_at ?? j.timestamp ?? ""))).filter((n) => n > 0);
|
|
1225
1435
|
if (times.length) {
|
|
1226
1436
|
out.started_at = Math.min(...times);
|
|
1227
1437
|
out.last_at = Math.max(...times);
|
|
1228
1438
|
}
|
|
1229
|
-
const withCwd = H.find((j) => typeof j.cwd === "string");
|
|
1230
|
-
if (withCwd) {
|
|
1231
|
-
out.cwd = withCwd.cwd;
|
|
1232
|
-
if (typeof withCwd.gitBranch === "string" && withCwd.gitBranch !== "HEAD") out.branch = withCwd.gitBranch;
|
|
1233
|
-
}
|
|
1234
1439
|
return out;
|
|
1235
1440
|
}
|
|
1236
1441
|
|
|
1237
1442
|
// ../core/src/adapters/codex.ts
|
|
1238
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync4, writeFileSync as
|
|
1239
|
-
import
|
|
1443
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1444
|
+
import path6 from "node:path";
|
|
1240
1445
|
|
|
1241
1446
|
// ../core/src/adapters/codex-config.ts
|
|
1242
|
-
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)
|
|
1447
|
+
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?(?=\r?$)/m;
|
|
1243
1448
|
var NOTIFY_KEY = /^[ \t]*notify[ \t]*=/m;
|
|
1244
1449
|
var OURS2 = /(?:hook\.js|sessionpipe-hook)",\s*"codex",\s*"notify"/;
|
|
1245
1450
|
function scan(line, st) {
|
|
@@ -1329,10 +1534,11 @@ function dropLine(text, index, length) {
|
|
|
1329
1534
|
}
|
|
1330
1535
|
function insertTopLevel(text, line) {
|
|
1331
1536
|
const { head, tail } = split(text);
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1537
|
+
const body = head.replace(/\s*$/, "");
|
|
1538
|
+
const eol = (body.includes("\n") ? body : text).includes("\r\n") ? "\r\n" : "\n";
|
|
1539
|
+
const top = head.trim() ? head.replace(/\s*$/, eol) : "";
|
|
1540
|
+
if (!tail) return top + line + eol;
|
|
1541
|
+
return `${top + line}${eol}${eol}${tail}`;
|
|
1336
1542
|
}
|
|
1337
1543
|
function withNotify(text, line) {
|
|
1338
1544
|
const n = readNotify(text);
|
|
@@ -1381,10 +1587,10 @@ var CODEX_EVENTS = [
|
|
|
1381
1587
|
];
|
|
1382
1588
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1383
1589
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1384
|
-
var codexHome = (env) => env.CODEX_HOME ||
|
|
1385
|
-
var hooksFile2 = (env) =>
|
|
1386
|
-
var configFile = (env) =>
|
|
1387
|
-
var sessionsDir = (env) =>
|
|
1590
|
+
var codexHome = (env) => env.CODEX_HOME || path6.join(homeDir(), ".codex");
|
|
1591
|
+
var hooksFile2 = (env) => path6.join(codexHome(env), "hooks.json");
|
|
1592
|
+
var configFile = (env) => path6.join(codexHome(env), "config.toml");
|
|
1593
|
+
var sessionsDir = (env) => path6.join(codexHome(env), "sessions");
|
|
1388
1594
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1389
1595
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
1390
1596
|
function installNotify(cmd, env) {
|
|
@@ -1398,8 +1604,8 @@ function installNotify(cmd, env) {
|
|
|
1398
1604
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
1399
1605
|
const n = readNotify(text);
|
|
1400
1606
|
const write = (next) => {
|
|
1401
|
-
mkdirSync3(
|
|
1402
|
-
|
|
1607
|
+
mkdirSync3(path6.dirname(file), { recursive: true });
|
|
1608
|
+
writeFileSync4(file, next);
|
|
1403
1609
|
};
|
|
1404
1610
|
if (n && !n.other) {
|
|
1405
1611
|
if (!n.misplaced && JSON.stringify(n.argv) === JSON.stringify(argv))
|
|
@@ -1444,12 +1650,12 @@ function uninstallNotify(env) {
|
|
|
1444
1650
|
const n = readNotify(text);
|
|
1445
1651
|
if (!n) return { file, changed: false };
|
|
1446
1652
|
if (!n.other) {
|
|
1447
|
-
|
|
1653
|
+
writeFileSync4(file, withoutNotify(text));
|
|
1448
1654
|
return { file, changed: true };
|
|
1449
1655
|
}
|
|
1450
1656
|
const prev = previousNotify(n.argv);
|
|
1451
1657
|
if (isComputerUse(n.argv) && prev && OURS2.test(JSON.stringify(prev))) {
|
|
1452
|
-
|
|
1658
|
+
writeFileSync4(file, replaceNotify(text, n, `notify = ${stripPreviousRaw(n.raw)}`));
|
|
1453
1659
|
return { file, changed: true };
|
|
1454
1660
|
}
|
|
1455
1661
|
return { file, changed: false };
|
|
@@ -1465,7 +1671,7 @@ function findRollout(threadId, env = process.env) {
|
|
|
1465
1671
|
return;
|
|
1466
1672
|
}
|
|
1467
1673
|
for (const n of names.sort().reverse()) {
|
|
1468
|
-
const p =
|
|
1674
|
+
const p = path6.join(dir, n);
|
|
1469
1675
|
if (n.endsWith(`-${threadId}.jsonl`)) {
|
|
1470
1676
|
best = p;
|
|
1471
1677
|
return;
|
|
@@ -1555,7 +1761,7 @@ var codex = {
|
|
|
1555
1761
|
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
1556
1762
|
} catch {
|
|
1557
1763
|
}
|
|
1558
|
-
const id = s.session_id;
|
|
1764
|
+
const id = s.session_id ?? s.thread_id ?? s.conversation_id ?? s["thread-id"] ?? s["session-id"];
|
|
1559
1765
|
if (!id) return null;
|
|
1560
1766
|
if (s.transcript_path === null) return null;
|
|
1561
1767
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
@@ -1650,7 +1856,7 @@ var codex = {
|
|
|
1650
1856
|
backfill(sinceMs, env = process.env) {
|
|
1651
1857
|
const rows2 = [];
|
|
1652
1858
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
1653
|
-
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(
|
|
1859
|
+
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path6.basename(f));
|
|
1654
1860
|
if (!m?.[1]) continue;
|
|
1655
1861
|
if (codexSubagent(rolloutMeta(f))) continue;
|
|
1656
1862
|
const facts = codexFacts(f);
|
|
@@ -1659,12 +1865,40 @@ var codex = {
|
|
|
1659
1865
|
rows2.push({ session: { id: m[1], ...rest }, started_at, last_at });
|
|
1660
1866
|
}
|
|
1661
1867
|
return rows2;
|
|
1662
|
-
}
|
|
1868
|
+
},
|
|
1869
|
+
limits: (transcript) => codexLimits(transcript)
|
|
1663
1870
|
};
|
|
1871
|
+
var epochIso2 = (t) => typeof t === "number" && t > 0 && t < 1e11 ? new Date(t * 1e3).toISOString() : void 0;
|
|
1664
1872
|
function prune3(o) {
|
|
1665
1873
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
1666
1874
|
return o;
|
|
1667
1875
|
}
|
|
1876
|
+
function codexLimits(file) {
|
|
1877
|
+
const { tail } = ends(file, 0, 256 * 1024);
|
|
1878
|
+
for (const r of parseLines(tail).reverse()) {
|
|
1879
|
+
const p = r.payload;
|
|
1880
|
+
if (r.type !== "event_msg" || p?.type !== "token_count") continue;
|
|
1881
|
+
if (!p.rate_limits || typeof p.rate_limits !== "object") continue;
|
|
1882
|
+
const rl = p.rate_limits;
|
|
1883
|
+
if (rl.limit_id != null && rl.limit_id !== "codex") continue;
|
|
1884
|
+
const windows = [];
|
|
1885
|
+
for (const id of ["primary", "secondary"]) {
|
|
1886
|
+
const w = rl[id];
|
|
1887
|
+
if (typeof w?.used_percent !== "number" || w.used_percent < 0) continue;
|
|
1888
|
+
windows.push(
|
|
1889
|
+
prune3({
|
|
1890
|
+
id,
|
|
1891
|
+
used_percent: w.used_percent,
|
|
1892
|
+
window_minutes: Number.isInteger(w.window_minutes) && Number(w.window_minutes) > 0 ? Number(w.window_minutes) : void 0,
|
|
1893
|
+
resets_at: epochIso2(w.resets_at)
|
|
1894
|
+
})
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1897
|
+
if (!windows.length) return null;
|
|
1898
|
+
return prune3({ windows, plan: typeof rl.plan_type === "string" ? rl.plan_type.slice(0, 64) : void 0 });
|
|
1899
|
+
}
|
|
1900
|
+
return null;
|
|
1901
|
+
}
|
|
1668
1902
|
function codexFacts(file) {
|
|
1669
1903
|
const { head, tail } = ends(file, 128 * 1024, 64 * 1024);
|
|
1670
1904
|
let H = parseLines(head);
|
|
@@ -1709,7 +1943,7 @@ function codexFacts(file) {
|
|
|
1709
1943
|
// ../core/src/adapters/devin.ts
|
|
1710
1944
|
import { createHash as createHash2 } from "node:crypto";
|
|
1711
1945
|
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
1712
|
-
import
|
|
1946
|
+
import path7 from "node:path";
|
|
1713
1947
|
|
|
1714
1948
|
// ../core/src/readers/sqlite.ts
|
|
1715
1949
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
@@ -1893,11 +2127,11 @@ function foreignEvent(file) {
|
|
|
1893
2127
|
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
1894
2128
|
}
|
|
1895
2129
|
var homeOf = (env) => env.HOME || homeDir();
|
|
1896
|
-
var appData = (env) => env.APPDATA ||
|
|
1897
|
-
var devinConfigDir = (env = process.env) => process.platform === "win32" ?
|
|
1898
|
-
var configFile2 = (env) =>
|
|
1899
|
-
var devinDataDir = (env = process.env) => process.platform === "win32" ?
|
|
1900
|
-
var sessionsDb = (env) =>
|
|
2130
|
+
var appData = (env) => env.APPDATA || path7.join(homeOf(env), "AppData", "Roaming");
|
|
2131
|
+
var devinConfigDir = (env = process.env) => process.platform === "win32" ? path7.join(appData(env), "devin") : path7.join(env.XDG_CONFIG_HOME || path7.join(homeOf(env), ".config"), "devin");
|
|
2132
|
+
var configFile2 = (env) => path7.join(devinConfigDir(env), "config.json");
|
|
2133
|
+
var devinDataDir = (env = process.env) => process.platform === "win32" ? path7.join(appData(env), "devin", "cli") : path7.join(env.XDG_DATA_HOME || path7.join(homeOf(env), ".local", "share"), "devin", "cli");
|
|
2134
|
+
var sessionsDb = (env) => path7.join(devinDataDir(env), "sessions.db");
|
|
1901
2135
|
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
1902
2136
|
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1903
2137
|
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
@@ -2069,7 +2303,7 @@ var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
|
2069
2303
|
function sessionFacts(row) {
|
|
2070
2304
|
const out = {};
|
|
2071
2305
|
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
2072
|
-
if (typeof row.working_directory === "string" &&
|
|
2306
|
+
if (typeof row.working_directory === "string" && path7.isAbsolute(row.working_directory))
|
|
2073
2307
|
out.cwd = row.working_directory;
|
|
2074
2308
|
const started = Number(row.created_at);
|
|
2075
2309
|
const last = Number(row.last_activity_at);
|
|
@@ -2114,8 +2348,8 @@ function readDevinChain(db, session, fromLine) {
|
|
|
2114
2348
|
}
|
|
2115
2349
|
function devinVersion(env = process.env) {
|
|
2116
2350
|
try {
|
|
2117
|
-
const target = readlinkSync(
|
|
2118
|
-
const name =
|
|
2351
|
+
const target = readlinkSync(path7.join(devinDataDir(env), "_versions", "current"));
|
|
2352
|
+
const name = path7.basename(target);
|
|
2119
2353
|
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
2120
2354
|
} catch {
|
|
2121
2355
|
return void 0;
|
|
@@ -2130,7 +2364,7 @@ function devinAccount(env = process.env) {
|
|
|
2130
2364
|
// ../core/src/adapters/gemini-cli.ts
|
|
2131
2365
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2132
2366
|
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
|
|
2133
|
-
import
|
|
2367
|
+
import path8 from "node:path";
|
|
2134
2368
|
var NAME5 = "gemini-cli";
|
|
2135
2369
|
var GEMINI_EVENTS = [
|
|
2136
2370
|
"SessionStart",
|
|
@@ -2143,26 +2377,31 @@ var GEMINI_EVENTS = [
|
|
|
2143
2377
|
"SessionEnd"
|
|
2144
2378
|
];
|
|
2145
2379
|
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
2146
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
2147
|
-
var settings = (env) =>
|
|
2380
|
+
var geminiDir = (env) => env.GEMINI_CLI_HOME || path8.join(homeDir(), ".gemini");
|
|
2381
|
+
var settings = (env) => path8.join(geminiDir(env), "settings.json");
|
|
2148
2382
|
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2149
2383
|
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
2384
|
+
function geminiVersion(platform = process.platform, env = process.env) {
|
|
2385
|
+
const win = platform === "win32";
|
|
2386
|
+
const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path8.dirname(process.execPath)], env }) : "gemini";
|
|
2387
|
+
if (!bin) throw new Error("no gemini on PATH");
|
|
2388
|
+
const o = {
|
|
2389
|
+
encoding: "utf8",
|
|
2390
|
+
timeout: 4e3,
|
|
2391
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2392
|
+
env
|
|
2393
|
+
};
|
|
2394
|
+
const out = /\.cmd$/i.test(bin) ? execFileSync4(`"${bin}" --version`, { ...o, shell: true }) : execFileSync4(bin, ["--version"], o);
|
|
2395
|
+
return out.trim();
|
|
2396
|
+
}
|
|
2150
2397
|
function geminiHooksActive(settingsFile) {
|
|
2151
|
-
|
|
2152
|
-
try {
|
|
2153
|
-
tools = JSON.parse(readFileSync5(settingsFile, "utf8")).tools;
|
|
2154
|
-
} catch {
|
|
2155
|
-
}
|
|
2398
|
+
const tools = readJson(settingsFile).tools;
|
|
2156
2399
|
if (tools?.enableHooks === true) return { active: true };
|
|
2157
2400
|
if (tools?.enableHooks === false)
|
|
2158
2401
|
return { active: false, note: "inactive: tools.enableHooks is false in settings.json" };
|
|
2159
2402
|
let version = "";
|
|
2160
2403
|
try {
|
|
2161
|
-
version =
|
|
2162
|
-
encoding: "utf8",
|
|
2163
|
-
timeout: 4e3,
|
|
2164
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
2165
|
-
}).trim();
|
|
2404
|
+
version = geminiVersion();
|
|
2166
2405
|
} catch {
|
|
2167
2406
|
return {
|
|
2168
2407
|
active: true,
|
|
@@ -2180,7 +2419,7 @@ var geminiCli = {
|
|
|
2180
2419
|
name: NAME5,
|
|
2181
2420
|
events: GEMINI_EVENTS,
|
|
2182
2421
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
2183
|
-
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(
|
|
2422
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path8.join(geminiDir(env), "tmp")),
|
|
2184
2423
|
configFiles: (env = process.env) => [settings(env)],
|
|
2185
2424
|
install: (cmd, env = process.env, opts = {}) => {
|
|
2186
2425
|
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
@@ -2317,6 +2556,22 @@ function prune5(o) {
|
|
|
2317
2556
|
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
2318
2557
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
2319
2558
|
|
|
2559
|
+
// ../core/src/launched-by.ts
|
|
2560
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
2561
|
+
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
2562
|
+
function launchedBy(env) {
|
|
2563
|
+
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
2564
|
+
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
2565
|
+
}
|
|
2566
|
+
function readHook(adapter, input) {
|
|
2567
|
+
const r = adapter.fromHook(input);
|
|
2568
|
+
if (r && !r.session.parent_id) {
|
|
2569
|
+
const launcher = launchedBy(input.env);
|
|
2570
|
+
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
2571
|
+
}
|
|
2572
|
+
return r;
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2320
2575
|
// src/index.ts
|
|
2321
2576
|
function matches(expect, got, pathStr = "") {
|
|
2322
2577
|
if (expect === "*") return null;
|
|
@@ -2347,7 +2602,7 @@ async function runAdapterFixtures(files) {
|
|
|
2347
2602
|
results.push({ file, ok: false, why: `no adapter ${fx.harness}` });
|
|
2348
2603
|
continue;
|
|
2349
2604
|
}
|
|
2350
|
-
const r = adapter
|
|
2605
|
+
const r = readHook(adapter, {
|
|
2351
2606
|
argv: fx.input.argv,
|
|
2352
2607
|
stdin: fx.input.stdin,
|
|
2353
2608
|
env: fx.input.env ?? {},
|