sessionpipe-conformance 0.7.0 → 0.9.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/README.md +1 -1
- package/dist/cli.d.ts +6 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +647 -158
- package/dist/cli.js.map +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +580 -105
- package/dist/index.js.map +4 -4
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sessionpipe-conformance 0.
|
|
2
|
+
// sessionpipe-conformance 0.9.0 · Apache-2.0 · https://sessionpipe.org
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
|
-
import { readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
|
|
6
|
-
import
|
|
5
|
+
import { readdirSync as readdirSync5, realpathSync as realpathSync2, statSync as statSync4 } from "node:fs";
|
|
6
|
+
import path8 from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
7
8
|
|
|
8
9
|
// src/index.ts
|
|
9
10
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
@@ -23,7 +24,7 @@ import path3 from "node:path";
|
|
|
23
24
|
|
|
24
25
|
// ../core/src/paths.ts
|
|
25
26
|
import os from "node:os";
|
|
26
|
-
var
|
|
27
|
+
var homeDir = () => os.homedir();
|
|
27
28
|
|
|
28
29
|
// ../core/src/readers/files.ts
|
|
29
30
|
import { closeSync, fstatSync, openSync, readdirSync, readSync, statSync } from "node:fs";
|
|
@@ -72,8 +73,8 @@ function scanLines(file, visit, cap = 16 * 1024 * 1024) {
|
|
|
72
73
|
let chunk = Buffer.concat([rest, buf.subarray(0, n)]);
|
|
73
74
|
let nl = chunk.indexOf(10);
|
|
74
75
|
while (nl >= 0) {
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
76
|
+
const r = visit(chunk.subarray(0, nl).toString("utf8"));
|
|
77
|
+
if (r !== null) return r;
|
|
77
78
|
chunk = chunk.subarray(nl + 1);
|
|
78
79
|
nl = chunk.indexOf(10);
|
|
79
80
|
}
|
|
@@ -115,6 +116,19 @@ function recentFiles(root, depth, sinceMs, match) {
|
|
|
115
116
|
return out;
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
// ../core/src/adapters/codex-rollout.ts
|
|
120
|
+
function codexSubagent(meta) {
|
|
121
|
+
if (meta?.type !== "session_meta") return false;
|
|
122
|
+
const p = meta.payload;
|
|
123
|
+
return !!p?.source?.subagent;
|
|
124
|
+
}
|
|
125
|
+
function codexReplayBoundary(meta) {
|
|
126
|
+
if (meta?.type !== "session_meta") return void 0;
|
|
127
|
+
const p = meta.payload;
|
|
128
|
+
if (!p || !p.forked_from_id && !p.source?.subagent) return void 0;
|
|
129
|
+
return (record) => typeof p.id === "string" && !!p.id && record?.type === "event_msg" && record.payload?.thread_id === p.id;
|
|
130
|
+
}
|
|
131
|
+
|
|
118
132
|
// ../core/src/readers/transcripts.ts
|
|
119
133
|
function pairTurns(msgs, endLine) {
|
|
120
134
|
const turns = [];
|
|
@@ -137,10 +151,14 @@ ${m.text}`;
|
|
|
137
151
|
}
|
|
138
152
|
if (cur?.assistant) turns.push(cur);
|
|
139
153
|
const line = cur && !cur.assistant ? cur.startLine : endLine;
|
|
140
|
-
|
|
154
|
+
const out = {
|
|
155
|
+
turns: turns.map((t) => ({ user: t.user, assistant: t.assistant, at: t.at, endLine: t.endLine })),
|
|
156
|
+
line
|
|
157
|
+
};
|
|
158
|
+
if (cur && !cur.assistant && cur.user) out.open = { user: cur.user, at: cur.at };
|
|
159
|
+
return out;
|
|
141
160
|
}
|
|
142
|
-
var isInjected = (text) => /^<[a-z_-]
|
|
143
|
-
var endOf = (lines) => lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
|
|
161
|
+
var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
|
|
144
162
|
var parse = (raw) => {
|
|
145
163
|
try {
|
|
146
164
|
return JSON.parse(raw);
|
|
@@ -148,6 +166,11 @@ var parse = (raw) => {
|
|
|
148
166
|
return null;
|
|
149
167
|
}
|
|
150
168
|
};
|
|
169
|
+
var endOf = (lines) => {
|
|
170
|
+
const last = lines[lines.length - 1];
|
|
171
|
+
if (last === void 0) return 0;
|
|
172
|
+
return last === "" || parse(last) === null ? lines.length - 1 : lines.length;
|
|
173
|
+
};
|
|
151
174
|
var textParts = (c, kinds) => {
|
|
152
175
|
if (typeof c === "string") return c;
|
|
153
176
|
if (!Array.isArray(c)) return "";
|
|
@@ -155,13 +178,21 @@ var textParts = (c, kinds) => {
|
|
|
155
178
|
(b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
|
|
156
179
|
).map((b) => b.text).join("\n");
|
|
157
180
|
};
|
|
181
|
+
var queuedPrompt = (a) => {
|
|
182
|
+
if (!a || typeof a !== "object") return "";
|
|
183
|
+
const q = a;
|
|
184
|
+
if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
|
|
185
|
+
if (q.origin?.kind && q.origin.kind !== "human") return "";
|
|
186
|
+
return textParts(q.prompt, ["text"]).trim();
|
|
187
|
+
};
|
|
158
188
|
function readClaude(lines, from) {
|
|
159
189
|
const msgs = [];
|
|
160
190
|
for (let i = from; i < lines.length; i++) {
|
|
161
191
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
162
192
|
if (!j || j.isSidechain) continue;
|
|
163
193
|
const origin = j.origin;
|
|
164
|
-
if (j.type === "user" && (j.isMeta || origin?.kind && origin.kind !== "human"))
|
|
194
|
+
if (j.type === "user" && (j.isMeta || j.isCompactSummary || j.repeatsCompactSummary || origin?.kind && origin.kind !== "human"))
|
|
195
|
+
continue;
|
|
165
196
|
const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
|
|
166
197
|
const c = j.message?.content;
|
|
167
198
|
if (j.type === "user") {
|
|
@@ -171,6 +202,9 @@ function readClaude(lines, from) {
|
|
|
171
202
|
} else if (j.type === "assistant" && Array.isArray(c)) {
|
|
172
203
|
const text = textParts(c, ["text"]).trim();
|
|
173
204
|
if (text) msgs.push({ role: "assistant", text, at, line: i });
|
|
205
|
+
} else if (j.type === "attachment") {
|
|
206
|
+
const text = queuedPrompt(j.attachment);
|
|
207
|
+
if (text && !isInjected(text)) msgs.push({ role: "user", text, at, line: i });
|
|
174
208
|
}
|
|
175
209
|
}
|
|
176
210
|
return pairTurns(msgs, endOf(lines));
|
|
@@ -180,7 +214,10 @@ function codexInjected(text) {
|
|
|
180
214
|
}
|
|
181
215
|
function readCodex(lines, from) {
|
|
182
216
|
const msgs = [];
|
|
183
|
-
|
|
217
|
+
const boundary = codexReplayBoundary(parse(lines.find((line) => line.trim()) ?? ""));
|
|
218
|
+
const start = boundary ? lines.findIndex((line) => boundary(parse(line))) : 0;
|
|
219
|
+
if (start < 0) return pairTurns(msgs, endOf(lines));
|
|
220
|
+
for (let i = Math.max(from, start); i < lines.length; i++) {
|
|
184
221
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
185
222
|
if (!j || j.type !== "response_item") continue;
|
|
186
223
|
const p = j.payload;
|
|
@@ -218,7 +255,7 @@ function readAntigravity(lines, from) {
|
|
|
218
255
|
const at = Date.parse(String(j.created_at ?? j.timestamp ?? "")) || Date.now();
|
|
219
256
|
if (j.type === "USER_INPUT") {
|
|
220
257
|
const m = /<USER_REQUEST>([\s\S]*?)(?:<\/USER_REQUEST>|$)/.exec(j.content);
|
|
221
|
-
const text = (m ? m[1]
|
|
258
|
+
const text = (m ? m[1] : j.content).trim();
|
|
222
259
|
if (text) msgs.push({ role: "user", text, at, line: i });
|
|
223
260
|
} else if (j.type === "PLANNER_RESPONSE") {
|
|
224
261
|
const text = j.content.trim();
|
|
@@ -266,6 +303,10 @@ function formatJson(value, f) {
|
|
|
266
303
|
const text = JSON.stringify(value, null, f.indent).replace(/\n/g, f.eol);
|
|
267
304
|
return f.finalNewline ? text + f.eol : text;
|
|
268
305
|
}
|
|
306
|
+
function readJson(file) {
|
|
307
|
+
const f = readJsonFile(file);
|
|
308
|
+
return f.ok ? f.value : {};
|
|
309
|
+
}
|
|
269
310
|
function entryFor(event, harness, cmd, o) {
|
|
270
311
|
const matcher = o.matcherFor(event);
|
|
271
312
|
return {
|
|
@@ -335,8 +376,7 @@ function uninstallClaudeShaped(file, events, opts = {}) {
|
|
|
335
376
|
unlinkSync(file);
|
|
336
377
|
return { file, changed: true, note: "removed (sessionpipe created it)" };
|
|
337
378
|
}
|
|
338
|
-
|
|
339
|
-
if (next !== f.raw) writeFileSync(file, next);
|
|
379
|
+
writeFileSync(file, formatJson(settings2, f));
|
|
340
380
|
return { file, changed: true };
|
|
341
381
|
}
|
|
342
382
|
function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
@@ -363,7 +403,7 @@ var HOOK_NAME = "sessionpipe";
|
|
|
363
403
|
var AG_EVENTS = ["PreInvocation", "PostInvocation", "PostToolUse", "Stop"];
|
|
364
404
|
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';
|
|
365
405
|
var antigravityAnswer = (event) => event === "PreToolUse" ? AG_STALE_PRETOOL : "{}\n";
|
|
366
|
-
var gemini = (env) => env.GEMINI_CLI_HOME || path3.join(
|
|
406
|
+
var gemini = (env) => env.GEMINI_CLI_HOME || path3.join(homeDir(), ".gemini");
|
|
367
407
|
var hooksFile = (env) => path3.join(gemini(env), "config", "hooks.json");
|
|
368
408
|
var projectsDir = (env) => path3.join(gemini(env), "config", "projects");
|
|
369
409
|
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) => path3.join(gemini(env), d));
|
|
@@ -429,7 +469,8 @@ var antigravity = {
|
|
|
429
469
|
const event = input.argv[1] ?? "";
|
|
430
470
|
let s = {};
|
|
431
471
|
try {
|
|
432
|
-
|
|
472
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
473
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
433
474
|
} catch {
|
|
434
475
|
}
|
|
435
476
|
const id = s.conversationId ?? s.session_id ?? s.sessionId;
|
|
@@ -472,9 +513,9 @@ var antigravity = {
|
|
|
472
513
|
default:
|
|
473
514
|
return { session, events: [], stdout };
|
|
474
515
|
}
|
|
475
|
-
const
|
|
476
|
-
if (typeof s.transcriptPath === "string")
|
|
477
|
-
return
|
|
516
|
+
const r = { session, events, stdout, hints: { workspaces } };
|
|
517
|
+
if (typeof s.transcriptPath === "string") r.transcript = s.transcriptPath;
|
|
518
|
+
return r;
|
|
478
519
|
},
|
|
479
520
|
readTranscript(file, fromLine) {
|
|
480
521
|
let text = "";
|
|
@@ -485,19 +526,16 @@ var antigravity = {
|
|
|
485
526
|
}
|
|
486
527
|
return readAntigravity(text.split("\n"), fromLine);
|
|
487
528
|
},
|
|
488
|
-
facts(session, transcript, hints
|
|
529
|
+
facts(session, transcript, hints) {
|
|
489
530
|
if (!transcript) return {};
|
|
490
531
|
return antigravityFacts(transcript, {
|
|
491
532
|
conversation: session.id,
|
|
492
533
|
workspaces: hints.workspaces ?? (session.cwd ? [session.cwd] : []),
|
|
493
|
-
state,
|
|
494
534
|
env: process.env
|
|
495
535
|
});
|
|
496
536
|
},
|
|
497
537
|
backfill(sinceMs, env = process.env) {
|
|
498
|
-
const
|
|
499
|
-
const noState = { get: () => ({}), save: () => {
|
|
500
|
-
} };
|
|
538
|
+
const rows2 = [];
|
|
501
539
|
for (const data of dataDirs(env)) {
|
|
502
540
|
let ids = [];
|
|
503
541
|
try {
|
|
@@ -511,13 +549,13 @@ var antigravity = {
|
|
|
511
549
|
} catch {
|
|
512
550
|
continue;
|
|
513
551
|
}
|
|
514
|
-
const facts = antigravityFacts(f, { conversation: id, workspaces: [],
|
|
552
|
+
const facts = antigravityFacts(f, { conversation: id, workspaces: [], env });
|
|
515
553
|
if (!facts.started_at || !facts.last_at) continue;
|
|
516
554
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
517
|
-
|
|
555
|
+
rows2.push({ session: { id, ...rest }, started_at, last_at });
|
|
518
556
|
}
|
|
519
557
|
}
|
|
520
|
-
return
|
|
558
|
+
return rows2;
|
|
521
559
|
}
|
|
522
560
|
};
|
|
523
561
|
function prune(o) {
|
|
@@ -542,8 +580,8 @@ var agWorkspaces = (paths) => (Array.isArray(paths) ? paths : []).map(agPath).fi
|
|
|
542
580
|
function agVariants(p) {
|
|
543
581
|
const out = [p];
|
|
544
582
|
try {
|
|
545
|
-
const
|
|
546
|
-
if (
|
|
583
|
+
const r = realpathSync.native(p);
|
|
584
|
+
if (r !== p) out.push(r);
|
|
547
585
|
} catch {
|
|
548
586
|
}
|
|
549
587
|
return out;
|
|
@@ -566,8 +604,8 @@ function agProjects(dir) {
|
|
|
566
604
|
continue;
|
|
567
605
|
}
|
|
568
606
|
const folders = [];
|
|
569
|
-
for (const
|
|
570
|
-
const f = agPath(
|
|
607
|
+
for (const r of j?.projectResources?.resources ?? []) {
|
|
608
|
+
const f = agPath(r?.folderUri ?? r?.gitFolder?.folderUri);
|
|
571
609
|
if (f) folders.push(...agVariants(f));
|
|
572
610
|
}
|
|
573
611
|
if (folders.length) out.push({ id, folders });
|
|
@@ -621,7 +659,7 @@ function antigravityFacts(file, opts) {
|
|
|
621
659
|
for (const j of H) {
|
|
622
660
|
if (j.type !== "USER_INPUT" || typeof j.content !== "string") continue;
|
|
623
661
|
const m = /<USER_REQUEST>([\s\S]*?)(?:<\/USER_REQUEST>|$)/.exec(j.content);
|
|
624
|
-
const text = (m ? m[1]
|
|
662
|
+
const text = (m ? m[1] : j.content).trim();
|
|
625
663
|
const line = (text.split("\n").find((l) => l.trim()) ?? "").trim();
|
|
626
664
|
if (!line || /^<[a-z_-]+>/i.test(line)) continue;
|
|
627
665
|
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
@@ -669,10 +707,9 @@ function gitFacts(cwd) {
|
|
|
669
707
|
return out;
|
|
670
708
|
}
|
|
671
709
|
function stripCredentials(url) {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
return u.replace(/^https?:\/\//, "").replace(/\.git$/, "");
|
|
710
|
+
const ssh = /^(?:ssh:\/\/)?git@([^:/]+)[:/](.+?)(?:\.git)?$/.exec(url);
|
|
711
|
+
if (ssh) return `${ssh[1]}/${ssh[2]}`;
|
|
712
|
+
return url.replace(/\/\/[^/@]*@/, "//").replace(/^https?:\/\//, "").replace(/\.git$/, "");
|
|
676
713
|
}
|
|
677
714
|
|
|
678
715
|
// ../core/src/adapters/claude-code.ts
|
|
@@ -703,12 +740,12 @@ var OPTS = {
|
|
|
703
740
|
timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
|
|
704
741
|
};
|
|
705
742
|
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
706
|
-
function claudeDirs(env = process.env, home = env.HOME ||
|
|
743
|
+
function claudeDirs(env = process.env, home = env.HOME || homeDir()) {
|
|
707
744
|
const out = [];
|
|
708
745
|
const add = (d) => {
|
|
709
746
|
if (!d) return;
|
|
710
|
-
const
|
|
711
|
-
if (!out.includes(
|
|
747
|
+
const r = path4.resolve(d);
|
|
748
|
+
if (!out.includes(r)) out.push(r);
|
|
712
749
|
};
|
|
713
750
|
add(path4.join(home, ".claude"));
|
|
714
751
|
add(env.CLAUDE_CONFIG_DIR);
|
|
@@ -725,7 +762,7 @@ function claudeDirs(env = process.env, home = env.HOME || HOME) {
|
|
|
725
762
|
var settingsIn = (dir) => path4.join(dir, "settings.json");
|
|
726
763
|
var configDirOf = (transcript) => path4.dirname(path4.dirname(path4.dirname(transcript)));
|
|
727
764
|
var validTool2 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
728
|
-
var endReason = (
|
|
765
|
+
var endReason = (r) => r === "clear" || r === "logout" || r === "exit" ? r : r === "prompt_input_exit" ? "exit" : "other";
|
|
729
766
|
var notifKind = (t) => t === "permission_prompt" ? "permission" : t === "idle_prompt" ? "idle" : t === "elicitation_dialog" ? "elicitation" : "question";
|
|
730
767
|
var claudeCode = {
|
|
731
768
|
name: NAME2,
|
|
@@ -744,11 +781,13 @@ var claudeCode = {
|
|
|
744
781
|
const event = input.argv[1] ?? "";
|
|
745
782
|
let s = {};
|
|
746
783
|
try {
|
|
747
|
-
|
|
784
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
785
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
748
786
|
} catch {
|
|
749
787
|
}
|
|
750
788
|
const id = s.session_id ?? s.sessionId;
|
|
751
789
|
if (!id) return null;
|
|
790
|
+
if (notClaudeCode(String(id), s)) return null;
|
|
752
791
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
753
792
|
if (typeof s.model === "string") session.model = s.model;
|
|
754
793
|
if (typeof s.agent_id === "string" && event.startsWith("Subagent") === false && s.agent_id)
|
|
@@ -867,12 +906,13 @@ var claudeCode = {
|
|
|
867
906
|
return null;
|
|
868
907
|
}
|
|
869
908
|
const hints = {};
|
|
909
|
+
if (event === "UserPromptSubmit" && typeof s.prompt === "string") hints.prompt = s.prompt;
|
|
870
910
|
const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
|
|
871
911
|
if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
|
|
872
912
|
if (event !== "PreToolUse" && event !== "PostToolUse") hints.claude_pid = claudePid(input.env);
|
|
873
|
-
const
|
|
874
|
-
if (typeof s.transcript_path === "string")
|
|
875
|
-
return
|
|
913
|
+
const r = { session, events, hints };
|
|
914
|
+
if (typeof s.transcript_path === "string") r.transcript = s.transcript_path;
|
|
915
|
+
return r;
|
|
876
916
|
},
|
|
877
917
|
readTranscript(file, fromLine) {
|
|
878
918
|
let text = "";
|
|
@@ -892,7 +932,7 @@ var claudeCode = {
|
|
|
892
932
|
return out;
|
|
893
933
|
},
|
|
894
934
|
backfill(sinceMs, env = process.env) {
|
|
895
|
-
const
|
|
935
|
+
const rows2 = [];
|
|
896
936
|
const files = [];
|
|
897
937
|
for (const d of claudeDirs(env))
|
|
898
938
|
files.push(...recentFiles(path4.join(d, "projects"), 1, sinceMs, (n) => /^[0-9a-f-]{36}\.jsonl$/.test(n)));
|
|
@@ -904,19 +944,23 @@ var claudeCode = {
|
|
|
904
944
|
if (!facts.started_at || !facts.last_at) continue;
|
|
905
945
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
906
946
|
const cwd = rest.cwd;
|
|
907
|
-
|
|
947
|
+
rows2.push({
|
|
908
948
|
session: { id, ...rest, ...gitFacts(cwd), ...rest.branch ? { branch: rest.branch } : {} },
|
|
909
949
|
started_at,
|
|
910
950
|
last_at
|
|
911
951
|
});
|
|
912
952
|
}
|
|
913
|
-
return
|
|
953
|
+
return rows2;
|
|
914
954
|
}
|
|
915
955
|
};
|
|
916
956
|
function prune2(o) {
|
|
917
957
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
918
958
|
return o;
|
|
919
959
|
}
|
|
960
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
961
|
+
function notClaudeCode(id, s) {
|
|
962
|
+
return typeof s.transcript_path !== "string" && !UUID.test(id);
|
|
963
|
+
}
|
|
920
964
|
function pickSource(v) {
|
|
921
965
|
return typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
922
966
|
}
|
|
@@ -943,13 +987,13 @@ var NAME_RANK = {
|
|
|
943
987
|
};
|
|
944
988
|
function registryEntry(j) {
|
|
945
989
|
if (!j || typeof j !== "object") return null;
|
|
946
|
-
const
|
|
947
|
-
const source =
|
|
990
|
+
const r = j;
|
|
991
|
+
const source = r.nameSource ? NAME_RANK[r.nameSource] : void 0;
|
|
948
992
|
return {
|
|
949
|
-
sessionId:
|
|
950
|
-
name: source && typeof
|
|
993
|
+
sessionId: r.sessionId ? String(r.sessionId) : null,
|
|
994
|
+
name: source && typeof r.name === "string" && r.name.trim() ? r.name.trim() : null,
|
|
951
995
|
name_source: source ?? null,
|
|
952
|
-
bridge: typeof
|
|
996
|
+
bridge: typeof r.bridgeSessionId === "string" ? r.bridgeSessionId : null
|
|
953
997
|
};
|
|
954
998
|
}
|
|
955
999
|
var liveCache = /* @__PURE__ */ new Map();
|
|
@@ -1022,23 +1066,27 @@ function parentOf() {
|
|
|
1022
1066
|
}
|
|
1023
1067
|
return (pid) => table.get(pid) || 0;
|
|
1024
1068
|
}
|
|
1025
|
-
var
|
|
1026
|
-
function
|
|
1027
|
-
const hit = accountCache.get(dir);
|
|
1028
|
-
if (hit !== void 0) return hit;
|
|
1069
|
+
var EMAIL_RE = /^[^@\s]{1,64}@[^@\s]{1,189}$/;
|
|
1070
|
+
function claudeLogin(dir, home = homeDir()) {
|
|
1029
1071
|
const files = [path4.join(dir, ".claude.json")];
|
|
1030
|
-
if (path4.resolve(dir) === path4.join(
|
|
1031
|
-
let acct = null;
|
|
1072
|
+
if (path4.resolve(dir) === path4.join(home, ".claude")) files.unshift(path4.join(home, ".claude.json"));
|
|
1032
1073
|
for (const f of files) {
|
|
1033
1074
|
try {
|
|
1034
1075
|
const a = JSON.parse(readFileSync3(f, "utf8")).oauthAccount;
|
|
1035
|
-
if (a?.accountUuid) {
|
|
1036
|
-
|
|
1037
|
-
|
|
1076
|
+
if (typeof a?.accountUuid === "string" && a.accountUuid) {
|
|
1077
|
+
const email = typeof a.emailAddress === "string" && EMAIL_RE.test(a.emailAddress) ? a.emailAddress : void 0;
|
|
1078
|
+
return { id: a.accountUuid.slice(0, 128), ...email ? { email } : {} };
|
|
1038
1079
|
}
|
|
1039
1080
|
} catch {
|
|
1040
1081
|
}
|
|
1041
1082
|
}
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
var accountCache = /* @__PURE__ */ new Map();
|
|
1086
|
+
function claudeAccount(dir) {
|
|
1087
|
+
const hit = accountCache.get(dir);
|
|
1088
|
+
if (hit !== void 0) return hit;
|
|
1089
|
+
const acct = claudeLogin(dir)?.id ?? null;
|
|
1042
1090
|
accountCache.set(dir, acct);
|
|
1043
1091
|
return acct;
|
|
1044
1092
|
}
|
|
@@ -1258,18 +1306,13 @@ function parseArgv(raw) {
|
|
|
1258
1306
|
function readNotify(text) {
|
|
1259
1307
|
const { head, tail, at } = split(text);
|
|
1260
1308
|
const m = NOTIFY_LINE.exec(head);
|
|
1261
|
-
if (m)
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
index: m.index,
|
|
1266
|
-
length: m[0].length,
|
|
1267
|
-
misplaced: false,
|
|
1268
|
-
other: !OURS2.test(m[1] ?? "")
|
|
1269
|
-
};
|
|
1309
|
+
if (m) {
|
|
1310
|
+
const raw = m[1];
|
|
1311
|
+
return { raw, argv: parseArgv(raw), index: m.index, length: m[0].length, misplaced: false, other: !OURS2.test(raw) };
|
|
1312
|
+
}
|
|
1270
1313
|
const k = NOTIFY_KEY.exec(head);
|
|
1271
1314
|
if (k) {
|
|
1272
|
-
const rest = head.slice(k.index).split("\n")[0]
|
|
1315
|
+
const rest = head.slice(k.index).split("\n")[0];
|
|
1273
1316
|
return {
|
|
1274
1317
|
raw: rest.replace(NOTIFY_KEY, "").trim(),
|
|
1275
1318
|
argv: null,
|
|
@@ -1280,15 +1323,11 @@ function readNotify(text) {
|
|
|
1280
1323
|
};
|
|
1281
1324
|
}
|
|
1282
1325
|
const t = NOTIFY_LINE.exec(tail);
|
|
1283
|
-
if (t
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
argv: parseArgv(t[
|
|
1287
|
-
|
|
1288
|
-
length: t[0].length,
|
|
1289
|
-
misplaced: true,
|
|
1290
|
-
other: false
|
|
1291
|
-
};
|
|
1326
|
+
if (t) {
|
|
1327
|
+
const raw = t[1];
|
|
1328
|
+
if (OURS2.test(raw))
|
|
1329
|
+
return { raw, argv: parseArgv(raw), index: at + t.index, length: t[0].length, misplaced: true, other: false };
|
|
1330
|
+
}
|
|
1292
1331
|
return null;
|
|
1293
1332
|
}
|
|
1294
1333
|
function dropLine(text, index, length) {
|
|
@@ -1307,9 +1346,7 @@ function withNotify(text, line) {
|
|
|
1307
1346
|
if (n?.misplaced) return insertTopLevel(dropLine(text, n.index, n.length), line);
|
|
1308
1347
|
return insertTopLevel(text, line);
|
|
1309
1348
|
}
|
|
1310
|
-
function replaceNotify(text, line) {
|
|
1311
|
-
const n = readNotify(text);
|
|
1312
|
-
if (!n || n.misplaced || !n.length) return null;
|
|
1349
|
+
function replaceNotify(text, n, line) {
|
|
1313
1350
|
return text.slice(0, n.index) + line + text.slice(n.index + n.length);
|
|
1314
1351
|
}
|
|
1315
1352
|
function withoutNotify(text) {
|
|
@@ -1350,12 +1387,12 @@ var CODEX_EVENTS = [
|
|
|
1350
1387
|
];
|
|
1351
1388
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1352
1389
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1353
|
-
var codexHome = (env) => env.CODEX_HOME || path5.join(
|
|
1390
|
+
var codexHome = (env) => env.CODEX_HOME || path5.join(homeDir(), ".codex");
|
|
1354
1391
|
var hooksFile2 = (env) => path5.join(codexHome(env), "hooks.json");
|
|
1355
1392
|
var configFile = (env) => path5.join(codexHome(env), "config.toml");
|
|
1356
1393
|
var sessionsDir = (env) => path5.join(codexHome(env), "sessions");
|
|
1357
1394
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1358
|
-
var endReason2 = (
|
|
1395
|
+
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
1359
1396
|
function installNotify(cmd, env) {
|
|
1360
1397
|
const file = configFile(env);
|
|
1361
1398
|
let text = "";
|
|
@@ -1388,11 +1425,8 @@ function installNotify(cmd, env) {
|
|
|
1388
1425
|
return { file, changed: false, note: "notify current (after Codex Computer Use)" };
|
|
1389
1426
|
}
|
|
1390
1427
|
if (!prev || OURS2.test(JSON.stringify(prev))) {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
write(next);
|
|
1394
|
-
return { file, changed: true, note: "chained after Codex Computer Use (--previous-notify)" };
|
|
1395
|
-
}
|
|
1428
|
+
write(replaceNotify(text, n, `notify = ${withPreviousRaw(n.raw, argv)}`));
|
|
1429
|
+
return { file, changed: true, note: "chained after Codex Computer Use (--previous-notify)" };
|
|
1396
1430
|
}
|
|
1397
1431
|
}
|
|
1398
1432
|
return {
|
|
@@ -1421,8 +1455,7 @@ function uninstallNotify(env) {
|
|
|
1421
1455
|
}
|
|
1422
1456
|
const prev = previousNotify(n.argv);
|
|
1423
1457
|
if (isComputerUse(n.argv) && prev && OURS2.test(JSON.stringify(prev))) {
|
|
1424
|
-
|
|
1425
|
-
if (next) writeFileSync3(file, next);
|
|
1458
|
+
writeFileSync3(file, replaceNotify(text, n, `notify = ${stripPreviousRaw(n.raw)}`));
|
|
1426
1459
|
return { file, changed: true };
|
|
1427
1460
|
}
|
|
1428
1461
|
return { file, changed: false };
|
|
@@ -1452,6 +1485,13 @@ function findRollout(threadId, env = process.env) {
|
|
|
1452
1485
|
walk2(sessionsDir(env), 0);
|
|
1453
1486
|
return best;
|
|
1454
1487
|
}
|
|
1488
|
+
function rolloutMeta(file) {
|
|
1489
|
+
return scanLines(file, (line) => parseLines([line])[0] ?? {});
|
|
1490
|
+
}
|
|
1491
|
+
function ofAnotherThread(file, id) {
|
|
1492
|
+
const m = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl(?:\.zst)?$/i.exec(file);
|
|
1493
|
+
return !!m?.[1] && m[1].toLowerCase() !== id.toLowerCase();
|
|
1494
|
+
}
|
|
1455
1495
|
var codex = {
|
|
1456
1496
|
name: NAME3,
|
|
1457
1497
|
events: CODEX_EVENTS,
|
|
@@ -1494,12 +1534,15 @@ var codex = {
|
|
|
1494
1534
|
if (event === "notify") {
|
|
1495
1535
|
const raw = input.argv.find((a) => a.startsWith("{")) ?? input.stdin;
|
|
1496
1536
|
try {
|
|
1497
|
-
|
|
1537
|
+
const j = raw ? JSON.parse(raw) : {};
|
|
1538
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
1498
1539
|
} catch {
|
|
1499
1540
|
}
|
|
1500
1541
|
const id2 = s["thread-id"] ?? s.thread_id ?? s["session-id"] ?? s.session_id;
|
|
1501
1542
|
if (!id2) return null;
|
|
1502
|
-
const
|
|
1543
|
+
const file = findRollout(String(id2), input.env);
|
|
1544
|
+
if (!file || codexSubagent(rolloutMeta(file))) return null;
|
|
1545
|
+
const r2 = {
|
|
1503
1546
|
session: { id: String(id2), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd },
|
|
1504
1547
|
events: [
|
|
1505
1548
|
{
|
|
@@ -1508,18 +1551,19 @@ var codex = {
|
|
|
1508
1551
|
harnessEvent: "notify"
|
|
1509
1552
|
}
|
|
1510
1553
|
],
|
|
1511
|
-
hints: {}
|
|
1554
|
+
hints: {},
|
|
1555
|
+
transcript: file
|
|
1512
1556
|
};
|
|
1513
|
-
|
|
1514
|
-
if (file) r3.transcript = file;
|
|
1515
|
-
return r3;
|
|
1557
|
+
return r2;
|
|
1516
1558
|
}
|
|
1517
1559
|
try {
|
|
1518
|
-
|
|
1560
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
1561
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
1519
1562
|
} catch {
|
|
1520
1563
|
}
|
|
1521
1564
|
const id = s.session_id;
|
|
1522
1565
|
if (!id) return null;
|
|
1566
|
+
if (s.transcript_path === null) return null;
|
|
1523
1567
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
1524
1568
|
if (typeof s.model === "string") session.model = s.model;
|
|
1525
1569
|
const turn_id = typeof s.turn_id === "string" ? s.turn_id : void 0;
|
|
@@ -1590,9 +1634,10 @@ var codex = {
|
|
|
1590
1634
|
default:
|
|
1591
1635
|
return null;
|
|
1592
1636
|
}
|
|
1593
|
-
const
|
|
1594
|
-
if (typeof s.transcript_path === "string"
|
|
1595
|
-
|
|
1637
|
+
const r = { session, events, hints: {} };
|
|
1638
|
+
if (typeof s.transcript_path === "string" && !ofAnotherThread(s.transcript_path, String(id)))
|
|
1639
|
+
r.transcript = s.transcript_path;
|
|
1640
|
+
return r;
|
|
1596
1641
|
},
|
|
1597
1642
|
readTranscript(file, fromLine) {
|
|
1598
1643
|
let text = "";
|
|
@@ -1609,16 +1654,17 @@ var codex = {
|
|
|
1609
1654
|
return out;
|
|
1610
1655
|
},
|
|
1611
1656
|
backfill(sinceMs, env = process.env) {
|
|
1612
|
-
const
|
|
1657
|
+
const rows2 = [];
|
|
1613
1658
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
1614
1659
|
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path5.basename(f));
|
|
1615
1660
|
if (!m?.[1]) continue;
|
|
1661
|
+
if (codexSubagent(rolloutMeta(f))) continue;
|
|
1616
1662
|
const facts = codexFacts(f);
|
|
1617
1663
|
if (!facts.started_at || !facts.last_at) continue;
|
|
1618
1664
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
1619
|
-
|
|
1665
|
+
rows2.push({ session: { id: m[1], ...rest }, started_at, last_at });
|
|
1620
1666
|
}
|
|
1621
|
-
return
|
|
1667
|
+
return rows2;
|
|
1622
1668
|
}
|
|
1623
1669
|
};
|
|
1624
1670
|
function prune3(o) {
|
|
@@ -1627,10 +1673,11 @@ function prune3(o) {
|
|
|
1627
1673
|
}
|
|
1628
1674
|
function codexFacts(file) {
|
|
1629
1675
|
const { head, tail } = ends(file, 128 * 1024, 64 * 1024);
|
|
1630
|
-
|
|
1631
|
-
|
|
1676
|
+
let H = parseLines(head);
|
|
1677
|
+
let T = parseLines(tail);
|
|
1632
1678
|
const out = {};
|
|
1633
1679
|
const meta = H.find((j) => j.type === "session_meta" && j.payload);
|
|
1680
|
+
const boundary = codexReplayBoundary(meta);
|
|
1634
1681
|
if (meta?.payload) {
|
|
1635
1682
|
const p = meta.payload;
|
|
1636
1683
|
if (p.cwd) out.cwd = p.cwd;
|
|
@@ -1638,6 +1685,13 @@ function codexFacts(file) {
|
|
|
1638
1685
|
if (p.git?.branch) out.branch = p.git.branch;
|
|
1639
1686
|
if (p.git?.repository_url) out.repo = String(p.git.repository_url).replace(/\/\/[^/@]*@/, "//");
|
|
1640
1687
|
}
|
|
1688
|
+
if (boundary) {
|
|
1689
|
+
const h = H.findIndex(boundary);
|
|
1690
|
+
const t = T.findIndex(boundary);
|
|
1691
|
+
H = h < 0 ? [] : H.slice(h);
|
|
1692
|
+
const found = h >= 0 || t >= 0 || scanLines(file, (line) => boundary(parseLines([line])[0] ?? null) ? true : null, Number.POSITIVE_INFINITY);
|
|
1693
|
+
T = t >= 0 ? T.slice(t) : found ? T : [];
|
|
1694
|
+
}
|
|
1641
1695
|
const ctx = H.concat(T).reverse().find((j) => j.type === "turn_context" && j.payload?.model);
|
|
1642
1696
|
if (ctx?.payload?.model) out.model = ctx.payload.model;
|
|
1643
1697
|
for (const j of H) {
|
|
@@ -1645,12 +1699,12 @@ function codexFacts(file) {
|
|
|
1645
1699
|
if (j.type !== "response_item" || !p || p.type !== "message" || p.role !== "user") continue;
|
|
1646
1700
|
const text = (p.content ?? []).filter((b) => b && b.type === "input_text" && typeof b.text === "string").map((b) => b.text).join("\n").trim();
|
|
1647
1701
|
if (!text || codexInjected(text)) continue;
|
|
1648
|
-
const line = text.split("\n")
|
|
1702
|
+
const line = text.split("\n")[0];
|
|
1649
1703
|
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
1650
1704
|
out.title_source = "first-ask";
|
|
1651
1705
|
break;
|
|
1652
1706
|
}
|
|
1653
|
-
const times = H.concat(T).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
1707
|
+
const times = (boundary && meta ? [meta, ...H, ...T] : H.concat(T)).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
1654
1708
|
if (times.length) {
|
|
1655
1709
|
out.started_at = Math.min(...times);
|
|
1656
1710
|
out.last_at = Math.max(...times);
|
|
@@ -1658,11 +1712,432 @@ function codexFacts(file) {
|
|
|
1658
1712
|
return out;
|
|
1659
1713
|
}
|
|
1660
1714
|
|
|
1661
|
-
// ../core/src/adapters/
|
|
1662
|
-
import {
|
|
1663
|
-
import { existsSync as
|
|
1715
|
+
// ../core/src/adapters/devin.ts
|
|
1716
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1717
|
+
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
1664
1718
|
import path6 from "node:path";
|
|
1665
|
-
|
|
1719
|
+
|
|
1720
|
+
// ../core/src/readers/sqlite.ts
|
|
1721
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
1722
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
1723
|
+
import { createRequire } from "node:module";
|
|
1724
|
+
function quiet(fn) {
|
|
1725
|
+
const original = process.emitWarning;
|
|
1726
|
+
try {
|
|
1727
|
+
process.emitWarning = ((warning, ...rest) => {
|
|
1728
|
+
const kind = typeof rest[0] === "string" ? rest[0] : rest[0]?.type;
|
|
1729
|
+
if (kind === "ExperimentalWarning" && /sqlite/i.test(String(warning))) return;
|
|
1730
|
+
original.call(process, warning, ...rest);
|
|
1731
|
+
});
|
|
1732
|
+
return fn();
|
|
1733
|
+
} finally {
|
|
1734
|
+
process.emitWarning = original;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
var ctorCache;
|
|
1738
|
+
function dbCtor() {
|
|
1739
|
+
if (ctorCache !== void 0) return ctorCache;
|
|
1740
|
+
ctorCache = null;
|
|
1741
|
+
try {
|
|
1742
|
+
const req = createRequire(import.meta.url);
|
|
1743
|
+
const m = quiet(() => req("node:sqlite"));
|
|
1744
|
+
if (typeof m?.DatabaseSync === "function") ctorCache = m.DatabaseSync;
|
|
1745
|
+
} catch {
|
|
1746
|
+
}
|
|
1747
|
+
return ctorCache;
|
|
1748
|
+
}
|
|
1749
|
+
function plain(row) {
|
|
1750
|
+
if (!row || typeof row !== "object") return null;
|
|
1751
|
+
const out = {};
|
|
1752
|
+
for (const [k, v] of Object.entries(row)) {
|
|
1753
|
+
if (typeof v === "bigint") out[k] = Number(v);
|
|
1754
|
+
else if (v instanceof Uint8Array) out[k] = null;
|
|
1755
|
+
else out[k] = v;
|
|
1756
|
+
}
|
|
1757
|
+
return out;
|
|
1758
|
+
}
|
|
1759
|
+
var rows = (v) => {
|
|
1760
|
+
if (!Array.isArray(v)) return null;
|
|
1761
|
+
const out = [];
|
|
1762
|
+
for (const r of v) {
|
|
1763
|
+
const p = plain(r);
|
|
1764
|
+
if (p) out.push(p);
|
|
1765
|
+
}
|
|
1766
|
+
return out;
|
|
1767
|
+
};
|
|
1768
|
+
function viaNode(file, sql, params, o) {
|
|
1769
|
+
const C = dbCtor();
|
|
1770
|
+
if (!C) return null;
|
|
1771
|
+
let db;
|
|
1772
|
+
try {
|
|
1773
|
+
db = quiet(() => new C(file, { readOnly: true, timeout: o.timeoutMs }));
|
|
1774
|
+
} catch {
|
|
1775
|
+
return null;
|
|
1776
|
+
}
|
|
1777
|
+
try {
|
|
1778
|
+
try {
|
|
1779
|
+
db.exec(`PRAGMA busy_timeout = ${Math.round(o.timeoutMs)}`);
|
|
1780
|
+
} catch {
|
|
1781
|
+
}
|
|
1782
|
+
return quiet(() => rows(db.prepare(sql).all(...params)));
|
|
1783
|
+
} catch {
|
|
1784
|
+
return null;
|
|
1785
|
+
} finally {
|
|
1786
|
+
try {
|
|
1787
|
+
db.close();
|
|
1788
|
+
} catch {
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
var CHILD = `const { DatabaseSync } = require("node:sqlite");
|
|
1793
|
+
const [, file, sql, params, ms] = process.argv;
|
|
1794
|
+
const db = new DatabaseSync(file, { readOnly: true, timeout: Number(ms) });
|
|
1795
|
+
try { db.exec("PRAGMA busy_timeout = " + Number(ms)); } catch {}
|
|
1796
|
+
const out = db.prepare(sql).all(...JSON.parse(params));
|
|
1797
|
+
db.close();
|
|
1798
|
+
process.stdout.write(JSON.stringify(out));`;
|
|
1799
|
+
function viaChild(file, sql, params, o) {
|
|
1800
|
+
try {
|
|
1801
|
+
const out = execFileSync3(
|
|
1802
|
+
process.execPath,
|
|
1803
|
+
["--experimental-sqlite", "--no-warnings", "-e", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],
|
|
1804
|
+
{
|
|
1805
|
+
encoding: "utf8",
|
|
1806
|
+
timeout: o.timeoutMs + 3e3,
|
|
1807
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1808
|
+
maxBuffer: o.maxBuffer,
|
|
1809
|
+
windowsHide: true
|
|
1810
|
+
}
|
|
1811
|
+
);
|
|
1812
|
+
return rows(JSON.parse(out || "null"));
|
|
1813
|
+
} catch {
|
|
1814
|
+
return null;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
var SAFE = /^[A-Za-z0-9 ._:@/+-]{0,256}$/;
|
|
1818
|
+
function inlineParams(sql, params) {
|
|
1819
|
+
if (sql.includes("'")) return null;
|
|
1820
|
+
const holes = sql.split("?").length - 1;
|
|
1821
|
+
if (holes !== params.length) return null;
|
|
1822
|
+
let i = 0;
|
|
1823
|
+
let bad = false;
|
|
1824
|
+
const out = sql.replace(/\?/g, () => {
|
|
1825
|
+
const p = params[i++];
|
|
1826
|
+
if (typeof p === "number") {
|
|
1827
|
+
if (!Number.isFinite(p)) {
|
|
1828
|
+
bad = true;
|
|
1829
|
+
return "NULL";
|
|
1830
|
+
}
|
|
1831
|
+
return String(p);
|
|
1832
|
+
}
|
|
1833
|
+
if (typeof p !== "string" || !SAFE.test(p)) {
|
|
1834
|
+
bad = true;
|
|
1835
|
+
return "NULL";
|
|
1836
|
+
}
|
|
1837
|
+
return `'${p}'`;
|
|
1838
|
+
});
|
|
1839
|
+
return bad ? null : out;
|
|
1840
|
+
}
|
|
1841
|
+
function viaCli(file, sql, params, o) {
|
|
1842
|
+
const text = inlineParams(sql, params);
|
|
1843
|
+
if (!text) return null;
|
|
1844
|
+
try {
|
|
1845
|
+
const out = execFileSync3("sqlite3", ["-readonly", "-json", file, text], {
|
|
1846
|
+
encoding: "utf8",
|
|
1847
|
+
timeout: o.timeoutMs + 3e3,
|
|
1848
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1849
|
+
maxBuffer: o.maxBuffer,
|
|
1850
|
+
windowsHide: true
|
|
1851
|
+
}).trim();
|
|
1852
|
+
if (!out) return [];
|
|
1853
|
+
return rows(JSON.parse(out));
|
|
1854
|
+
} catch {
|
|
1855
|
+
return null;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
function querySqlite(file, sql, params = [], opts = {}) {
|
|
1859
|
+
const o = {
|
|
1860
|
+
timeoutMs: opts.timeoutMs ?? 2e3,
|
|
1861
|
+
engine: opts.engine ?? "auto",
|
|
1862
|
+
maxBuffer: opts.maxBuffer ?? 32 * 1024 * 1024
|
|
1863
|
+
};
|
|
1864
|
+
if (!file) return null;
|
|
1865
|
+
try {
|
|
1866
|
+
if (!existsSync5(file)) return null;
|
|
1867
|
+
} catch {
|
|
1868
|
+
return null;
|
|
1869
|
+
}
|
|
1870
|
+
const ways = o.engine === "auto" ? [viaNode, viaChild, viaCli] : o.engine === "node" ? [viaNode] : o.engine === "child" ? [viaChild] : [viaCli];
|
|
1871
|
+
for (const way of ways) {
|
|
1872
|
+
const r = way(file, sql, params, o);
|
|
1873
|
+
if (r) return r;
|
|
1874
|
+
}
|
|
1875
|
+
return null;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// ../core/src/adapters/devin.ts
|
|
1879
|
+
var NAME4 = "devin";
|
|
1880
|
+
var DEVIN_EVENTS = [
|
|
1881
|
+
"SessionStart",
|
|
1882
|
+
"UserPromptSubmit",
|
|
1883
|
+
"PreToolUse",
|
|
1884
|
+
"PostToolUse",
|
|
1885
|
+
"PermissionRequest",
|
|
1886
|
+
"Stop",
|
|
1887
|
+
"PostCompaction",
|
|
1888
|
+
"SessionEnd"
|
|
1889
|
+
];
|
|
1890
|
+
var OPTS3 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1891
|
+
var eventsFor3 = (lean) => lean ? DEVIN_EVENTS.filter((e) => e !== "PreToolUse") : [...DEVIN_EVENTS];
|
|
1892
|
+
var INSTALL_NOTE = "Devin drops a whole hooks file on one event name it doesn't know; `/hooks` in `devin` lists what loaded";
|
|
1893
|
+
var CLAUDE_NOTE = "Devin also reads ~/.claude/settings.json hooks (read_config_from.claude); a Claude-only event name there makes it ignore that file whole";
|
|
1894
|
+
var deadFileNote = (bad) => `Devin loads NO hooks from this file: "${bad}" is not one of its event names and it rejects the file whole \u2014 remove that key (\`/hooks\` in \`devin\` lists what loaded)`;
|
|
1895
|
+
function foreignEvent(file) {
|
|
1896
|
+
const hooks = readJson(file).hooks;
|
|
1897
|
+
if (!hooks || typeof hooks !== "object" || Array.isArray(hooks)) return void 0;
|
|
1898
|
+
const ok = DEVIN_EVENTS;
|
|
1899
|
+
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
1900
|
+
}
|
|
1901
|
+
var homeOf = (env) => env.HOME || homeDir();
|
|
1902
|
+
var appData = (env) => env.APPDATA || path6.join(homeOf(env), "AppData", "Roaming");
|
|
1903
|
+
var devinConfigDir = (env = process.env) => process.platform === "win32" ? path6.join(appData(env), "devin") : path6.join(env.XDG_CONFIG_HOME || path6.join(homeOf(env), ".config"), "devin");
|
|
1904
|
+
var configFile2 = (env) => path6.join(devinConfigDir(env), "config.json");
|
|
1905
|
+
var devinDataDir = (env = process.env) => process.platform === "win32" ? path6.join(appData(env), "devin", "cli") : path6.join(env.XDG_DATA_HOME || path6.join(homeOf(env), ".local", "share"), "devin", "cli");
|
|
1906
|
+
var sessionsDb = (env) => path6.join(devinDataDir(env), "sessions.db");
|
|
1907
|
+
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
1908
|
+
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1909
|
+
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
1910
|
+
var pickSource2 = (v) => typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
1911
|
+
function errText2(v) {
|
|
1912
|
+
if (typeof v === "string" && v) return v.slice(0, 2e3);
|
|
1913
|
+
if (v && typeof v === "object") {
|
|
1914
|
+
const e = v;
|
|
1915
|
+
if (typeof e.message === "string" && e.message) return e.message.slice(0, 2e3);
|
|
1916
|
+
if (typeof e.error === "string" && e.error) return e.error.slice(0, 2e3);
|
|
1917
|
+
}
|
|
1918
|
+
return void 0;
|
|
1919
|
+
}
|
|
1920
|
+
var cut = (s, n) => s.length > n ? `${s.slice(0, n).replace(/\s+\S*$/, "")}\u2026` : s;
|
|
1921
|
+
var devin = {
|
|
1922
|
+
name: NAME4,
|
|
1923
|
+
events: DEVIN_EVENTS,
|
|
1924
|
+
detect: (env = process.env) => existsSync6(devinConfigDir(env)) || existsSync6(devinDataDir(env)),
|
|
1925
|
+
configFiles: (env = process.env) => [configFile2(env)],
|
|
1926
|
+
install: (cmd, env = process.env, opts = {}) => {
|
|
1927
|
+
const file = configFile2(env);
|
|
1928
|
+
const r = installClaudeShaped(file, NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
1929
|
+
if (r.skipped) return [r];
|
|
1930
|
+
const bad = foreignEvent(file);
|
|
1931
|
+
return [{ ...r, note: bad ? deadFileNote(bad) : INSTALL_NOTE }];
|
|
1932
|
+
},
|
|
1933
|
+
uninstall: (env = process.env, opts = {}) => [
|
|
1934
|
+
uninstallClaudeShaped(configFile2(env), DEVIN_EVENTS, { created: opts.created?.includes(configFile2(env)) })
|
|
1935
|
+
],
|
|
1936
|
+
installed: (cmd, env = process.env, opts = {}) => {
|
|
1937
|
+
const file = configFile2(env);
|
|
1938
|
+
const state = installedClaudeShaped(file, NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
1939
|
+
const bad = state === "missing" ? void 0 : foreignEvent(file);
|
|
1940
|
+
return [{ file, state: bad ? "inactive" : state, note: bad ? deadFileNote(bad) : CLAUDE_NOTE }];
|
|
1941
|
+
},
|
|
1942
|
+
fromHook(input) {
|
|
1943
|
+
const event = input.argv[1] ?? "";
|
|
1944
|
+
let s = {};
|
|
1945
|
+
try {
|
|
1946
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
1947
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
1948
|
+
} catch {
|
|
1949
|
+
}
|
|
1950
|
+
const id = typeof s.session_id === "string" ? s.session_id : "";
|
|
1951
|
+
if (!id) return null;
|
|
1952
|
+
const session = { id, cwd: input.env.DEVIN_PROJECT_DIR ?? input.cwd };
|
|
1953
|
+
const turn_id = typeof s.prompt_id === "string" ? s.prompt_id : void 0;
|
|
1954
|
+
const tool = validTool4(s.tool_name) ? s.tool_name : void 0;
|
|
1955
|
+
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
1956
|
+
const response = s.tool_response && typeof s.tool_response === "object" && !Array.isArray(s.tool_response) ? s.tool_response : void 0;
|
|
1957
|
+
const permissionId = () => `perm-${createHash2("sha256").update(`${turn_id ?? ""}|${tool ?? ""}|${JSON.stringify(s.tool_input ?? null)}`).digest("hex").slice(0, 16)}`;
|
|
1958
|
+
const ev = (type, data) => ({ type, data: prune4(data), harnessEvent: event });
|
|
1959
|
+
const events = [];
|
|
1960
|
+
switch (event) {
|
|
1961
|
+
case "SessionStart":
|
|
1962
|
+
events.push(ev("session.started", { source: pickSource2(s.source) }));
|
|
1963
|
+
break;
|
|
1964
|
+
case "UserPromptSubmit":
|
|
1965
|
+
events.push(
|
|
1966
|
+
ev("turn.started", {
|
|
1967
|
+
turn_id,
|
|
1968
|
+
prompt_chars: typeof s.prompt === "string" ? [...s.prompt].length : void 0
|
|
1969
|
+
})
|
|
1970
|
+
);
|
|
1971
|
+
break;
|
|
1972
|
+
case "PreToolUse":
|
|
1973
|
+
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, input: s.tool_input }));
|
|
1974
|
+
break;
|
|
1975
|
+
case "PostToolUse":
|
|
1976
|
+
if (tool)
|
|
1977
|
+
events.push(
|
|
1978
|
+
ev("tool.ended", {
|
|
1979
|
+
tool,
|
|
1980
|
+
call_id,
|
|
1981
|
+
turn_id,
|
|
1982
|
+
// `ok` is required on tool.ended (PROTOCOL.md §events), so a response
|
|
1983
|
+
// that carries no success flag at all reads as "nothing said it failed".
|
|
1984
|
+
ok: response?.success !== false,
|
|
1985
|
+
error: errText2(response?.error),
|
|
1986
|
+
input: s.tool_input,
|
|
1987
|
+
output: response ? response.output : s.tool_response
|
|
1988
|
+
})
|
|
1989
|
+
);
|
|
1990
|
+
break;
|
|
1991
|
+
case "PermissionRequest":
|
|
1992
|
+
events.push(ev("attention.needed", { attention_id: call_id ?? permissionId(), kind: "permission", tool }));
|
|
1993
|
+
break;
|
|
1994
|
+
case "Stop":
|
|
1995
|
+
events.push(ev("turn.ended", { turn_id, reason: "stop" }));
|
|
1996
|
+
break;
|
|
1997
|
+
case "PostCompaction":
|
|
1998
|
+
events.push(ev("context.compacted", { trigger: "auto" }));
|
|
1999
|
+
break;
|
|
2000
|
+
case "SessionEnd":
|
|
2001
|
+
events.push(ev("session.ended", { reason: endReason3(s.reason) }));
|
|
2002
|
+
break;
|
|
2003
|
+
default:
|
|
2004
|
+
return null;
|
|
2005
|
+
}
|
|
2006
|
+
return {
|
|
2007
|
+
session,
|
|
2008
|
+
events,
|
|
2009
|
+
hints: {},
|
|
2010
|
+
transcript: transcriptRef(sessionsDb(input.env), id)
|
|
2011
|
+
};
|
|
2012
|
+
},
|
|
2013
|
+
readTranscript(file, fromLine) {
|
|
2014
|
+
const ref = splitRef(file);
|
|
2015
|
+
if (!ref) return { turns: [], line: fromLine };
|
|
2016
|
+
return readDevinChain(ref.db, ref.session, fromLine);
|
|
2017
|
+
},
|
|
2018
|
+
facts(session, transcript, _hints, _state, env = process.env) {
|
|
2019
|
+
const out = {};
|
|
2020
|
+
const version = devinVersion(env);
|
|
2021
|
+
if (version) out.harness_version = version;
|
|
2022
|
+
const account = devinAccount(env);
|
|
2023
|
+
if (account) out.account_id = account;
|
|
2024
|
+
const ref = splitRef(transcript) ?? { db: sessionsDb(env), session: session.id };
|
|
2025
|
+
if (!SESSION_ID.test(ref.session)) return out;
|
|
2026
|
+
const rows2 = querySqlite(ref.db, SESSION_SQL, [ref.session]);
|
|
2027
|
+
const row = rows2?.[0];
|
|
2028
|
+
if (!row) return out;
|
|
2029
|
+
Object.assign(out, sessionFacts(row));
|
|
2030
|
+
return out;
|
|
2031
|
+
},
|
|
2032
|
+
backfill(sinceMs, env = process.env) {
|
|
2033
|
+
const rows2 = querySqlite(sessionsDb(env), BACKFILL_SQL, ["text", Math.floor(sinceMs / 1e3)]);
|
|
2034
|
+
if (!rows2) return [];
|
|
2035
|
+
const out = [];
|
|
2036
|
+
for (const row of rows2) {
|
|
2037
|
+
const id = String(row.id);
|
|
2038
|
+
if (!SESSION_ID.test(id)) continue;
|
|
2039
|
+
const { started_at, last_at, ...rest } = sessionFacts(row);
|
|
2040
|
+
if (!started_at || !last_at) continue;
|
|
2041
|
+
out.push({ session: { id, ...rest }, started_at, last_at });
|
|
2042
|
+
}
|
|
2043
|
+
return out;
|
|
2044
|
+
}
|
|
2045
|
+
};
|
|
2046
|
+
function prune4(o) {
|
|
2047
|
+
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
2048
|
+
return o;
|
|
2049
|
+
}
|
|
2050
|
+
var transcriptRef = (db, session) => `${db}#${session}`;
|
|
2051
|
+
function splitRef(ref) {
|
|
2052
|
+
if (!ref) return null;
|
|
2053
|
+
const at = ref.lastIndexOf("#");
|
|
2054
|
+
if (at <= 0) return null;
|
|
2055
|
+
const db = ref.slice(0, at);
|
|
2056
|
+
const session = ref.slice(at + 1);
|
|
2057
|
+
return db && SESSION_ID.test(session) ? { db, session } : null;
|
|
2058
|
+
}
|
|
2059
|
+
var CHAIN_LIMIT = 4e3;
|
|
2060
|
+
var CHAIN_SQL = `WITH RECURSIVE chain(node_id, parent_node_id, chat_message, created_at) AS (
|
|
2061
|
+
SELECT node_id, parent_node_id, chat_message, created_at FROM message_nodes
|
|
2062
|
+
WHERE session_id = ? AND node_id = (SELECT main_chain_id FROM sessions WHERE id = ?)
|
|
2063
|
+
UNION ALL
|
|
2064
|
+
SELECT m.node_id, m.parent_node_id, m.chat_message, m.created_at FROM message_nodes m
|
|
2065
|
+
JOIN chain c ON m.node_id = c.parent_node_id AND m.session_id = ? AND m.node_id >= ?
|
|
2066
|
+
)
|
|
2067
|
+
SELECT node_id, chat_message, created_at FROM chain ORDER BY node_id DESC LIMIT ${CHAIN_LIMIT}`;
|
|
2068
|
+
var SESSION_FIELDS = `s.model, s.working_directory, s.created_at, s.last_activity_at,
|
|
2069
|
+
(SELECT p.content FROM prompt_history p WHERE p.session_id = s.id AND p.is_shell = 0
|
|
2070
|
+
ORDER BY p.timestamp ASC, p.id ASC LIMIT 1) AS first_ask`;
|
|
2071
|
+
var SESSION_SQL = `SELECT ${SESSION_FIELDS} FROM sessions s WHERE s.id = ? LIMIT 1`;
|
|
2072
|
+
var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
2073
|
+
WHERE s.hidden = 0 AND typeof(s.id) = ? AND s.last_activity_at >= ?
|
|
2074
|
+
ORDER BY s.last_activity_at DESC LIMIT 500`;
|
|
2075
|
+
function sessionFacts(row) {
|
|
2076
|
+
const out = {};
|
|
2077
|
+
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
2078
|
+
if (typeof row.working_directory === "string" && path6.isAbsolute(row.working_directory))
|
|
2079
|
+
out.cwd = row.working_directory;
|
|
2080
|
+
const started = Number(row.created_at);
|
|
2081
|
+
const last = Number(row.last_activity_at);
|
|
2082
|
+
if (Number.isFinite(started) && started > 0) out.started_at = Math.round(started * 1e3);
|
|
2083
|
+
if (Number.isFinite(last) && last > 0) out.last_at = Math.round(last * 1e3);
|
|
2084
|
+
if (typeof row.first_ask === "string") {
|
|
2085
|
+
const line = row.first_ask.split("\n").find((l) => l.trim()) ?? "";
|
|
2086
|
+
if (line.trim()) {
|
|
2087
|
+
out.title = cut(line.trim(), 80);
|
|
2088
|
+
out.title_source = "first-ask";
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
return out;
|
|
2092
|
+
}
|
|
2093
|
+
function readDevinChain(db, session, fromLine) {
|
|
2094
|
+
if (!SESSION_ID.test(session)) return { turns: [], line: fromLine };
|
|
2095
|
+
const floor = Number.isFinite(fromLine) && fromLine > 0 ? Math.floor(fromLine) : 0;
|
|
2096
|
+
const rows2 = querySqlite(db, CHAIN_SQL, [session, session, session, floor]);
|
|
2097
|
+
if (!rows2?.length) return { turns: [], line: fromLine };
|
|
2098
|
+
const msgs = [];
|
|
2099
|
+
let end = fromLine;
|
|
2100
|
+
for (const row of rows2) {
|
|
2101
|
+
const node = Number(row.node_id);
|
|
2102
|
+
if (!Number.isFinite(node)) continue;
|
|
2103
|
+
if (node + 1 > end) end = node + 1;
|
|
2104
|
+
if (node < fromLine) continue;
|
|
2105
|
+
let j;
|
|
2106
|
+
try {
|
|
2107
|
+
j = JSON.parse(String(row.chat_message ?? ""));
|
|
2108
|
+
} catch {
|
|
2109
|
+
continue;
|
|
2110
|
+
}
|
|
2111
|
+
const text = typeof j.content === "string" ? j.content.trim() : "";
|
|
2112
|
+
if (!text) continue;
|
|
2113
|
+
const secs = Number(row.created_at);
|
|
2114
|
+
const at = Date.parse(String(j.metadata?.created_at ?? "")) || (Number.isFinite(secs) && secs > 0 ? Math.round(secs * 1e3) : 0) || Date.now();
|
|
2115
|
+
if (j.role === "user" && j.metadata?.is_user_input === true) msgs.push({ role: "user", text, at, line: node });
|
|
2116
|
+
else if (j.role === "assistant") msgs.push({ role: "assistant", text, at, line: node });
|
|
2117
|
+
}
|
|
2118
|
+
msgs.sort((a, b) => a.line - b.line);
|
|
2119
|
+
return pairTurns(msgs, end);
|
|
2120
|
+
}
|
|
2121
|
+
function devinVersion(env = process.env) {
|
|
2122
|
+
try {
|
|
2123
|
+
const target = readlinkSync(path6.join(devinDataDir(env), "_versions", "current"));
|
|
2124
|
+
const name = path6.basename(target);
|
|
2125
|
+
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
2126
|
+
} catch {
|
|
2127
|
+
return void 0;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
function devinAccount(env = process.env) {
|
|
2131
|
+
const d = readJson(configFile2(env)).devin;
|
|
2132
|
+
const id = d && typeof d === "object" ? d.org_id : void 0;
|
|
2133
|
+
return typeof id === "string" && /^[A-Za-z0-9_.:-]{1,128}$/.test(id) ? id : void 0;
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
// ../core/src/adapters/gemini-cli.ts
|
|
2137
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2138
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
|
|
2139
|
+
import path7 from "node:path";
|
|
2140
|
+
var NAME5 = "gemini-cli";
|
|
1666
2141
|
var GEMINI_EVENTS = [
|
|
1667
2142
|
"SessionStart",
|
|
1668
2143
|
"BeforeAgent",
|
|
@@ -1673,11 +2148,11 @@ var GEMINI_EVENTS = [
|
|
|
1673
2148
|
"PreCompress",
|
|
1674
2149
|
"SessionEnd"
|
|
1675
2150
|
];
|
|
1676
|
-
var
|
|
1677
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
1678
|
-
var settings = (env) =>
|
|
1679
|
-
var
|
|
1680
|
-
var
|
|
2151
|
+
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
2152
|
+
var geminiDir = (env) => env.GEMINI_CLI_HOME || path7.join(homeDir(), ".gemini");
|
|
2153
|
+
var settings = (env) => path7.join(geminiDir(env), "settings.json");
|
|
2154
|
+
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2155
|
+
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
1681
2156
|
function geminiHooksActive(settingsFile) {
|
|
1682
2157
|
let tools;
|
|
1683
2158
|
try {
|
|
@@ -1689,7 +2164,7 @@ function geminiHooksActive(settingsFile) {
|
|
|
1689
2164
|
return { active: false, note: "inactive: tools.enableHooks is false in settings.json" };
|
|
1690
2165
|
let version = "";
|
|
1691
2166
|
try {
|
|
1692
|
-
version =
|
|
2167
|
+
version = execFileSync4("gemini", ["--version"], {
|
|
1693
2168
|
encoding: "utf8",
|
|
1694
2169
|
timeout: 4e3,
|
|
1695
2170
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -1708,21 +2183,21 @@ function geminiHooksActive(settingsFile) {
|
|
|
1708
2183
|
};
|
|
1709
2184
|
}
|
|
1710
2185
|
var geminiCli = {
|
|
1711
|
-
name:
|
|
2186
|
+
name: NAME5,
|
|
1712
2187
|
events: GEMINI_EVENTS,
|
|
1713
2188
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
1714
|
-
detect: (env = process.env) =>
|
|
2189
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path7.join(geminiDir(env), "tmp")),
|
|
1715
2190
|
configFiles: (env = process.env) => [settings(env)],
|
|
1716
2191
|
install: (cmd, env = process.env, opts = {}) => {
|
|
1717
|
-
const
|
|
2192
|
+
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
1718
2193
|
const a = geminiHooksActive(settings(env));
|
|
1719
|
-
return [a.note && !
|
|
2194
|
+
return [a.note && !r.skipped ? { ...r, note: [r.note, a.note].filter(Boolean).join("; ") } : r];
|
|
1720
2195
|
},
|
|
1721
2196
|
uninstall: (env = process.env, opts = {}) => [
|
|
1722
2197
|
uninstallClaudeShaped(settings(env), GEMINI_EVENTS, { created: opts.created?.includes(settings(env)) })
|
|
1723
2198
|
],
|
|
1724
2199
|
installed: (cmd, env = process.env, opts = {}) => {
|
|
1725
|
-
const state = installedClaudeShaped(settings(env),
|
|
2200
|
+
const state = installedClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
1726
2201
|
const a = geminiHooksActive(settings(env));
|
|
1727
2202
|
return [
|
|
1728
2203
|
{
|
|
@@ -1736,14 +2211,15 @@ var geminiCli = {
|
|
|
1736
2211
|
const event = input.argv[1] ?? "";
|
|
1737
2212
|
let s = {};
|
|
1738
2213
|
try {
|
|
1739
|
-
|
|
2214
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
2215
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
1740
2216
|
} catch {
|
|
1741
2217
|
}
|
|
1742
2218
|
const id = s.session_id;
|
|
1743
2219
|
if (!id) return null;
|
|
1744
2220
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
1745
|
-
const tool =
|
|
1746
|
-
const ev = (type, data) => ({ type, data:
|
|
2221
|
+
const tool = validTool5(s.tool_name) ? s.tool_name : void 0;
|
|
2222
|
+
const ev = (type, data) => ({ type, data: prune5(data), harnessEvent: event });
|
|
1747
2223
|
const events = [];
|
|
1748
2224
|
switch (event) {
|
|
1749
2225
|
case "SessionStart":
|
|
@@ -1801,9 +2277,9 @@ var geminiCli = {
|
|
|
1801
2277
|
default:
|
|
1802
2278
|
return null;
|
|
1803
2279
|
}
|
|
1804
|
-
const
|
|
1805
|
-
if (typeof s.transcript_path === "string")
|
|
1806
|
-
return
|
|
2280
|
+
const r = { session, events, hints: {} };
|
|
2281
|
+
if (typeof s.transcript_path === "string") r.transcript = s.transcript_path;
|
|
2282
|
+
return r;
|
|
1807
2283
|
},
|
|
1808
2284
|
readTranscript(file, fromLine) {
|
|
1809
2285
|
let text = "";
|
|
@@ -1824,7 +2300,7 @@ var geminiCli = {
|
|
|
1824
2300
|
const raw = first ? first.content ?? first.message ?? first.text : void 0;
|
|
1825
2301
|
const text = typeof raw === "string" ? raw.trim() : "";
|
|
1826
2302
|
if (text) {
|
|
1827
|
-
const line = text.split("\n")
|
|
2303
|
+
const line = text.split("\n")[0];
|
|
1828
2304
|
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
1829
2305
|
out.title_source = "first-ask";
|
|
1830
2306
|
}
|
|
@@ -1838,13 +2314,13 @@ var geminiCli = {
|
|
|
1838
2314
|
backfill: () => []
|
|
1839
2315
|
// chat logs are keyed by a project hash, not a session id we can name; the first hook of a resumed session reports it
|
|
1840
2316
|
};
|
|
1841
|
-
function
|
|
2317
|
+
function prune5(o) {
|
|
1842
2318
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
1843
2319
|
return o;
|
|
1844
2320
|
}
|
|
1845
2321
|
|
|
1846
2322
|
// ../core/src/adapters/index.ts
|
|
1847
|
-
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity];
|
|
2323
|
+
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
1848
2324
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
1849
2325
|
|
|
1850
2326
|
// src/index.ts
|
|
@@ -1853,16 +2329,16 @@ function matches(expect, got, pathStr = "") {
|
|
|
1853
2329
|
if (Array.isArray(expect)) {
|
|
1854
2330
|
if (!Array.isArray(got) || got.length !== expect.length) return `${pathStr}: expected ${expect.length} items`;
|
|
1855
2331
|
for (let i = 0; i < expect.length; i++) {
|
|
1856
|
-
const
|
|
1857
|
-
if (
|
|
2332
|
+
const r = matches(expect[i], got[i], `${pathStr}[${i}]`);
|
|
2333
|
+
if (r) return r;
|
|
1858
2334
|
}
|
|
1859
2335
|
return null;
|
|
1860
2336
|
}
|
|
1861
2337
|
if (expect && typeof expect === "object") {
|
|
1862
2338
|
if (!got || typeof got !== "object") return `${pathStr}: expected an object`;
|
|
1863
2339
|
for (const [k, v] of Object.entries(expect)) {
|
|
1864
|
-
const
|
|
1865
|
-
if (
|
|
2340
|
+
const r = matches(v, got[k], `${pathStr}.${k}`);
|
|
2341
|
+
if (r) return r;
|
|
1866
2342
|
}
|
|
1867
2343
|
return null;
|
|
1868
2344
|
}
|
|
@@ -1877,51 +2353,64 @@ async function runAdapterFixtures(files) {
|
|
|
1877
2353
|
results.push({ file, ok: false, why: `no adapter ${fx.harness}` });
|
|
1878
2354
|
continue;
|
|
1879
2355
|
}
|
|
1880
|
-
const
|
|
2356
|
+
const r = adapter.fromHook({
|
|
1881
2357
|
argv: fx.input.argv,
|
|
1882
2358
|
stdin: fx.input.stdin,
|
|
1883
2359
|
env: fx.input.env ?? {},
|
|
1884
2360
|
cwd: fx.input.cwd ?? "/"
|
|
1885
2361
|
});
|
|
1886
|
-
if (!
|
|
1887
|
-
results.push({ file, ok: false, why: "fromHook returned null" });
|
|
2362
|
+
if (!r) {
|
|
2363
|
+
results.push(fx.expect.length ? { file, ok: false, why: "fromHook returned null" } : { file, ok: true });
|
|
1888
2364
|
continue;
|
|
1889
2365
|
}
|
|
1890
2366
|
let why = matches(
|
|
1891
2367
|
fx.expect.map((e) => e.type),
|
|
1892
|
-
|
|
2368
|
+
r.events.map((e) => e.type),
|
|
1893
2369
|
"types"
|
|
1894
2370
|
);
|
|
1895
2371
|
if (!why)
|
|
1896
2372
|
for (const [i, e] of fx.expect.entries()) {
|
|
1897
|
-
const got =
|
|
1898
|
-
why = matches(e.harness.event, got
|
|
2373
|
+
const got = r.events[i];
|
|
2374
|
+
why = matches(e.harness.event, got.harnessEvent, `[${i}].harness.event`) ?? matches(e.data, got.data, `[${i}].data`) ?? matches({ ...e.session, seq: "*" }, r.session, `[${i}].session`);
|
|
1899
2375
|
if (why) break;
|
|
1900
2376
|
}
|
|
1901
2377
|
results.push(why ? { file, ok: false, why } : { file, ok: true });
|
|
1902
2378
|
}
|
|
1903
|
-
const failed = results.filter((
|
|
1904
|
-
const table = results.map((
|
|
2379
|
+
const failed = results.filter((r) => !r.ok).length;
|
|
2380
|
+
const table = results.map((r) => `${r.ok ? "ok " : "FAIL"} ${r.file}${r.why ? ` \u2014 ${r.why}` : ""}`).join("\n");
|
|
1905
2381
|
return { results, failed, table };
|
|
1906
2382
|
}
|
|
1907
2383
|
|
|
1908
2384
|
// src/cli.ts
|
|
1909
|
-
var target = process.argv[2];
|
|
1910
|
-
if (!target) {
|
|
1911
|
-
process.stdout.write("usage: sessionpipe-conformance <conformance/harness dir | https://receiver>\n");
|
|
1912
|
-
process.exit(2);
|
|
1913
|
-
}
|
|
1914
|
-
if (/^https?:\/\//.test(target)) {
|
|
1915
|
-
process.stdout.write("receiver scenarios land in M3\n");
|
|
1916
|
-
process.exit(2);
|
|
1917
|
-
}
|
|
1918
2385
|
var walk = (d) => readdirSync5(d).flatMap((n) => {
|
|
1919
|
-
const p =
|
|
2386
|
+
const p = path8.join(d, n);
|
|
1920
2387
|
return statSync4(p).isDirectory() ? walk(p) : p.endsWith(".json") ? [p] : [];
|
|
1921
2388
|
});
|
|
1922
|
-
|
|
1923
|
-
|
|
2389
|
+
async function main(argv, io = process) {
|
|
2390
|
+
const target = argv[0];
|
|
2391
|
+
if (!target) {
|
|
2392
|
+
io.stdout.write("usage: sessionpipe-conformance <conformance/harness dir | https://receiver>\n");
|
|
2393
|
+
return 2;
|
|
2394
|
+
}
|
|
2395
|
+
if (/^https?:\/\//.test(target)) {
|
|
2396
|
+
io.stdout.write("receiver scenarios land in M3\n");
|
|
2397
|
+
return 2;
|
|
2398
|
+
}
|
|
2399
|
+
const r = await runAdapterFixtures(walk(target));
|
|
2400
|
+
io.stdout.write(`${r.table}
|
|
1924
2401
|
${r.results.length - r.failed}/${r.results.length} passed
|
|
1925
2402
|
`);
|
|
1926
|
-
|
|
2403
|
+
return r.failed ? 1 : 0;
|
|
2404
|
+
}
|
|
2405
|
+
function isMain() {
|
|
2406
|
+
try {
|
|
2407
|
+
return !!process.argv[1] && import.meta.url === pathToFileURL(realpathSync2(process.argv[1])).href;
|
|
2408
|
+
} catch {
|
|
2409
|
+
return false;
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
if (isMain()) process.exit(await main(process.argv.slice(2)));
|
|
2413
|
+
export {
|
|
2414
|
+
main
|
|
2415
|
+
};
|
|
1927
2416
|
//# sourceMappingURL=cli.js.map
|