sessionpipe-core 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/control.js +54 -3
- package/dist/control.js.map +2 -2
- package/dist/index.js +706 -167
- package/dist/index.js.map +4 -4
- package/dist/schema/v1.d.ts +139 -71
- package/dist/schema/v1.d.ts.map +1 -1
- package/dist/schema.js +56 -5
- package/dist/schema.js.map +2 -2
- package/dist/src/adapters/antigravity-control.d.ts.map +1 -1
- package/dist/src/adapters/antigravity.d.ts +1 -2
- package/dist/src/adapters/antigravity.d.ts.map +1 -1
- package/dist/src/adapters/claude-code-control.d.ts +2 -0
- package/dist/src/adapters/claude-code-control.d.ts.map +1 -1
- package/dist/src/adapters/claude-code.d.ts +10 -0
- package/dist/src/adapters/claude-code.d.ts.map +1 -1
- package/dist/src/adapters/claude-shaped.d.ts.map +1 -1
- package/dist/src/adapters/codex-config.d.ts +2 -2
- package/dist/src/adapters/codex-config.d.ts.map +1 -1
- package/dist/src/adapters/codex-control.d.ts.map +1 -1
- package/dist/src/adapters/codex-rollout.d.ts +8 -0
- package/dist/src/adapters/codex-rollout.d.ts.map +1 -0
- package/dist/src/adapters/codex.d.ts +1 -1
- package/dist/src/adapters/codex.d.ts.map +1 -1
- package/dist/src/adapters/devin.d.ts +32 -0
- package/dist/src/adapters/devin.d.ts.map +1 -0
- package/dist/src/adapters/gemini-cli.d.ts.map +1 -1
- package/dist/src/adapters/index.d.ts +4 -2
- package/dist/src/adapters/index.d.ts.map +1 -1
- package/dist/src/adapters/types.d.ts +9 -1
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/control/verify.d.ts +27 -0
- package/dist/src/control/verify.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/outbox.d.ts +13 -2
- package/dist/src/outbox.d.ts.map +1 -1
- package/dist/src/paths.d.ts +3 -1
- package/dist/src/paths.d.ts.map +1 -1
- package/dist/src/privacy/filter.d.ts +2 -0
- package/dist/src/privacy/filter.d.ts.map +1 -1
- package/dist/src/privacy/index.d.ts +1 -1
- package/dist/src/privacy/index.d.ts.map +1 -1
- package/dist/src/readers/files.d.ts.map +1 -1
- package/dist/src/readers/git.d.ts.map +1 -1
- package/dist/src/readers/sqlite.d.ts +23 -0
- package/dist/src/readers/sqlite.d.ts.map +1 -0
- package/dist/src/readers/transcripts.d.ts +5 -2
- package/dist/src/readers/transcripts.d.ts.map +1 -1
- package/dist/src/short-id.d.ts +2 -0
- package/dist/src/short-id.d.ts.map +1 -0
- package/dist/src/sinks/file.d.ts.map +1 -1
- package/dist/src/sinks/stdout.d.ts.map +1 -1
- package/dist/src/types.d.ts +1 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/ulid.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// sessionpipe-core 0.
|
|
1
|
+
// sessionpipe-core 0.9.0 · Apache-2.0 · https://sessionpipe.org
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __export = (target, all) => {
|
|
4
4
|
for (var name in all)
|
|
@@ -21,20 +21,20 @@ import path4 from "node:path";
|
|
|
21
21
|
// src/paths.ts
|
|
22
22
|
import os from "node:os";
|
|
23
23
|
import path from "node:path";
|
|
24
|
-
var
|
|
24
|
+
var homeDir = () => os.homedir();
|
|
25
25
|
function configFile(env = process.env) {
|
|
26
26
|
if (env.SESSIONPIPE_CONFIG) return path.resolve(env.SESSIONPIPE_CONFIG);
|
|
27
27
|
if (process.platform === "win32")
|
|
28
|
-
return path.join(env.APPDATA || path.join(
|
|
29
|
-
return path.join(env.XDG_CONFIG_HOME || path.join(
|
|
28
|
+
return path.join(env.APPDATA || path.join(homeDir(), "AppData", "Roaming"), "sessionpipe", "config.json");
|
|
29
|
+
return path.join(env.XDG_CONFIG_HOME || path.join(homeDir(), ".config"), "sessionpipe", "config.json");
|
|
30
30
|
}
|
|
31
31
|
function stateDir(env = process.env) {
|
|
32
32
|
if (env.SESSIONPIPE_STATE) return path.resolve(env.SESSIONPIPE_STATE);
|
|
33
33
|
if (process.platform === "win32")
|
|
34
|
-
return path.join(env.LOCALAPPDATA || path.join(
|
|
35
|
-
return path.join(env.XDG_STATE_HOME || path.join(
|
|
34
|
+
return path.join(env.LOCALAPPDATA || path.join(homeDir(), "AppData", "Local"), "sessionpipe");
|
|
35
|
+
return path.join(env.XDG_STATE_HOME || path.join(homeDir(), ".local", "state"), "sessionpipe");
|
|
36
36
|
}
|
|
37
|
-
function tilde(p, home =
|
|
37
|
+
function tilde(p, home = homeDir()) {
|
|
38
38
|
if (!p) return p;
|
|
39
39
|
if (p === home) return "~";
|
|
40
40
|
return p.startsWith(home + path.sep) || p.startsWith(`${home}/`) ? `~${p.slice(home.length)}` : p;
|
|
@@ -131,6 +131,19 @@ function recentFiles(root, depth, sinceMs, match) {
|
|
|
131
131
|
return out;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
// src/adapters/codex-rollout.ts
|
|
135
|
+
function codexSubagent(meta) {
|
|
136
|
+
if (meta?.type !== "session_meta") return false;
|
|
137
|
+
const p = meta.payload;
|
|
138
|
+
return !!p?.source?.subagent;
|
|
139
|
+
}
|
|
140
|
+
function codexReplayBoundary(meta) {
|
|
141
|
+
if (meta?.type !== "session_meta") return void 0;
|
|
142
|
+
const p = meta.payload;
|
|
143
|
+
if (!p || !p.forked_from_id && !p.source?.subagent) return void 0;
|
|
144
|
+
return (record) => typeof p.id === "string" && !!p.id && record?.type === "event_msg" && record.payload?.thread_id === p.id;
|
|
145
|
+
}
|
|
146
|
+
|
|
134
147
|
// src/readers/transcripts.ts
|
|
135
148
|
function pairTurns(msgs, endLine) {
|
|
136
149
|
const turns = [];
|
|
@@ -153,10 +166,14 @@ ${m.text}`;
|
|
|
153
166
|
}
|
|
154
167
|
if (cur?.assistant) turns.push(cur);
|
|
155
168
|
const line = cur && !cur.assistant ? cur.startLine : endLine;
|
|
156
|
-
|
|
169
|
+
const out = {
|
|
170
|
+
turns: turns.map((t) => ({ user: t.user, assistant: t.assistant, at: t.at, endLine: t.endLine })),
|
|
171
|
+
line
|
|
172
|
+
};
|
|
173
|
+
if (cur && !cur.assistant && cur.user) out.open = { user: cur.user, at: cur.at };
|
|
174
|
+
return out;
|
|
157
175
|
}
|
|
158
|
-
var isInjected = (text) => /^<[a-z_-]
|
|
159
|
-
var endOf = (lines) => lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
|
|
176
|
+
var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
|
|
160
177
|
var parse = (raw) => {
|
|
161
178
|
try {
|
|
162
179
|
return JSON.parse(raw);
|
|
@@ -164,6 +181,11 @@ var parse = (raw) => {
|
|
|
164
181
|
return null;
|
|
165
182
|
}
|
|
166
183
|
};
|
|
184
|
+
var endOf = (lines) => {
|
|
185
|
+
const last = lines[lines.length - 1];
|
|
186
|
+
if (last === void 0) return 0;
|
|
187
|
+
return last === "" || parse(last) === null ? lines.length - 1 : lines.length;
|
|
188
|
+
};
|
|
167
189
|
var textParts = (c, kinds) => {
|
|
168
190
|
if (typeof c === "string") return c;
|
|
169
191
|
if (!Array.isArray(c)) return "";
|
|
@@ -171,13 +193,21 @@ var textParts = (c, kinds) => {
|
|
|
171
193
|
(b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
|
|
172
194
|
).map((b) => b.text).join("\n");
|
|
173
195
|
};
|
|
196
|
+
var queuedPrompt = (a) => {
|
|
197
|
+
if (!a || typeof a !== "object") return "";
|
|
198
|
+
const q = a;
|
|
199
|
+
if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
|
|
200
|
+
if (q.origin?.kind && q.origin.kind !== "human") return "";
|
|
201
|
+
return textParts(q.prompt, ["text"]).trim();
|
|
202
|
+
};
|
|
174
203
|
function readClaude(lines, from) {
|
|
175
204
|
const msgs = [];
|
|
176
205
|
for (let i = from; i < lines.length; i++) {
|
|
177
206
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
178
207
|
if (!j || j.isSidechain) continue;
|
|
179
208
|
const origin = j.origin;
|
|
180
|
-
if (j.type === "user" && (j.isMeta || origin?.kind && origin.kind !== "human"))
|
|
209
|
+
if (j.type === "user" && (j.isMeta || j.isCompactSummary || j.repeatsCompactSummary || origin?.kind && origin.kind !== "human"))
|
|
210
|
+
continue;
|
|
181
211
|
const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
|
|
182
212
|
const c = j.message?.content;
|
|
183
213
|
if (j.type === "user") {
|
|
@@ -187,6 +217,9 @@ function readClaude(lines, from) {
|
|
|
187
217
|
} else if (j.type === "assistant" && Array.isArray(c)) {
|
|
188
218
|
const text = textParts(c, ["text"]).trim();
|
|
189
219
|
if (text) msgs.push({ role: "assistant", text, at, line: i });
|
|
220
|
+
} else if (j.type === "attachment") {
|
|
221
|
+
const text = queuedPrompt(j.attachment);
|
|
222
|
+
if (text && !isInjected(text)) msgs.push({ role: "user", text, at, line: i });
|
|
190
223
|
}
|
|
191
224
|
}
|
|
192
225
|
return pairTurns(msgs, endOf(lines));
|
|
@@ -196,7 +229,10 @@ function codexInjected(text) {
|
|
|
196
229
|
}
|
|
197
230
|
function readCodex(lines, from) {
|
|
198
231
|
const msgs = [];
|
|
199
|
-
|
|
232
|
+
const boundary = codexReplayBoundary(parse(lines.find((line) => line.trim()) ?? ""));
|
|
233
|
+
const start = boundary ? lines.findIndex((line) => boundary(parse(line))) : 0;
|
|
234
|
+
if (start < 0) return pairTurns(msgs, endOf(lines));
|
|
235
|
+
for (let i = Math.max(from, start); i < lines.length; i++) {
|
|
200
236
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
201
237
|
if (!j || j.type !== "response_item") continue;
|
|
202
238
|
const p = j.payload;
|
|
@@ -234,7 +270,7 @@ function readAntigravity(lines, from) {
|
|
|
234
270
|
const at = Date.parse(String(j.created_at ?? j.timestamp ?? "")) || Date.now();
|
|
235
271
|
if (j.type === "USER_INPUT") {
|
|
236
272
|
const m = /<USER_REQUEST>([\s\S]*?)(?:<\/USER_REQUEST>|$)/.exec(j.content);
|
|
237
|
-
const text = (m ? m[1]
|
|
273
|
+
const text = (m ? m[1] : j.content).trim();
|
|
238
274
|
if (text) msgs.push({ role: "user", text, at, line: i });
|
|
239
275
|
} else if (j.type === "PLANNER_RESPONSE") {
|
|
240
276
|
const text = j.content.trim();
|
|
@@ -282,6 +318,10 @@ function formatJson(value, f) {
|
|
|
282
318
|
const text = JSON.stringify(value, null, f.indent).replace(/\n/g, f.eol);
|
|
283
319
|
return f.finalNewline ? text + f.eol : text;
|
|
284
320
|
}
|
|
321
|
+
function readJson(file) {
|
|
322
|
+
const f = readJsonFile(file);
|
|
323
|
+
return f.ok ? f.value : {};
|
|
324
|
+
}
|
|
285
325
|
function entryFor(event, harness, cmd, o) {
|
|
286
326
|
const matcher = o.matcherFor(event);
|
|
287
327
|
return {
|
|
@@ -351,8 +391,7 @@ function uninstallClaudeShaped(file, events, opts = {}) {
|
|
|
351
391
|
unlinkSync(file);
|
|
352
392
|
return { file, changed: true, note: "removed (sessionpipe created it)" };
|
|
353
393
|
}
|
|
354
|
-
|
|
355
|
-
if (next !== f.raw) writeFileSync(file, next);
|
|
394
|
+
writeFileSync(file, formatJson(settings2, f));
|
|
356
395
|
return { file, changed: true };
|
|
357
396
|
}
|
|
358
397
|
function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
@@ -379,7 +418,7 @@ var HOOK_NAME = "sessionpipe";
|
|
|
379
418
|
var AG_EVENTS = ["PreInvocation", "PostInvocation", "PostToolUse", "Stop"];
|
|
380
419
|
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';
|
|
381
420
|
var antigravityAnswer = (event) => event === "PreToolUse" ? AG_STALE_PRETOOL : "{}\n";
|
|
382
|
-
var gemini = (env) => env.GEMINI_CLI_HOME || path4.join(
|
|
421
|
+
var gemini = (env) => env.GEMINI_CLI_HOME || path4.join(homeDir(), ".gemini");
|
|
383
422
|
var hooksFile = (env) => path4.join(gemini(env), "config", "hooks.json");
|
|
384
423
|
var projectsDir = (env) => path4.join(gemini(env), "config", "projects");
|
|
385
424
|
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) => path4.join(gemini(env), d));
|
|
@@ -445,7 +484,8 @@ var antigravity = {
|
|
|
445
484
|
const event = input.argv[1] ?? "";
|
|
446
485
|
let s = {};
|
|
447
486
|
try {
|
|
448
|
-
|
|
487
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
488
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
449
489
|
} catch {
|
|
450
490
|
}
|
|
451
491
|
const id = s.conversationId ?? s.session_id ?? s.sessionId;
|
|
@@ -501,19 +541,16 @@ var antigravity = {
|
|
|
501
541
|
}
|
|
502
542
|
return readAntigravity(text.split("\n"), fromLine);
|
|
503
543
|
},
|
|
504
|
-
facts(session, transcript, hints
|
|
544
|
+
facts(session, transcript, hints) {
|
|
505
545
|
if (!transcript) return {};
|
|
506
546
|
return antigravityFacts(transcript, {
|
|
507
547
|
conversation: session.id,
|
|
508
548
|
workspaces: hints.workspaces ?? (session.cwd ? [session.cwd] : []),
|
|
509
|
-
state,
|
|
510
549
|
env: process.env
|
|
511
550
|
});
|
|
512
551
|
},
|
|
513
552
|
backfill(sinceMs, env = process.env) {
|
|
514
|
-
const
|
|
515
|
-
const noState = { get: () => ({}), save: () => {
|
|
516
|
-
} };
|
|
553
|
+
const rows2 = [];
|
|
517
554
|
for (const data of dataDirs(env)) {
|
|
518
555
|
let ids = [];
|
|
519
556
|
try {
|
|
@@ -527,13 +564,13 @@ var antigravity = {
|
|
|
527
564
|
} catch {
|
|
528
565
|
continue;
|
|
529
566
|
}
|
|
530
|
-
const facts = antigravityFacts(f, { conversation: id, workspaces: [],
|
|
567
|
+
const facts = antigravityFacts(f, { conversation: id, workspaces: [], env });
|
|
531
568
|
if (!facts.started_at || !facts.last_at) continue;
|
|
532
569
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
533
|
-
|
|
570
|
+
rows2.push({ session: { id, ...rest }, started_at, last_at });
|
|
534
571
|
}
|
|
535
572
|
}
|
|
536
|
-
return
|
|
573
|
+
return rows2;
|
|
537
574
|
}
|
|
538
575
|
};
|
|
539
576
|
function prune(o) {
|
|
@@ -583,7 +620,7 @@ function agProjects(dir) {
|
|
|
583
620
|
}
|
|
584
621
|
const folders = [];
|
|
585
622
|
for (const r of j?.projectResources?.resources ?? []) {
|
|
586
|
-
const f = agPath(r?.folderUri);
|
|
623
|
+
const f = agPath(r?.folderUri ?? r?.gitFolder?.folderUri);
|
|
587
624
|
if (f) folders.push(...agVariants(f));
|
|
588
625
|
}
|
|
589
626
|
if (folders.length) out.push({ id, folders });
|
|
@@ -637,7 +674,7 @@ function antigravityFacts(file, opts) {
|
|
|
637
674
|
for (const j of H) {
|
|
638
675
|
if (j.type !== "USER_INPUT" || typeof j.content !== "string") continue;
|
|
639
676
|
const m = /<USER_REQUEST>([\s\S]*?)(?:<\/USER_REQUEST>|$)/.exec(j.content);
|
|
640
|
-
const text = (m ? m[1]
|
|
677
|
+
const text = (m ? m[1] : j.content).trim();
|
|
641
678
|
const line = (text.split("\n").find((l) => l.trim()) ?? "").trim();
|
|
642
679
|
if (!line || /^<[a-z_-]+>/i.test(line)) continue;
|
|
643
680
|
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
@@ -736,7 +773,7 @@ function runHeadless(bin, args, o) {
|
|
|
736
773
|
if (o.onLine) {
|
|
737
774
|
const chunk = partial + b.toString("utf8");
|
|
738
775
|
const lines = chunk.split("\n");
|
|
739
|
-
partial = lines.pop()
|
|
776
|
+
partial = lines.pop();
|
|
740
777
|
for (const line of lines) {
|
|
741
778
|
if (!line.trim()) continue;
|
|
742
779
|
try {
|
|
@@ -885,7 +922,7 @@ function agyError(stderr) {
|
|
|
885
922
|
for (const k of ["message", "error", "detail"]) if (typeof j[k] === "string" && j[k]) return j[k];
|
|
886
923
|
} catch {
|
|
887
924
|
}
|
|
888
|
-
return raw.split("\n")[0]
|
|
925
|
+
return raw.split("\n")[0].slice(0, 300);
|
|
889
926
|
}
|
|
890
927
|
var AgyReading = class {
|
|
891
928
|
session = null;
|
|
@@ -962,10 +999,9 @@ function gitFacts(cwd) {
|
|
|
962
999
|
return out;
|
|
963
1000
|
}
|
|
964
1001
|
function stripCredentials(url) {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
return u.replace(/^https?:\/\//, "").replace(/\.git$/, "");
|
|
1002
|
+
const ssh = /^(?:ssh:\/\/)?git@([^:/]+)[:/](.+?)(?:\.git)?$/.exec(url);
|
|
1003
|
+
if (ssh) return `${ssh[1]}/${ssh[2]}`;
|
|
1004
|
+
return url.replace(/\/\/[^/@]*@/, "//").replace(/^https?:\/\//, "").replace(/\.git$/, "");
|
|
969
1005
|
}
|
|
970
1006
|
|
|
971
1007
|
// src/adapters/claude-code.ts
|
|
@@ -996,7 +1032,7 @@ var OPTS = {
|
|
|
996
1032
|
timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
|
|
997
1033
|
};
|
|
998
1034
|
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
999
|
-
function claudeDirs(env = process.env, home = env.HOME ||
|
|
1035
|
+
function claudeDirs(env = process.env, home = env.HOME || homeDir()) {
|
|
1000
1036
|
const out = [];
|
|
1001
1037
|
const add = (d) => {
|
|
1002
1038
|
if (!d) return;
|
|
@@ -1037,11 +1073,13 @@ var claudeCode = {
|
|
|
1037
1073
|
const event = input.argv[1] ?? "";
|
|
1038
1074
|
let s = {};
|
|
1039
1075
|
try {
|
|
1040
|
-
|
|
1076
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
1077
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
1041
1078
|
} catch {
|
|
1042
1079
|
}
|
|
1043
1080
|
const id = s.session_id ?? s.sessionId;
|
|
1044
1081
|
if (!id) return null;
|
|
1082
|
+
if (notClaudeCode(String(id), s)) return null;
|
|
1045
1083
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
1046
1084
|
if (typeof s.model === "string") session.model = s.model;
|
|
1047
1085
|
if (typeof s.agent_id === "string" && event.startsWith("Subagent") === false && s.agent_id)
|
|
@@ -1160,6 +1198,7 @@ var claudeCode = {
|
|
|
1160
1198
|
return null;
|
|
1161
1199
|
}
|
|
1162
1200
|
const hints = {};
|
|
1201
|
+
if (event === "UserPromptSubmit" && typeof s.prompt === "string") hints.prompt = s.prompt;
|
|
1163
1202
|
const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
|
|
1164
1203
|
if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
|
|
1165
1204
|
if (event !== "PreToolUse" && event !== "PostToolUse") hints.claude_pid = claudePid(input.env);
|
|
@@ -1185,7 +1224,7 @@ var claudeCode = {
|
|
|
1185
1224
|
return out;
|
|
1186
1225
|
},
|
|
1187
1226
|
backfill(sinceMs, env = process.env) {
|
|
1188
|
-
const
|
|
1227
|
+
const rows2 = [];
|
|
1189
1228
|
const files = [];
|
|
1190
1229
|
for (const d of claudeDirs(env))
|
|
1191
1230
|
files.push(...recentFiles(path7.join(d, "projects"), 1, sinceMs, (n) => /^[0-9a-f-]{36}\.jsonl$/.test(n)));
|
|
@@ -1197,19 +1236,23 @@ var claudeCode = {
|
|
|
1197
1236
|
if (!facts.started_at || !facts.last_at) continue;
|
|
1198
1237
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
1199
1238
|
const cwd = rest.cwd;
|
|
1200
|
-
|
|
1239
|
+
rows2.push({
|
|
1201
1240
|
session: { id, ...rest, ...gitFacts(cwd), ...rest.branch ? { branch: rest.branch } : {} },
|
|
1202
1241
|
started_at,
|
|
1203
1242
|
last_at
|
|
1204
1243
|
});
|
|
1205
1244
|
}
|
|
1206
|
-
return
|
|
1245
|
+
return rows2;
|
|
1207
1246
|
}
|
|
1208
1247
|
};
|
|
1209
1248
|
function prune2(o) {
|
|
1210
1249
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
1211
1250
|
return o;
|
|
1212
1251
|
}
|
|
1252
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1253
|
+
function notClaudeCode(id, s) {
|
|
1254
|
+
return typeof s.transcript_path !== "string" && !UUID.test(id);
|
|
1255
|
+
}
|
|
1213
1256
|
function pickSource(v) {
|
|
1214
1257
|
return typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
1215
1258
|
}
|
|
@@ -1315,26 +1358,41 @@ function parentOf() {
|
|
|
1315
1358
|
}
|
|
1316
1359
|
return (pid) => table.get(pid) || 0;
|
|
1317
1360
|
}
|
|
1318
|
-
var
|
|
1319
|
-
function
|
|
1320
|
-
const hit = accountCache.get(dir);
|
|
1321
|
-
if (hit !== void 0) return hit;
|
|
1361
|
+
var EMAIL_RE = /^[^@\s]{1,64}@[^@\s]{1,189}$/;
|
|
1362
|
+
function claudeLogin(dir, home = homeDir()) {
|
|
1322
1363
|
const files = [path7.join(dir, ".claude.json")];
|
|
1323
|
-
if (path7.resolve(dir) === path7.join(
|
|
1324
|
-
let acct = null;
|
|
1364
|
+
if (path7.resolve(dir) === path7.join(home, ".claude")) files.unshift(path7.join(home, ".claude.json"));
|
|
1325
1365
|
for (const f of files) {
|
|
1326
1366
|
try {
|
|
1327
1367
|
const a = JSON.parse(readFileSync3(f, "utf8")).oauthAccount;
|
|
1328
|
-
if (a?.accountUuid) {
|
|
1329
|
-
|
|
1330
|
-
|
|
1368
|
+
if (typeof a?.accountUuid === "string" && a.accountUuid) {
|
|
1369
|
+
const email = typeof a.emailAddress === "string" && EMAIL_RE.test(a.emailAddress) ? a.emailAddress : void 0;
|
|
1370
|
+
return { id: a.accountUuid.slice(0, 128), ...email ? { email } : {} };
|
|
1331
1371
|
}
|
|
1332
1372
|
} catch {
|
|
1333
1373
|
}
|
|
1334
1374
|
}
|
|
1375
|
+
return null;
|
|
1376
|
+
}
|
|
1377
|
+
var accountCache = /* @__PURE__ */ new Map();
|
|
1378
|
+
function claudeAccount(dir) {
|
|
1379
|
+
const hit = accountCache.get(dir);
|
|
1380
|
+
if (hit !== void 0) return hit;
|
|
1381
|
+
const acct = claudeLogin(dir)?.id ?? null;
|
|
1335
1382
|
accountCache.set(dir, acct);
|
|
1336
1383
|
return acct;
|
|
1337
1384
|
}
|
|
1385
|
+
function claudeAccounts(dirs, home = homeDir()) {
|
|
1386
|
+
const out = [];
|
|
1387
|
+
for (const d of dirs) {
|
|
1388
|
+
const l = claudeLogin(d, home);
|
|
1389
|
+
if (!l) continue;
|
|
1390
|
+
const seen = out.find((a) => a.id === l.id);
|
|
1391
|
+
if (!seen) out.push({ harness: NAME2, ...l });
|
|
1392
|
+
else if (!seen.email && l.email) seen.email = l.email;
|
|
1393
|
+
}
|
|
1394
|
+
return out;
|
|
1395
|
+
}
|
|
1338
1396
|
var LINK_RE = /^https:\/\/claude\.ai\/code\/session_[A-Za-z0-9]+$/;
|
|
1339
1397
|
function transcriptLink(records) {
|
|
1340
1398
|
for (let i = records.length - 1; i >= 0; i--) {
|
|
@@ -1490,7 +1548,8 @@ var claude_code_control_exports = {};
|
|
|
1490
1548
|
__export(claude_code_control_exports, {
|
|
1491
1549
|
JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
|
|
1492
1550
|
claudeEnv: () => claudeEnv,
|
|
1493
|
-
claudeLogin: () =>
|
|
1551
|
+
claudeLogin: () => claudeLogin2,
|
|
1552
|
+
cmpVersion: () => cmpVersion,
|
|
1494
1553
|
detectCaps: () => detectCaps,
|
|
1495
1554
|
findClaude: () => findClaude,
|
|
1496
1555
|
findTranscript: () => findTranscript,
|
|
@@ -1616,7 +1675,7 @@ function parseAuthStatus(stdout) {
|
|
|
1616
1675
|
return { loggedIn: null };
|
|
1617
1676
|
}
|
|
1618
1677
|
}
|
|
1619
|
-
function
|
|
1678
|
+
function claudeLogin2(bin, configDir, env = process.env) {
|
|
1620
1679
|
return new Promise((resolve) => {
|
|
1621
1680
|
execFile(
|
|
1622
1681
|
bin,
|
|
@@ -1867,18 +1926,13 @@ function parseArgv(raw) {
|
|
|
1867
1926
|
function readNotify(text) {
|
|
1868
1927
|
const { head, tail, at } = split(text);
|
|
1869
1928
|
const m = NOTIFY_LINE.exec(head);
|
|
1870
|
-
if (m)
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
index: m.index,
|
|
1875
|
-
length: m[0].length,
|
|
1876
|
-
misplaced: false,
|
|
1877
|
-
other: !OURS2.test(m[1] ?? "")
|
|
1878
|
-
};
|
|
1929
|
+
if (m) {
|
|
1930
|
+
const raw = m[1];
|
|
1931
|
+
return { raw, argv: parseArgv(raw), index: m.index, length: m[0].length, misplaced: false, other: !OURS2.test(raw) };
|
|
1932
|
+
}
|
|
1879
1933
|
const k = NOTIFY_KEY.exec(head);
|
|
1880
1934
|
if (k) {
|
|
1881
|
-
const rest = head.slice(k.index).split("\n")[0]
|
|
1935
|
+
const rest = head.slice(k.index).split("\n")[0];
|
|
1882
1936
|
return {
|
|
1883
1937
|
raw: rest.replace(NOTIFY_KEY, "").trim(),
|
|
1884
1938
|
argv: null,
|
|
@@ -1889,15 +1943,11 @@ function readNotify(text) {
|
|
|
1889
1943
|
};
|
|
1890
1944
|
}
|
|
1891
1945
|
const t = NOTIFY_LINE.exec(tail);
|
|
1892
|
-
if (t
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
argv: parseArgv(t[
|
|
1896
|
-
|
|
1897
|
-
length: t[0].length,
|
|
1898
|
-
misplaced: true,
|
|
1899
|
-
other: false
|
|
1900
|
-
};
|
|
1946
|
+
if (t) {
|
|
1947
|
+
const raw = t[1];
|
|
1948
|
+
if (OURS2.test(raw))
|
|
1949
|
+
return { raw, argv: parseArgv(raw), index: at + t.index, length: t[0].length, misplaced: true, other: false };
|
|
1950
|
+
}
|
|
1901
1951
|
return null;
|
|
1902
1952
|
}
|
|
1903
1953
|
function dropLine(text, index, length) {
|
|
@@ -1916,9 +1966,7 @@ function withNotify(text, line) {
|
|
|
1916
1966
|
if (n?.misplaced) return insertTopLevel(dropLine(text, n.index, n.length), line);
|
|
1917
1967
|
return insertTopLevel(text, line);
|
|
1918
1968
|
}
|
|
1919
|
-
function replaceNotify(text, line) {
|
|
1920
|
-
const n = readNotify(text);
|
|
1921
|
-
if (!n || n.misplaced || !n.length) return null;
|
|
1969
|
+
function replaceNotify(text, n, line) {
|
|
1922
1970
|
return text.slice(0, n.index) + line + text.slice(n.index + n.length);
|
|
1923
1971
|
}
|
|
1924
1972
|
function withoutNotify(text) {
|
|
@@ -1959,7 +2007,7 @@ var CODEX_EVENTS = [
|
|
|
1959
2007
|
];
|
|
1960
2008
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1961
2009
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1962
|
-
var codexHome = (env) => env.CODEX_HOME || path9.join(
|
|
2010
|
+
var codexHome = (env) => env.CODEX_HOME || path9.join(homeDir(), ".codex");
|
|
1963
2011
|
var hooksFile2 = (env) => path9.join(codexHome(env), "hooks.json");
|
|
1964
2012
|
var configFile2 = (env) => path9.join(codexHome(env), "config.toml");
|
|
1965
2013
|
var sessionsDir = (env) => path9.join(codexHome(env), "sessions");
|
|
@@ -1997,11 +2045,8 @@ function installNotify(cmd, env) {
|
|
|
1997
2045
|
return { file, changed: false, note: "notify current (after Codex Computer Use)" };
|
|
1998
2046
|
}
|
|
1999
2047
|
if (!prev || OURS2.test(JSON.stringify(prev))) {
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
write(next);
|
|
2003
|
-
return { file, changed: true, note: "chained after Codex Computer Use (--previous-notify)" };
|
|
2004
|
-
}
|
|
2048
|
+
write(replaceNotify(text, n, `notify = ${withPreviousRaw(n.raw, argv)}`));
|
|
2049
|
+
return { file, changed: true, note: "chained after Codex Computer Use (--previous-notify)" };
|
|
2005
2050
|
}
|
|
2006
2051
|
}
|
|
2007
2052
|
return {
|
|
@@ -2030,8 +2075,7 @@ function uninstallNotify(env) {
|
|
|
2030
2075
|
}
|
|
2031
2076
|
const prev = previousNotify(n.argv);
|
|
2032
2077
|
if (isComputerUse(n.argv) && prev && OURS2.test(JSON.stringify(prev))) {
|
|
2033
|
-
|
|
2034
|
-
if (next) writeFileSync3(file, next);
|
|
2078
|
+
writeFileSync3(file, replaceNotify(text, n, `notify = ${stripPreviousRaw(n.raw)}`));
|
|
2035
2079
|
return { file, changed: true };
|
|
2036
2080
|
}
|
|
2037
2081
|
return { file, changed: false };
|
|
@@ -2061,6 +2105,13 @@ function findRollout(threadId, env = process.env) {
|
|
|
2061
2105
|
walk(sessionsDir(env), 0);
|
|
2062
2106
|
return best;
|
|
2063
2107
|
}
|
|
2108
|
+
function rolloutMeta(file) {
|
|
2109
|
+
return scanLines(file, (line) => parseLines([line])[0] ?? {});
|
|
2110
|
+
}
|
|
2111
|
+
function ofAnotherThread(file, id) {
|
|
2112
|
+
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);
|
|
2113
|
+
return !!m?.[1] && m[1].toLowerCase() !== id.toLowerCase();
|
|
2114
|
+
}
|
|
2064
2115
|
var codex = {
|
|
2065
2116
|
name: NAME3,
|
|
2066
2117
|
events: CODEX_EVENTS,
|
|
@@ -2103,11 +2154,14 @@ var codex = {
|
|
|
2103
2154
|
if (event === "notify") {
|
|
2104
2155
|
const raw = input.argv.find((a) => a.startsWith("{")) ?? input.stdin;
|
|
2105
2156
|
try {
|
|
2106
|
-
|
|
2157
|
+
const j = raw ? JSON.parse(raw) : {};
|
|
2158
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
2107
2159
|
} catch {
|
|
2108
2160
|
}
|
|
2109
2161
|
const id2 = s["thread-id"] ?? s.thread_id ?? s["session-id"] ?? s.session_id;
|
|
2110
2162
|
if (!id2) return null;
|
|
2163
|
+
const file = findRollout(String(id2), input.env);
|
|
2164
|
+
if (!file || codexSubagent(rolloutMeta(file))) return null;
|
|
2111
2165
|
const r2 = {
|
|
2112
2166
|
session: { id: String(id2), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd },
|
|
2113
2167
|
events: [
|
|
@@ -2117,18 +2171,19 @@ var codex = {
|
|
|
2117
2171
|
harnessEvent: "notify"
|
|
2118
2172
|
}
|
|
2119
2173
|
],
|
|
2120
|
-
hints: {}
|
|
2174
|
+
hints: {},
|
|
2175
|
+
transcript: file
|
|
2121
2176
|
};
|
|
2122
|
-
const file = findRollout(String(id2), input.env);
|
|
2123
|
-
if (file) r2.transcript = file;
|
|
2124
2177
|
return r2;
|
|
2125
2178
|
}
|
|
2126
2179
|
try {
|
|
2127
|
-
|
|
2180
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
2181
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
2128
2182
|
} catch {
|
|
2129
2183
|
}
|
|
2130
2184
|
const id = s.session_id;
|
|
2131
2185
|
if (!id) return null;
|
|
2186
|
+
if (s.transcript_path === null) return null;
|
|
2132
2187
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
2133
2188
|
if (typeof s.model === "string") session.model = s.model;
|
|
2134
2189
|
const turn_id = typeof s.turn_id === "string" ? s.turn_id : void 0;
|
|
@@ -2200,7 +2255,8 @@ var codex = {
|
|
|
2200
2255
|
return null;
|
|
2201
2256
|
}
|
|
2202
2257
|
const r = { session, events, hints: {} };
|
|
2203
|
-
if (typeof s.transcript_path === "string"
|
|
2258
|
+
if (typeof s.transcript_path === "string" && !ofAnotherThread(s.transcript_path, String(id)))
|
|
2259
|
+
r.transcript = s.transcript_path;
|
|
2204
2260
|
return r;
|
|
2205
2261
|
},
|
|
2206
2262
|
readTranscript(file, fromLine) {
|
|
@@ -2218,16 +2274,17 @@ var codex = {
|
|
|
2218
2274
|
return out;
|
|
2219
2275
|
},
|
|
2220
2276
|
backfill(sinceMs, env = process.env) {
|
|
2221
|
-
const
|
|
2277
|
+
const rows2 = [];
|
|
2222
2278
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
2223
2279
|
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path9.basename(f));
|
|
2224
2280
|
if (!m?.[1]) continue;
|
|
2281
|
+
if (codexSubagent(rolloutMeta(f))) continue;
|
|
2225
2282
|
const facts = codexFacts(f);
|
|
2226
2283
|
if (!facts.started_at || !facts.last_at) continue;
|
|
2227
2284
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
2228
|
-
|
|
2285
|
+
rows2.push({ session: { id: m[1], ...rest }, started_at, last_at });
|
|
2229
2286
|
}
|
|
2230
|
-
return
|
|
2287
|
+
return rows2;
|
|
2231
2288
|
}
|
|
2232
2289
|
};
|
|
2233
2290
|
function prune3(o) {
|
|
@@ -2236,10 +2293,11 @@ function prune3(o) {
|
|
|
2236
2293
|
}
|
|
2237
2294
|
function codexFacts(file) {
|
|
2238
2295
|
const { head, tail } = ends(file, 128 * 1024, 64 * 1024);
|
|
2239
|
-
|
|
2240
|
-
|
|
2296
|
+
let H = parseLines(head);
|
|
2297
|
+
let T = parseLines(tail);
|
|
2241
2298
|
const out = {};
|
|
2242
2299
|
const meta = H.find((j) => j.type === "session_meta" && j.payload);
|
|
2300
|
+
const boundary = codexReplayBoundary(meta);
|
|
2243
2301
|
if (meta?.payload) {
|
|
2244
2302
|
const p = meta.payload;
|
|
2245
2303
|
if (p.cwd) out.cwd = p.cwd;
|
|
@@ -2247,6 +2305,13 @@ function codexFacts(file) {
|
|
|
2247
2305
|
if (p.git?.branch) out.branch = p.git.branch;
|
|
2248
2306
|
if (p.git?.repository_url) out.repo = String(p.git.repository_url).replace(/\/\/[^/@]*@/, "//");
|
|
2249
2307
|
}
|
|
2308
|
+
if (boundary) {
|
|
2309
|
+
const h = H.findIndex(boundary);
|
|
2310
|
+
const t = T.findIndex(boundary);
|
|
2311
|
+
H = h < 0 ? [] : H.slice(h);
|
|
2312
|
+
const found = h >= 0 || t >= 0 || scanLines(file, (line) => boundary(parseLines([line])[0] ?? null) ? true : null, Number.POSITIVE_INFINITY);
|
|
2313
|
+
T = t >= 0 ? T.slice(t) : found ? T : [];
|
|
2314
|
+
}
|
|
2250
2315
|
const ctx = H.concat(T).reverse().find((j) => j.type === "turn_context" && j.payload?.model);
|
|
2251
2316
|
if (ctx?.payload?.model) out.model = ctx.payload.model;
|
|
2252
2317
|
for (const j of H) {
|
|
@@ -2254,12 +2319,12 @@ function codexFacts(file) {
|
|
|
2254
2319
|
if (j.type !== "response_item" || !p || p.type !== "message" || p.role !== "user") continue;
|
|
2255
2320
|
const text = (p.content ?? []).filter((b) => b && b.type === "input_text" && typeof b.text === "string").map((b) => b.text).join("\n").trim();
|
|
2256
2321
|
if (!text || codexInjected(text)) continue;
|
|
2257
|
-
const line = text.split("\n")
|
|
2322
|
+
const line = text.split("\n")[0];
|
|
2258
2323
|
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
2259
2324
|
out.title_source = "first-ask";
|
|
2260
2325
|
break;
|
|
2261
2326
|
}
|
|
2262
|
-
const times = H.concat(T).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
2327
|
+
const times = (boundary && meta ? [meta, ...H, ...T] : H.concat(T)).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
2263
2328
|
if (times.length) {
|
|
2264
2329
|
out.started_at = Math.min(...times);
|
|
2265
2330
|
out.last_at = Math.max(...times);
|
|
@@ -2401,11 +2466,432 @@ function codexStderr(stderr) {
|
|
|
2401
2466
|
}
|
|
2402
2467
|
var reading2 = () => new CodexReading();
|
|
2403
2468
|
|
|
2404
|
-
// src/adapters/
|
|
2405
|
-
import {
|
|
2406
|
-
import { existsSync as
|
|
2469
|
+
// src/adapters/devin.ts
|
|
2470
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2471
|
+
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
2407
2472
|
import path11 from "node:path";
|
|
2408
|
-
|
|
2473
|
+
|
|
2474
|
+
// src/readers/sqlite.ts
|
|
2475
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2476
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
2477
|
+
import { createRequire } from "node:module";
|
|
2478
|
+
function quiet(fn) {
|
|
2479
|
+
const original = process.emitWarning;
|
|
2480
|
+
try {
|
|
2481
|
+
process.emitWarning = ((warning, ...rest) => {
|
|
2482
|
+
const kind = typeof rest[0] === "string" ? rest[0] : rest[0]?.type;
|
|
2483
|
+
if (kind === "ExperimentalWarning" && /sqlite/i.test(String(warning))) return;
|
|
2484
|
+
original.call(process, warning, ...rest);
|
|
2485
|
+
});
|
|
2486
|
+
return fn();
|
|
2487
|
+
} finally {
|
|
2488
|
+
process.emitWarning = original;
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
var ctorCache;
|
|
2492
|
+
function dbCtor() {
|
|
2493
|
+
if (ctorCache !== void 0) return ctorCache;
|
|
2494
|
+
ctorCache = null;
|
|
2495
|
+
try {
|
|
2496
|
+
const req = createRequire(import.meta.url);
|
|
2497
|
+
const m = quiet(() => req("node:sqlite"));
|
|
2498
|
+
if (typeof m?.DatabaseSync === "function") ctorCache = m.DatabaseSync;
|
|
2499
|
+
} catch {
|
|
2500
|
+
}
|
|
2501
|
+
return ctorCache;
|
|
2502
|
+
}
|
|
2503
|
+
function plain(row) {
|
|
2504
|
+
if (!row || typeof row !== "object") return null;
|
|
2505
|
+
const out = {};
|
|
2506
|
+
for (const [k, v] of Object.entries(row)) {
|
|
2507
|
+
if (typeof v === "bigint") out[k] = Number(v);
|
|
2508
|
+
else if (v instanceof Uint8Array) out[k] = null;
|
|
2509
|
+
else out[k] = v;
|
|
2510
|
+
}
|
|
2511
|
+
return out;
|
|
2512
|
+
}
|
|
2513
|
+
var rows = (v) => {
|
|
2514
|
+
if (!Array.isArray(v)) return null;
|
|
2515
|
+
const out = [];
|
|
2516
|
+
for (const r of v) {
|
|
2517
|
+
const p = plain(r);
|
|
2518
|
+
if (p) out.push(p);
|
|
2519
|
+
}
|
|
2520
|
+
return out;
|
|
2521
|
+
};
|
|
2522
|
+
function viaNode(file, sql, params, o) {
|
|
2523
|
+
const C = dbCtor();
|
|
2524
|
+
if (!C) return null;
|
|
2525
|
+
let db;
|
|
2526
|
+
try {
|
|
2527
|
+
db = quiet(() => new C(file, { readOnly: true, timeout: o.timeoutMs }));
|
|
2528
|
+
} catch {
|
|
2529
|
+
return null;
|
|
2530
|
+
}
|
|
2531
|
+
try {
|
|
2532
|
+
try {
|
|
2533
|
+
db.exec(`PRAGMA busy_timeout = ${Math.round(o.timeoutMs)}`);
|
|
2534
|
+
} catch {
|
|
2535
|
+
}
|
|
2536
|
+
return quiet(() => rows(db.prepare(sql).all(...params)));
|
|
2537
|
+
} catch {
|
|
2538
|
+
return null;
|
|
2539
|
+
} finally {
|
|
2540
|
+
try {
|
|
2541
|
+
db.close();
|
|
2542
|
+
} catch {
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
var CHILD = `const { DatabaseSync } = require("node:sqlite");
|
|
2547
|
+
const [, file, sql, params, ms] = process.argv;
|
|
2548
|
+
const db = new DatabaseSync(file, { readOnly: true, timeout: Number(ms) });
|
|
2549
|
+
try { db.exec("PRAGMA busy_timeout = " + Number(ms)); } catch {}
|
|
2550
|
+
const out = db.prepare(sql).all(...JSON.parse(params));
|
|
2551
|
+
db.close();
|
|
2552
|
+
process.stdout.write(JSON.stringify(out));`;
|
|
2553
|
+
function viaChild(file, sql, params, o) {
|
|
2554
|
+
try {
|
|
2555
|
+
const out = execFileSync4(
|
|
2556
|
+
process.execPath,
|
|
2557
|
+
["--experimental-sqlite", "--no-warnings", "-e", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],
|
|
2558
|
+
{
|
|
2559
|
+
encoding: "utf8",
|
|
2560
|
+
timeout: o.timeoutMs + 3e3,
|
|
2561
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2562
|
+
maxBuffer: o.maxBuffer,
|
|
2563
|
+
windowsHide: true
|
|
2564
|
+
}
|
|
2565
|
+
);
|
|
2566
|
+
return rows(JSON.parse(out || "null"));
|
|
2567
|
+
} catch {
|
|
2568
|
+
return null;
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
var SAFE = /^[A-Za-z0-9 ._:@/+-]{0,256}$/;
|
|
2572
|
+
function inlineParams(sql, params) {
|
|
2573
|
+
if (sql.includes("'")) return null;
|
|
2574
|
+
const holes = sql.split("?").length - 1;
|
|
2575
|
+
if (holes !== params.length) return null;
|
|
2576
|
+
let i = 0;
|
|
2577
|
+
let bad = false;
|
|
2578
|
+
const out = sql.replace(/\?/g, () => {
|
|
2579
|
+
const p = params[i++];
|
|
2580
|
+
if (typeof p === "number") {
|
|
2581
|
+
if (!Number.isFinite(p)) {
|
|
2582
|
+
bad = true;
|
|
2583
|
+
return "NULL";
|
|
2584
|
+
}
|
|
2585
|
+
return String(p);
|
|
2586
|
+
}
|
|
2587
|
+
if (typeof p !== "string" || !SAFE.test(p)) {
|
|
2588
|
+
bad = true;
|
|
2589
|
+
return "NULL";
|
|
2590
|
+
}
|
|
2591
|
+
return `'${p}'`;
|
|
2592
|
+
});
|
|
2593
|
+
return bad ? null : out;
|
|
2594
|
+
}
|
|
2595
|
+
function viaCli(file, sql, params, o) {
|
|
2596
|
+
const text = inlineParams(sql, params);
|
|
2597
|
+
if (!text) return null;
|
|
2598
|
+
try {
|
|
2599
|
+
const out = execFileSync4("sqlite3", ["-readonly", "-json", file, text], {
|
|
2600
|
+
encoding: "utf8",
|
|
2601
|
+
timeout: o.timeoutMs + 3e3,
|
|
2602
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2603
|
+
maxBuffer: o.maxBuffer,
|
|
2604
|
+
windowsHide: true
|
|
2605
|
+
}).trim();
|
|
2606
|
+
if (!out) return [];
|
|
2607
|
+
return rows(JSON.parse(out));
|
|
2608
|
+
} catch {
|
|
2609
|
+
return null;
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
function querySqlite(file, sql, params = [], opts = {}) {
|
|
2613
|
+
const o = {
|
|
2614
|
+
timeoutMs: opts.timeoutMs ?? 2e3,
|
|
2615
|
+
engine: opts.engine ?? "auto",
|
|
2616
|
+
maxBuffer: opts.maxBuffer ?? 32 * 1024 * 1024
|
|
2617
|
+
};
|
|
2618
|
+
if (!file) return null;
|
|
2619
|
+
try {
|
|
2620
|
+
if (!existsSync5(file)) return null;
|
|
2621
|
+
} catch {
|
|
2622
|
+
return null;
|
|
2623
|
+
}
|
|
2624
|
+
const ways = o.engine === "auto" ? [viaNode, viaChild, viaCli] : o.engine === "node" ? [viaNode] : o.engine === "child" ? [viaChild] : [viaCli];
|
|
2625
|
+
for (const way of ways) {
|
|
2626
|
+
const r = way(file, sql, params, o);
|
|
2627
|
+
if (r) return r;
|
|
2628
|
+
}
|
|
2629
|
+
return null;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
// src/adapters/devin.ts
|
|
2633
|
+
var NAME4 = "devin";
|
|
2634
|
+
var DEVIN_EVENTS = [
|
|
2635
|
+
"SessionStart",
|
|
2636
|
+
"UserPromptSubmit",
|
|
2637
|
+
"PreToolUse",
|
|
2638
|
+
"PostToolUse",
|
|
2639
|
+
"PermissionRequest",
|
|
2640
|
+
"Stop",
|
|
2641
|
+
"PostCompaction",
|
|
2642
|
+
"SessionEnd"
|
|
2643
|
+
];
|
|
2644
|
+
var OPTS3 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
2645
|
+
var eventsFor3 = (lean) => lean ? DEVIN_EVENTS.filter((e) => e !== "PreToolUse") : [...DEVIN_EVENTS];
|
|
2646
|
+
var INSTALL_NOTE = "Devin drops a whole hooks file on one event name it doesn't know; `/hooks` in `devin` lists what loaded";
|
|
2647
|
+
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";
|
|
2648
|
+
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)`;
|
|
2649
|
+
function foreignEvent(file) {
|
|
2650
|
+
const hooks = readJson(file).hooks;
|
|
2651
|
+
if (!hooks || typeof hooks !== "object" || Array.isArray(hooks)) return void 0;
|
|
2652
|
+
const ok = DEVIN_EVENTS;
|
|
2653
|
+
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
2654
|
+
}
|
|
2655
|
+
var homeOf = (env) => env.HOME || homeDir();
|
|
2656
|
+
var appData = (env) => env.APPDATA || path11.join(homeOf(env), "AppData", "Roaming");
|
|
2657
|
+
var devinConfigDir = (env = process.env) => process.platform === "win32" ? path11.join(appData(env), "devin") : path11.join(env.XDG_CONFIG_HOME || path11.join(homeOf(env), ".config"), "devin");
|
|
2658
|
+
var configFile3 = (env) => path11.join(devinConfigDir(env), "config.json");
|
|
2659
|
+
var devinDataDir = (env = process.env) => process.platform === "win32" ? path11.join(appData(env), "devin", "cli") : path11.join(env.XDG_DATA_HOME || path11.join(homeOf(env), ".local", "share"), "devin", "cli");
|
|
2660
|
+
var sessionsDb = (env) => path11.join(devinDataDir(env), "sessions.db");
|
|
2661
|
+
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
2662
|
+
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2663
|
+
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
2664
|
+
var pickSource2 = (v) => typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
2665
|
+
function errText2(v) {
|
|
2666
|
+
if (typeof v === "string" && v) return v.slice(0, 2e3);
|
|
2667
|
+
if (v && typeof v === "object") {
|
|
2668
|
+
const e = v;
|
|
2669
|
+
if (typeof e.message === "string" && e.message) return e.message.slice(0, 2e3);
|
|
2670
|
+
if (typeof e.error === "string" && e.error) return e.error.slice(0, 2e3);
|
|
2671
|
+
}
|
|
2672
|
+
return void 0;
|
|
2673
|
+
}
|
|
2674
|
+
var cut = (s, n) => s.length > n ? `${s.slice(0, n).replace(/\s+\S*$/, "")}\u2026` : s;
|
|
2675
|
+
var devin = {
|
|
2676
|
+
name: NAME4,
|
|
2677
|
+
events: DEVIN_EVENTS,
|
|
2678
|
+
detect: (env = process.env) => existsSync6(devinConfigDir(env)) || existsSync6(devinDataDir(env)),
|
|
2679
|
+
configFiles: (env = process.env) => [configFile3(env)],
|
|
2680
|
+
install: (cmd, env = process.env, opts = {}) => {
|
|
2681
|
+
const file = configFile3(env);
|
|
2682
|
+
const r = installClaudeShaped(file, NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
2683
|
+
if (r.skipped) return [r];
|
|
2684
|
+
const bad = foreignEvent(file);
|
|
2685
|
+
return [{ ...r, note: bad ? deadFileNote(bad) : INSTALL_NOTE }];
|
|
2686
|
+
},
|
|
2687
|
+
uninstall: (env = process.env, opts = {}) => [
|
|
2688
|
+
uninstallClaudeShaped(configFile3(env), DEVIN_EVENTS, { created: opts.created?.includes(configFile3(env)) })
|
|
2689
|
+
],
|
|
2690
|
+
installed: (cmd, env = process.env, opts = {}) => {
|
|
2691
|
+
const file = configFile3(env);
|
|
2692
|
+
const state = installedClaudeShaped(file, NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
2693
|
+
const bad = state === "missing" ? void 0 : foreignEvent(file);
|
|
2694
|
+
return [{ file, state: bad ? "inactive" : state, note: bad ? deadFileNote(bad) : CLAUDE_NOTE }];
|
|
2695
|
+
},
|
|
2696
|
+
fromHook(input) {
|
|
2697
|
+
const event = input.argv[1] ?? "";
|
|
2698
|
+
let s = {};
|
|
2699
|
+
try {
|
|
2700
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
2701
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
2702
|
+
} catch {
|
|
2703
|
+
}
|
|
2704
|
+
const id = typeof s.session_id === "string" ? s.session_id : "";
|
|
2705
|
+
if (!id) return null;
|
|
2706
|
+
const session = { id, cwd: input.env.DEVIN_PROJECT_DIR ?? input.cwd };
|
|
2707
|
+
const turn_id = typeof s.prompt_id === "string" ? s.prompt_id : void 0;
|
|
2708
|
+
const tool = validTool4(s.tool_name) ? s.tool_name : void 0;
|
|
2709
|
+
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
2710
|
+
const response = s.tool_response && typeof s.tool_response === "object" && !Array.isArray(s.tool_response) ? s.tool_response : void 0;
|
|
2711
|
+
const permissionId = () => `perm-${createHash2("sha256").update(`${turn_id ?? ""}|${tool ?? ""}|${JSON.stringify(s.tool_input ?? null)}`).digest("hex").slice(0, 16)}`;
|
|
2712
|
+
const ev = (type, data) => ({ type, data: prune4(data), harnessEvent: event });
|
|
2713
|
+
const events = [];
|
|
2714
|
+
switch (event) {
|
|
2715
|
+
case "SessionStart":
|
|
2716
|
+
events.push(ev("session.started", { source: pickSource2(s.source) }));
|
|
2717
|
+
break;
|
|
2718
|
+
case "UserPromptSubmit":
|
|
2719
|
+
events.push(
|
|
2720
|
+
ev("turn.started", {
|
|
2721
|
+
turn_id,
|
|
2722
|
+
prompt_chars: typeof s.prompt === "string" ? [...s.prompt].length : void 0
|
|
2723
|
+
})
|
|
2724
|
+
);
|
|
2725
|
+
break;
|
|
2726
|
+
case "PreToolUse":
|
|
2727
|
+
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, input: s.tool_input }));
|
|
2728
|
+
break;
|
|
2729
|
+
case "PostToolUse":
|
|
2730
|
+
if (tool)
|
|
2731
|
+
events.push(
|
|
2732
|
+
ev("tool.ended", {
|
|
2733
|
+
tool,
|
|
2734
|
+
call_id,
|
|
2735
|
+
turn_id,
|
|
2736
|
+
// `ok` is required on tool.ended (PROTOCOL.md §events), so a response
|
|
2737
|
+
// that carries no success flag at all reads as "nothing said it failed".
|
|
2738
|
+
ok: response?.success !== false,
|
|
2739
|
+
error: errText2(response?.error),
|
|
2740
|
+
input: s.tool_input,
|
|
2741
|
+
output: response ? response.output : s.tool_response
|
|
2742
|
+
})
|
|
2743
|
+
);
|
|
2744
|
+
break;
|
|
2745
|
+
case "PermissionRequest":
|
|
2746
|
+
events.push(ev("attention.needed", { attention_id: call_id ?? permissionId(), kind: "permission", tool }));
|
|
2747
|
+
break;
|
|
2748
|
+
case "Stop":
|
|
2749
|
+
events.push(ev("turn.ended", { turn_id, reason: "stop" }));
|
|
2750
|
+
break;
|
|
2751
|
+
case "PostCompaction":
|
|
2752
|
+
events.push(ev("context.compacted", { trigger: "auto" }));
|
|
2753
|
+
break;
|
|
2754
|
+
case "SessionEnd":
|
|
2755
|
+
events.push(ev("session.ended", { reason: endReason3(s.reason) }));
|
|
2756
|
+
break;
|
|
2757
|
+
default:
|
|
2758
|
+
return null;
|
|
2759
|
+
}
|
|
2760
|
+
return {
|
|
2761
|
+
session,
|
|
2762
|
+
events,
|
|
2763
|
+
hints: {},
|
|
2764
|
+
transcript: transcriptRef(sessionsDb(input.env), id)
|
|
2765
|
+
};
|
|
2766
|
+
},
|
|
2767
|
+
readTranscript(file, fromLine) {
|
|
2768
|
+
const ref = splitRef(file);
|
|
2769
|
+
if (!ref) return { turns: [], line: fromLine };
|
|
2770
|
+
return readDevinChain(ref.db, ref.session, fromLine);
|
|
2771
|
+
},
|
|
2772
|
+
facts(session, transcript, _hints, _state, env = process.env) {
|
|
2773
|
+
const out = {};
|
|
2774
|
+
const version = devinVersion(env);
|
|
2775
|
+
if (version) out.harness_version = version;
|
|
2776
|
+
const account = devinAccount(env);
|
|
2777
|
+
if (account) out.account_id = account;
|
|
2778
|
+
const ref = splitRef(transcript) ?? { db: sessionsDb(env), session: session.id };
|
|
2779
|
+
if (!SESSION_ID.test(ref.session)) return out;
|
|
2780
|
+
const rows2 = querySqlite(ref.db, SESSION_SQL, [ref.session]);
|
|
2781
|
+
const row = rows2?.[0];
|
|
2782
|
+
if (!row) return out;
|
|
2783
|
+
Object.assign(out, sessionFacts(row));
|
|
2784
|
+
return out;
|
|
2785
|
+
},
|
|
2786
|
+
backfill(sinceMs, env = process.env) {
|
|
2787
|
+
const rows2 = querySqlite(sessionsDb(env), BACKFILL_SQL, ["text", Math.floor(sinceMs / 1e3)]);
|
|
2788
|
+
if (!rows2) return [];
|
|
2789
|
+
const out = [];
|
|
2790
|
+
for (const row of rows2) {
|
|
2791
|
+
const id = String(row.id);
|
|
2792
|
+
if (!SESSION_ID.test(id)) continue;
|
|
2793
|
+
const { started_at, last_at, ...rest } = sessionFacts(row);
|
|
2794
|
+
if (!started_at || !last_at) continue;
|
|
2795
|
+
out.push({ session: { id, ...rest }, started_at, last_at });
|
|
2796
|
+
}
|
|
2797
|
+
return out;
|
|
2798
|
+
}
|
|
2799
|
+
};
|
|
2800
|
+
function prune4(o) {
|
|
2801
|
+
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
2802
|
+
return o;
|
|
2803
|
+
}
|
|
2804
|
+
var transcriptRef = (db, session) => `${db}#${session}`;
|
|
2805
|
+
function splitRef(ref) {
|
|
2806
|
+
if (!ref) return null;
|
|
2807
|
+
const at = ref.lastIndexOf("#");
|
|
2808
|
+
if (at <= 0) return null;
|
|
2809
|
+
const db = ref.slice(0, at);
|
|
2810
|
+
const session = ref.slice(at + 1);
|
|
2811
|
+
return db && SESSION_ID.test(session) ? { db, session } : null;
|
|
2812
|
+
}
|
|
2813
|
+
var CHAIN_LIMIT = 4e3;
|
|
2814
|
+
var CHAIN_SQL = `WITH RECURSIVE chain(node_id, parent_node_id, chat_message, created_at) AS (
|
|
2815
|
+
SELECT node_id, parent_node_id, chat_message, created_at FROM message_nodes
|
|
2816
|
+
WHERE session_id = ? AND node_id = (SELECT main_chain_id FROM sessions WHERE id = ?)
|
|
2817
|
+
UNION ALL
|
|
2818
|
+
SELECT m.node_id, m.parent_node_id, m.chat_message, m.created_at FROM message_nodes m
|
|
2819
|
+
JOIN chain c ON m.node_id = c.parent_node_id AND m.session_id = ? AND m.node_id >= ?
|
|
2820
|
+
)
|
|
2821
|
+
SELECT node_id, chat_message, created_at FROM chain ORDER BY node_id DESC LIMIT ${CHAIN_LIMIT}`;
|
|
2822
|
+
var SESSION_FIELDS = `s.model, s.working_directory, s.created_at, s.last_activity_at,
|
|
2823
|
+
(SELECT p.content FROM prompt_history p WHERE p.session_id = s.id AND p.is_shell = 0
|
|
2824
|
+
ORDER BY p.timestamp ASC, p.id ASC LIMIT 1) AS first_ask`;
|
|
2825
|
+
var SESSION_SQL = `SELECT ${SESSION_FIELDS} FROM sessions s WHERE s.id = ? LIMIT 1`;
|
|
2826
|
+
var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
2827
|
+
WHERE s.hidden = 0 AND typeof(s.id) = ? AND s.last_activity_at >= ?
|
|
2828
|
+
ORDER BY s.last_activity_at DESC LIMIT 500`;
|
|
2829
|
+
function sessionFacts(row) {
|
|
2830
|
+
const out = {};
|
|
2831
|
+
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
2832
|
+
if (typeof row.working_directory === "string" && path11.isAbsolute(row.working_directory))
|
|
2833
|
+
out.cwd = row.working_directory;
|
|
2834
|
+
const started = Number(row.created_at);
|
|
2835
|
+
const last = Number(row.last_activity_at);
|
|
2836
|
+
if (Number.isFinite(started) && started > 0) out.started_at = Math.round(started * 1e3);
|
|
2837
|
+
if (Number.isFinite(last) && last > 0) out.last_at = Math.round(last * 1e3);
|
|
2838
|
+
if (typeof row.first_ask === "string") {
|
|
2839
|
+
const line = row.first_ask.split("\n").find((l) => l.trim()) ?? "";
|
|
2840
|
+
if (line.trim()) {
|
|
2841
|
+
out.title = cut(line.trim(), 80);
|
|
2842
|
+
out.title_source = "first-ask";
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
return out;
|
|
2846
|
+
}
|
|
2847
|
+
function readDevinChain(db, session, fromLine) {
|
|
2848
|
+
if (!SESSION_ID.test(session)) return { turns: [], line: fromLine };
|
|
2849
|
+
const floor = Number.isFinite(fromLine) && fromLine > 0 ? Math.floor(fromLine) : 0;
|
|
2850
|
+
const rows2 = querySqlite(db, CHAIN_SQL, [session, session, session, floor]);
|
|
2851
|
+
if (!rows2?.length) return { turns: [], line: fromLine };
|
|
2852
|
+
const msgs = [];
|
|
2853
|
+
let end = fromLine;
|
|
2854
|
+
for (const row of rows2) {
|
|
2855
|
+
const node = Number(row.node_id);
|
|
2856
|
+
if (!Number.isFinite(node)) continue;
|
|
2857
|
+
if (node + 1 > end) end = node + 1;
|
|
2858
|
+
if (node < fromLine) continue;
|
|
2859
|
+
let j;
|
|
2860
|
+
try {
|
|
2861
|
+
j = JSON.parse(String(row.chat_message ?? ""));
|
|
2862
|
+
} catch {
|
|
2863
|
+
continue;
|
|
2864
|
+
}
|
|
2865
|
+
const text = typeof j.content === "string" ? j.content.trim() : "";
|
|
2866
|
+
if (!text) continue;
|
|
2867
|
+
const secs = Number(row.created_at);
|
|
2868
|
+
const at = Date.parse(String(j.metadata?.created_at ?? "")) || (Number.isFinite(secs) && secs > 0 ? Math.round(secs * 1e3) : 0) || Date.now();
|
|
2869
|
+
if (j.role === "user" && j.metadata?.is_user_input === true) msgs.push({ role: "user", text, at, line: node });
|
|
2870
|
+
else if (j.role === "assistant") msgs.push({ role: "assistant", text, at, line: node });
|
|
2871
|
+
}
|
|
2872
|
+
msgs.sort((a, b) => a.line - b.line);
|
|
2873
|
+
return pairTurns(msgs, end);
|
|
2874
|
+
}
|
|
2875
|
+
function devinVersion(env = process.env) {
|
|
2876
|
+
try {
|
|
2877
|
+
const target = readlinkSync(path11.join(devinDataDir(env), "_versions", "current"));
|
|
2878
|
+
const name = path11.basename(target);
|
|
2879
|
+
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
2880
|
+
} catch {
|
|
2881
|
+
return void 0;
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
function devinAccount(env = process.env) {
|
|
2885
|
+
const d = readJson(configFile3(env)).devin;
|
|
2886
|
+
const id = d && typeof d === "object" ? d.org_id : void 0;
|
|
2887
|
+
return typeof id === "string" && /^[A-Za-z0-9_.:-]{1,128}$/.test(id) ? id : void 0;
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
// src/adapters/gemini-cli.ts
|
|
2891
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
2892
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
|
|
2893
|
+
import path12 from "node:path";
|
|
2894
|
+
var NAME5 = "gemini-cli";
|
|
2409
2895
|
var GEMINI_EVENTS = [
|
|
2410
2896
|
"SessionStart",
|
|
2411
2897
|
"BeforeAgent",
|
|
@@ -2416,11 +2902,11 @@ var GEMINI_EVENTS = [
|
|
|
2416
2902
|
"PreCompress",
|
|
2417
2903
|
"SessionEnd"
|
|
2418
2904
|
];
|
|
2419
|
-
var
|
|
2420
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
2421
|
-
var settings = (env) =>
|
|
2422
|
-
var
|
|
2423
|
-
var
|
|
2905
|
+
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
2906
|
+
var geminiDir = (env) => env.GEMINI_CLI_HOME || path12.join(homeDir(), ".gemini");
|
|
2907
|
+
var settings = (env) => path12.join(geminiDir(env), "settings.json");
|
|
2908
|
+
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2909
|
+
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
2424
2910
|
function geminiHooksActive(settingsFile) {
|
|
2425
2911
|
let tools;
|
|
2426
2912
|
try {
|
|
@@ -2432,7 +2918,7 @@ function geminiHooksActive(settingsFile) {
|
|
|
2432
2918
|
return { active: false, note: "inactive: tools.enableHooks is false in settings.json" };
|
|
2433
2919
|
let version = "";
|
|
2434
2920
|
try {
|
|
2435
|
-
version =
|
|
2921
|
+
version = execFileSync5("gemini", ["--version"], {
|
|
2436
2922
|
encoding: "utf8",
|
|
2437
2923
|
timeout: 4e3,
|
|
2438
2924
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -2451,13 +2937,13 @@ function geminiHooksActive(settingsFile) {
|
|
|
2451
2937
|
};
|
|
2452
2938
|
}
|
|
2453
2939
|
var geminiCli = {
|
|
2454
|
-
name:
|
|
2940
|
+
name: NAME5,
|
|
2455
2941
|
events: GEMINI_EVENTS,
|
|
2456
2942
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
2457
|
-
detect: (env = process.env) =>
|
|
2943
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path12.join(geminiDir(env), "tmp")),
|
|
2458
2944
|
configFiles: (env = process.env) => [settings(env)],
|
|
2459
2945
|
install: (cmd, env = process.env, opts = {}) => {
|
|
2460
|
-
const r = installClaudeShaped(settings(env),
|
|
2946
|
+
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
2461
2947
|
const a = geminiHooksActive(settings(env));
|
|
2462
2948
|
return [a.note && !r.skipped ? { ...r, note: [r.note, a.note].filter(Boolean).join("; ") } : r];
|
|
2463
2949
|
},
|
|
@@ -2465,7 +2951,7 @@ var geminiCli = {
|
|
|
2465
2951
|
uninstallClaudeShaped(settings(env), GEMINI_EVENTS, { created: opts.created?.includes(settings(env)) })
|
|
2466
2952
|
],
|
|
2467
2953
|
installed: (cmd, env = process.env, opts = {}) => {
|
|
2468
|
-
const state = installedClaudeShaped(settings(env),
|
|
2954
|
+
const state = installedClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
2469
2955
|
const a = geminiHooksActive(settings(env));
|
|
2470
2956
|
return [
|
|
2471
2957
|
{
|
|
@@ -2479,14 +2965,15 @@ var geminiCli = {
|
|
|
2479
2965
|
const event = input.argv[1] ?? "";
|
|
2480
2966
|
let s = {};
|
|
2481
2967
|
try {
|
|
2482
|
-
|
|
2968
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
2969
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
2483
2970
|
} catch {
|
|
2484
2971
|
}
|
|
2485
2972
|
const id = s.session_id;
|
|
2486
2973
|
if (!id) return null;
|
|
2487
2974
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
2488
|
-
const tool =
|
|
2489
|
-
const ev = (type, data) => ({ type, data:
|
|
2975
|
+
const tool = validTool5(s.tool_name) ? s.tool_name : void 0;
|
|
2976
|
+
const ev = (type, data) => ({ type, data: prune5(data), harnessEvent: event });
|
|
2490
2977
|
const events = [];
|
|
2491
2978
|
switch (event) {
|
|
2492
2979
|
case "SessionStart":
|
|
@@ -2567,7 +3054,7 @@ var geminiCli = {
|
|
|
2567
3054
|
const raw = first ? first.content ?? first.message ?? first.text : void 0;
|
|
2568
3055
|
const text = typeof raw === "string" ? raw.trim() : "";
|
|
2569
3056
|
if (text) {
|
|
2570
|
-
const line = text.split("\n")
|
|
3057
|
+
const line = text.split("\n")[0];
|
|
2571
3058
|
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
2572
3059
|
out.title_source = "first-ask";
|
|
2573
3060
|
}
|
|
@@ -2581,13 +3068,13 @@ var geminiCli = {
|
|
|
2581
3068
|
backfill: () => []
|
|
2582
3069
|
// chat logs are keyed by a project hash, not a session id we can name; the first hook of a resumed session reports it
|
|
2583
3070
|
};
|
|
2584
|
-
function
|
|
3071
|
+
function prune5(o) {
|
|
2585
3072
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
2586
3073
|
return o;
|
|
2587
3074
|
}
|
|
2588
3075
|
|
|
2589
3076
|
// src/adapters/index.ts
|
|
2590
|
-
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity];
|
|
3077
|
+
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
2591
3078
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
2592
3079
|
function drivableHarnesses(env = process.env) {
|
|
2593
3080
|
return [
|
|
@@ -2598,8 +3085,8 @@ function drivableHarnesses(env = process.env) {
|
|
|
2598
3085
|
}
|
|
2599
3086
|
|
|
2600
3087
|
// src/config.ts
|
|
2601
|
-
import { chmodSync, existsSync as
|
|
2602
|
-
import
|
|
3088
|
+
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3089
|
+
import path13 from "node:path";
|
|
2603
3090
|
var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
|
|
2604
3091
|
function readConfig(file = configFile()) {
|
|
2605
3092
|
try {
|
|
@@ -2615,10 +3102,10 @@ function readConfig(file = configFile()) {
|
|
|
2615
3102
|
}
|
|
2616
3103
|
}
|
|
2617
3104
|
function writeConfig(c, file = configFile()) {
|
|
2618
|
-
mkdirSync4(
|
|
3105
|
+
mkdirSync4(path13.dirname(file), { recursive: true, mode: 448 });
|
|
2619
3106
|
writeFileSync4(file, `${JSON.stringify(c, null, 2)}
|
|
2620
3107
|
`, { mode: 384 });
|
|
2621
|
-
if (process.platform !== "win32" &&
|
|
3108
|
+
if (process.platform !== "win32" && existsSync8(file)) {
|
|
2622
3109
|
try {
|
|
2623
3110
|
chmodSync(file, 384);
|
|
2624
3111
|
} catch {
|
|
@@ -2647,7 +3134,7 @@ function encodeTime(t) {
|
|
|
2647
3134
|
function ulid(now = Date.now()) {
|
|
2648
3135
|
if (now === lastTime) {
|
|
2649
3136
|
for (let i = lastRand.length - 1; i >= 0; i--) {
|
|
2650
|
-
const v =
|
|
3137
|
+
const v = lastRand[i] + 1;
|
|
2651
3138
|
if (v < 32) {
|
|
2652
3139
|
lastRand[i] = v;
|
|
2653
3140
|
break;
|
|
@@ -2656,8 +3143,7 @@ function ulid(now = Date.now()) {
|
|
|
2656
3143
|
}
|
|
2657
3144
|
} else {
|
|
2658
3145
|
lastTime = now;
|
|
2659
|
-
|
|
2660
|
-
lastRand = Array.from({ length: 16 }, (_, i) => (b[i] ?? 0) % 32);
|
|
3146
|
+
lastRand = Array.from(randomBytes(16), (b) => b % 32);
|
|
2661
3147
|
}
|
|
2662
3148
|
return encodeTime(now) + lastRand.map((v) => ALPHABET[v]).join("");
|
|
2663
3149
|
}
|
|
@@ -2685,7 +3171,7 @@ function makeEvent(a, ctx) {
|
|
|
2685
3171
|
import {
|
|
2686
3172
|
appendFileSync,
|
|
2687
3173
|
closeSync as closeSync3,
|
|
2688
|
-
existsSync as
|
|
3174
|
+
existsSync as existsSync9,
|
|
2689
3175
|
mkdirSync as mkdirSync5,
|
|
2690
3176
|
openSync as openSync3,
|
|
2691
3177
|
readdirSync as readdirSync6,
|
|
@@ -2696,42 +3182,45 @@ import {
|
|
|
2696
3182
|
unlinkSync as unlinkSync3,
|
|
2697
3183
|
writeFileSync as writeFileSync5
|
|
2698
3184
|
} from "node:fs";
|
|
2699
|
-
import
|
|
3185
|
+
import path14 from "node:path";
|
|
3186
|
+
var READ_CHUNK = 1024 * 1024;
|
|
3187
|
+
var READ_MAX = 8 * 1024 * 1024;
|
|
2700
3188
|
var Outbox = class {
|
|
2701
3189
|
constructor(root) {
|
|
2702
3190
|
this.root = root;
|
|
2703
3191
|
}
|
|
2704
3192
|
root;
|
|
2705
3193
|
dir(harness) {
|
|
2706
|
-
return
|
|
3194
|
+
return path14.join(this.root, "outbox", safeId(harness));
|
|
2707
3195
|
}
|
|
2708
3196
|
file(ref) {
|
|
2709
|
-
return
|
|
3197
|
+
return path14.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
|
|
2710
3198
|
}
|
|
2711
3199
|
seqFile(ref) {
|
|
2712
|
-
return
|
|
3200
|
+
return path14.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
|
|
2713
3201
|
}
|
|
2714
3202
|
/** The next seq for a session, persisted (survives a hibernating machine and a
|
|
2715
3203
|
* restart; an in-memory counter would restart at 0 and collide). */
|
|
2716
3204
|
nextSeq(ref) {
|
|
2717
3205
|
const f = this.seqFile(ref);
|
|
2718
|
-
let n =
|
|
3206
|
+
let n = -1;
|
|
2719
3207
|
try {
|
|
2720
|
-
|
|
3208
|
+
const t = readFileSync8(f, "utf8").trim();
|
|
3209
|
+
if (/^\d+$/.test(t)) n = Number(t);
|
|
2721
3210
|
} catch {
|
|
2722
|
-
n = this.lastSeqInFile(ref) + 1;
|
|
2723
3211
|
}
|
|
2724
|
-
|
|
3212
|
+
if (n < 0) n = this.lastSeqInFile(ref) + 1;
|
|
3213
|
+
mkdirSync5(path14.dirname(f), { recursive: true, mode: 448 });
|
|
2725
3214
|
writeFileSync5(f, String(n + 1), { mode: 384 });
|
|
2726
3215
|
return n;
|
|
2727
3216
|
}
|
|
2728
3217
|
lastSeqInFile(ref) {
|
|
2729
3218
|
const f = this.file(ref);
|
|
2730
|
-
if (!
|
|
3219
|
+
if (!existsSync9(f)) return -1;
|
|
2731
3220
|
const lines = readFileSync8(f, "utf8").trim().split("\n");
|
|
2732
3221
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2733
3222
|
try {
|
|
2734
|
-
const j = JSON.parse(lines[i]
|
|
3223
|
+
const j = JSON.parse(lines[i]);
|
|
2735
3224
|
if (typeof j.session?.seq === "number") return j.session.seq;
|
|
2736
3225
|
} catch {
|
|
2737
3226
|
}
|
|
@@ -2747,10 +3236,10 @@ var Outbox = class {
|
|
|
2747
3236
|
/** Every session file, newest first. */
|
|
2748
3237
|
sessions() {
|
|
2749
3238
|
const out = [];
|
|
2750
|
-
const base =
|
|
2751
|
-
if (!
|
|
3239
|
+
const base = path14.join(this.root, "outbox");
|
|
3240
|
+
if (!existsSync9(base)) return [];
|
|
2752
3241
|
for (const h of readdirSync6(base)) {
|
|
2753
|
-
const d =
|
|
3242
|
+
const d = path14.join(base, h);
|
|
2754
3243
|
let names = [];
|
|
2755
3244
|
try {
|
|
2756
3245
|
names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
|
|
@@ -2759,7 +3248,7 @@ var Outbox = class {
|
|
|
2759
3248
|
}
|
|
2760
3249
|
for (const n of names) {
|
|
2761
3250
|
try {
|
|
2762
|
-
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(
|
|
3251
|
+
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(path14.join(d, n)).mtimeMs });
|
|
2763
3252
|
} catch {
|
|
2764
3253
|
}
|
|
2765
3254
|
}
|
|
@@ -2767,31 +3256,43 @@ var Outbox = class {
|
|
|
2767
3256
|
return out.sort((a, b) => b.mtime - a.mtime).map((x) => x.ref);
|
|
2768
3257
|
}
|
|
2769
3258
|
/** Read events after byte offset `from`; returns them and the new offset. Lines
|
|
2770
|
-
* are read whole; a partial trailing line (a write in progress) is left.
|
|
3259
|
+
* are read whole; a partial trailing line (a write in progress) is left. A read
|
|
3260
|
+
* takes READ_CHUNK bytes at a time, not the rest of the file: a session file grows
|
|
3261
|
+
* to tens of MB and a drain takes it in batches of 50. It reads on until the batch
|
|
3262
|
+
* is full or the file ends, so lines that parse to nothing never stop a drain; one
|
|
3263
|
+
* line longer than the chunk gets a larger one, up to READ_MAX. */
|
|
2771
3264
|
read(ref, from, maxEvents = 500) {
|
|
2772
3265
|
const f = this.file(ref);
|
|
2773
|
-
if (!
|
|
3266
|
+
if (!existsSync9(f)) return { events: [], offset: from };
|
|
2774
3267
|
const size = statSync6(f).size;
|
|
2775
3268
|
if (from >= size) return { events: [], offset: from };
|
|
2776
3269
|
const fd = openSync3(f, "r");
|
|
2777
3270
|
try {
|
|
2778
|
-
const buf = Buffer.alloc(Math.min(size - from, 8 * 1024 * 1024));
|
|
2779
|
-
const n = readSync3(fd, buf, 0, buf.length, from);
|
|
2780
|
-
const text = buf.subarray(0, n).toString("utf8");
|
|
2781
3271
|
const events = [];
|
|
2782
3272
|
let offset = from;
|
|
2783
|
-
let
|
|
2784
|
-
while (events.length < maxEvents) {
|
|
2785
|
-
const
|
|
2786
|
-
|
|
2787
|
-
const
|
|
2788
|
-
pos =
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
3273
|
+
let chunk = READ_CHUNK;
|
|
3274
|
+
while (events.length < maxEvents && offset < size) {
|
|
3275
|
+
const buf = Buffer.alloc(Math.min(size - offset, chunk));
|
|
3276
|
+
const n = readSync3(fd, buf, 0, buf.length, offset);
|
|
3277
|
+
const text = buf.subarray(0, n).toString("utf8");
|
|
3278
|
+
let pos = 0;
|
|
3279
|
+
let used = 0;
|
|
3280
|
+
while (events.length < maxEvents) {
|
|
3281
|
+
const nl = text.indexOf("\n", pos);
|
|
3282
|
+
if (nl < 0) break;
|
|
3283
|
+
const line = text.slice(pos, nl);
|
|
3284
|
+
pos = nl + 1;
|
|
3285
|
+
used += Buffer.byteLength(line, "utf8") + 1;
|
|
3286
|
+
if (!line.trim()) continue;
|
|
3287
|
+
try {
|
|
3288
|
+
events.push(JSON.parse(line));
|
|
3289
|
+
} catch {
|
|
3290
|
+
}
|
|
2794
3291
|
}
|
|
3292
|
+
offset += used;
|
|
3293
|
+
if (used) continue;
|
|
3294
|
+
if (offset + n >= size || chunk >= READ_MAX) break;
|
|
3295
|
+
chunk *= 2;
|
|
2795
3296
|
}
|
|
2796
3297
|
return { events, offset };
|
|
2797
3298
|
} finally {
|
|
@@ -2835,7 +3336,7 @@ var Cursors = class {
|
|
|
2835
3336
|
root;
|
|
2836
3337
|
sink;
|
|
2837
3338
|
file() {
|
|
2838
|
-
return
|
|
3339
|
+
return path14.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
2839
3340
|
}
|
|
2840
3341
|
read() {
|
|
2841
3342
|
try {
|
|
@@ -2846,7 +3347,7 @@ var Cursors = class {
|
|
|
2846
3347
|
}
|
|
2847
3348
|
write(c) {
|
|
2848
3349
|
const f = this.file();
|
|
2849
|
-
mkdirSync5(
|
|
3350
|
+
mkdirSync5(path14.dirname(f), { recursive: true, mode: 448 });
|
|
2850
3351
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
2851
3352
|
writeFileSync5(tmp, JSON.stringify(c), { mode: 384 });
|
|
2852
3353
|
renameSync(tmp, f);
|
|
@@ -2856,11 +3357,14 @@ var Cursors = class {
|
|
|
2856
3357
|
}
|
|
2857
3358
|
};
|
|
2858
3359
|
function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
2859
|
-
|
|
3360
|
+
return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
|
|
3361
|
+
}
|
|
3362
|
+
function takeNamedLock(root, name, staleMs = 5 * 6e4) {
|
|
3363
|
+
const dir = path14.join(root, "locks");
|
|
2860
3364
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
2861
|
-
const f =
|
|
3365
|
+
const f = path14.join(dir, `${name}.lock`);
|
|
2862
3366
|
try {
|
|
2863
|
-
if (Date.now() - statSync6(f).mtimeMs > staleMs) unlinkSync3(f);
|
|
3367
|
+
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync8(f, "utf8")))) unlinkSync3(f);
|
|
2864
3368
|
else return null;
|
|
2865
3369
|
} catch {
|
|
2866
3370
|
}
|
|
@@ -2876,9 +3380,18 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
2876
3380
|
}
|
|
2877
3381
|
};
|
|
2878
3382
|
}
|
|
3383
|
+
function processAlive(pid) {
|
|
3384
|
+
if (!Number.isInteger(pid) || pid <= 0) return true;
|
|
3385
|
+
try {
|
|
3386
|
+
process.kill(pid, 0);
|
|
3387
|
+
return true;
|
|
3388
|
+
} catch (e) {
|
|
3389
|
+
return e.code === "EPERM";
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
2879
3392
|
|
|
2880
3393
|
// src/privacy/pii.ts
|
|
2881
|
-
import { createHash as
|
|
3394
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2882
3395
|
var RULESET_PII = "pii@1";
|
|
2883
3396
|
var EMAIL = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
|
|
2884
3397
|
var PHONE = /(?<![\w.])(?:\+\d{1,3}[ .-]?)?(?:\(\d{2,4}\)[ .-]?)?\d{2,4}(?:[ .-]?\d{2,4}){1,3}(?!\w)/g;
|
|
@@ -2900,7 +3413,7 @@ function reducePii(text, home) {
|
|
|
2900
3413
|
return out;
|
|
2901
3414
|
}
|
|
2902
3415
|
function hashMachine(name) {
|
|
2903
|
-
return
|
|
3416
|
+
return createHash3("sha256").update(name).digest("hex").slice(0, 8);
|
|
2904
3417
|
}
|
|
2905
3418
|
function reducePiiDeep(v, home) {
|
|
2906
3419
|
if (typeof v === "string") return reducePii(v, home);
|
|
@@ -2989,12 +3502,12 @@ function redactAssignments(text) {
|
|
|
2989
3502
|
let last = 0;
|
|
2990
3503
|
NAME_SEP.lastIndex = 0;
|
|
2991
3504
|
for (let m = NAME_SEP.exec(text); m; m = NAME_SEP.exec(text)) {
|
|
2992
|
-
const name = m[1]
|
|
3505
|
+
const name = m[1];
|
|
2993
3506
|
if (!isSecretName(name)) continue;
|
|
2994
3507
|
VALUE.lastIndex = NAME_SEP.lastIndex;
|
|
2995
3508
|
const v = VALUE.exec(text);
|
|
2996
3509
|
if (!v) continue;
|
|
2997
|
-
const value = v[1] ?? v[2] ?? v[3] ?? v[4]
|
|
3510
|
+
const value = v[1] ?? v[2] ?? v[3] ?? v[4];
|
|
2998
3511
|
NAME_SEP.lastIndex = VALUE.lastIndex;
|
|
2999
3512
|
if (isPlaceholder(value) || value.includes(REDACTED)) continue;
|
|
3000
3513
|
if (/tokens?$/i.test(name) && /^\d+(?:[.,]\d+)*[km]?$/i.test(value)) continue;
|
|
@@ -3050,7 +3563,7 @@ function redactSecrets(text) {
|
|
|
3050
3563
|
let out = text;
|
|
3051
3564
|
for (const re of TOKEN_PATTERNS) out = out.replace(re, REDACTED);
|
|
3052
3565
|
out = out.replace(URL_CREDENTIALS, (_m, head, _pw, at) => `${head}${REDACTED}${at}`);
|
|
3053
|
-
out = out.replace(AUTH_HEADER, (
|
|
3566
|
+
out = out.replace(AUTH_HEADER, (_m, head) => `${head}${REDACTED}`);
|
|
3054
3567
|
out = redactAssignments(out);
|
|
3055
3568
|
out = out.replace(
|
|
3056
3569
|
PROSE,
|
|
@@ -3082,6 +3595,7 @@ var TYPE_TIER = {
|
|
|
3082
3595
|
"turn.started": 0,
|
|
3083
3596
|
"turn.ended": 0,
|
|
3084
3597
|
"turn.transcript": 2,
|
|
3598
|
+
"turn.progress": 2,
|
|
3085
3599
|
"tool.started": 1,
|
|
3086
3600
|
"tool.ended": 1,
|
|
3087
3601
|
"files.changed": 1,
|
|
@@ -3101,6 +3615,7 @@ var DATA_ALLOW = {
|
|
|
3101
3615
|
"turn.started": { 0: ["turn_id"], 1: ["prompt_chars"] },
|
|
3102
3616
|
"turn.ended": { 0: ["turn_id", "reason", "ms"], 1: ["error"] },
|
|
3103
3617
|
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
3618
|
+
"turn.progress": { 2: ["turn", "user", "assistant", "at"] },
|
|
3104
3619
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
3105
3620
|
"tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
|
|
3106
3621
|
"files.changed": { 1: ["paths"] },
|
|
@@ -3141,10 +3656,12 @@ function cap(v, max) {
|
|
|
3141
3656
|
}
|
|
3142
3657
|
return v;
|
|
3143
3658
|
}
|
|
3659
|
+
function capTail(v, max) {
|
|
3660
|
+
return v.length > max ? `\u2026${v.slice(v.length - max + 1)}` : v;
|
|
3661
|
+
}
|
|
3144
3662
|
function filterEvent(event, tier, pii, opts = {}) {
|
|
3145
3663
|
const minTier = TYPE_TIER[event.type];
|
|
3146
3664
|
if (minTier !== void 0 && minTier > tier) return null;
|
|
3147
|
-
if (event.type === "native" && tier < 3) return null;
|
|
3148
3665
|
const session = {};
|
|
3149
3666
|
for (const k of SESSION_ALLOW)
|
|
3150
3667
|
if (event.session[k] !== void 0)
|
|
@@ -3154,7 +3671,8 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3154
3671
|
if (keys === null) data = { ...event.data };
|
|
3155
3672
|
else for (const k of keys) if (event.data[k] !== void 0) data[k] = event.data[k];
|
|
3156
3673
|
if (typeof data.user === "string") data.user = cap(data.user, MAX_SIDE);
|
|
3157
|
-
if (typeof data.assistant === "string")
|
|
3674
|
+
if (typeof data.assistant === "string")
|
|
3675
|
+
data.assistant = event.type === "turn.progress" ? capTail(data.assistant, MAX_SIDE) : cap(data.assistant, MAX_SIDE);
|
|
3158
3676
|
if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
|
|
3159
3677
|
if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
|
|
3160
3678
|
if (typeof data.message === "string") data.message = cap(data.message, 200);
|
|
@@ -3175,9 +3693,15 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3175
3693
|
return out;
|
|
3176
3694
|
}
|
|
3177
3695
|
|
|
3696
|
+
// src/short-id.ts
|
|
3697
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3698
|
+
function shortId(harness, id) {
|
|
3699
|
+
return createHash4("sha256").update(`sessionpipe.session:${harness}:${id}`).digest("hex").slice(0, 8);
|
|
3700
|
+
}
|
|
3701
|
+
|
|
3178
3702
|
// src/sinks/file.ts
|
|
3179
3703
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3180
|
-
import
|
|
3704
|
+
import path15 from "node:path";
|
|
3181
3705
|
var FileSink = class {
|
|
3182
3706
|
constructor(name, file, tier, pii) {
|
|
3183
3707
|
this.name = name;
|
|
@@ -3191,7 +3715,7 @@ var FileSink = class {
|
|
|
3191
3715
|
pii;
|
|
3192
3716
|
async send(events) {
|
|
3193
3717
|
try {
|
|
3194
|
-
mkdirSync6(
|
|
3718
|
+
mkdirSync6(path15.dirname(this.file), { recursive: true });
|
|
3195
3719
|
appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
3196
3720
|
`);
|
|
3197
3721
|
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
@@ -3215,8 +3739,12 @@ var StdoutSink = class {
|
|
|
3215
3739
|
pii;
|
|
3216
3740
|
write;
|
|
3217
3741
|
async send(events) {
|
|
3218
|
-
|
|
3219
|
-
|
|
3742
|
+
try {
|
|
3743
|
+
for (const e of events) this.write(JSON.stringify(e));
|
|
3744
|
+
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
3745
|
+
} catch (e) {
|
|
3746
|
+
return { ok: false, acknowledged: 0, error: String(e.message ?? e), action: "retry" };
|
|
3747
|
+
}
|
|
3220
3748
|
}
|
|
3221
3749
|
};
|
|
3222
3750
|
|
|
@@ -3227,8 +3755,8 @@ export {
|
|
|
3227
3755
|
AG_STALE_PRETOOL,
|
|
3228
3756
|
Cursors,
|
|
3229
3757
|
DEFAULT_CONFIG,
|
|
3758
|
+
DEVIN_EVENTS,
|
|
3230
3759
|
FileSink,
|
|
3231
|
-
HOME,
|
|
3232
3760
|
JOB_TIMEOUT_MS,
|
|
3233
3761
|
Outbox,
|
|
3234
3762
|
PROTOCOL_VERSION,
|
|
@@ -3241,6 +3769,8 @@ export {
|
|
|
3241
3769
|
antigravity,
|
|
3242
3770
|
antigravityAnswer,
|
|
3243
3771
|
antigravity_control_exports as antigravityControl,
|
|
3772
|
+
capTail,
|
|
3773
|
+
claudeAccounts,
|
|
3244
3774
|
claudeCode,
|
|
3245
3775
|
claude_code_control_exports as claudeControl,
|
|
3246
3776
|
claudeDirs,
|
|
@@ -3248,23 +3778,30 @@ export {
|
|
|
3248
3778
|
codex_control_exports as codexControl,
|
|
3249
3779
|
configFile,
|
|
3250
3780
|
deniedNote,
|
|
3781
|
+
devin,
|
|
3782
|
+
devinConfigDir,
|
|
3783
|
+
devinDataDir,
|
|
3251
3784
|
drivableHarnesses,
|
|
3252
3785
|
filterEvent,
|
|
3253
3786
|
geminiCli,
|
|
3254
3787
|
gitFacts,
|
|
3255
3788
|
hasSecret,
|
|
3256
3789
|
hashMachine,
|
|
3790
|
+
homeDir,
|
|
3257
3791
|
isInjected,
|
|
3258
3792
|
isSecretName,
|
|
3259
3793
|
isUlid,
|
|
3260
3794
|
jobEnv,
|
|
3261
3795
|
machineName,
|
|
3262
3796
|
makeEvent,
|
|
3797
|
+
notClaudeCode,
|
|
3263
3798
|
pairTurns,
|
|
3799
|
+
processAlive,
|
|
3264
3800
|
readAntigravity,
|
|
3265
3801
|
readClaude,
|
|
3266
3802
|
readCodex,
|
|
3267
3803
|
readConfig,
|
|
3804
|
+
readDevinChain,
|
|
3268
3805
|
readGemini,
|
|
3269
3806
|
redactDeep,
|
|
3270
3807
|
redactSecrets,
|
|
@@ -3272,9 +3809,11 @@ export {
|
|
|
3272
3809
|
reducePiiDeep,
|
|
3273
3810
|
runHeadless,
|
|
3274
3811
|
safeId,
|
|
3812
|
+
shortId,
|
|
3275
3813
|
stateDir,
|
|
3276
3814
|
stripCredentials,
|
|
3277
3815
|
takeLock,
|
|
3816
|
+
takeNamedLock,
|
|
3278
3817
|
tilde,
|
|
3279
3818
|
ulid,
|
|
3280
3819
|
writeConfig
|