sessionpipe-conformance 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +211 -96
- package/dist/cli.js.map +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +195 -93
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAuBD,oGAAoG;AACpG,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAuBD,oGAAoG;AACpG,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwDtE"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// sessionpipe-conformance 0.
|
|
1
|
+
// sessionpipe-conformance 0.13.1 · Apache-2.0 · https://sessionpipe.org
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
@@ -10,7 +10,7 @@ var homeDir = () => os.homedir();
|
|
|
10
10
|
// ../core/src/adapters/claude-code.ts
|
|
11
11
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
|
-
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2
|
|
13
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
14
14
|
import { userInfo } from "node:os";
|
|
15
15
|
import path3 from "node:path";
|
|
16
16
|
|
|
@@ -162,7 +162,14 @@ ${m.text}`;
|
|
|
162
162
|
continue;
|
|
163
163
|
}
|
|
164
164
|
if (cur) turns.push(cur);
|
|
165
|
-
cur = {
|
|
165
|
+
cur = {
|
|
166
|
+
user: m.text,
|
|
167
|
+
assistant: "",
|
|
168
|
+
at: m.at,
|
|
169
|
+
startLine: m.line,
|
|
170
|
+
endLine: m.line,
|
|
171
|
+
...m.injected ? { injected: true } : {}
|
|
172
|
+
};
|
|
166
173
|
} else {
|
|
167
174
|
if (!cur) cur = { user: "", assistant: "", at: m.at, startLine: m.line, endLine: m.line };
|
|
168
175
|
cur.assistant += (cur.assistant ? "\n\n" : "") + m.text;
|
|
@@ -172,10 +179,17 @@ ${m.text}`;
|
|
|
172
179
|
if (cur?.assistant) turns.push(cur);
|
|
173
180
|
const line = cur && !cur.assistant ? cur.startLine : endLine;
|
|
174
181
|
const out = {
|
|
175
|
-
turns: turns.map((t) => ({
|
|
182
|
+
turns: turns.map((t) => ({
|
|
183
|
+
user: t.user,
|
|
184
|
+
assistant: t.assistant,
|
|
185
|
+
at: t.at,
|
|
186
|
+
endLine: t.endLine,
|
|
187
|
+
...t.injected ? { injected: true } : {}
|
|
188
|
+
})),
|
|
176
189
|
line
|
|
177
190
|
};
|
|
178
|
-
if (cur && !cur.assistant && cur.user)
|
|
191
|
+
if (cur && !cur.assistant && cur.user)
|
|
192
|
+
out.open = { user: cur.user, at: cur.at, ...cur.injected ? { injected: true } : {} };
|
|
179
193
|
return out;
|
|
180
194
|
}
|
|
181
195
|
var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
|
|
@@ -198,27 +212,51 @@ var textParts = (c, kinds) => {
|
|
|
198
212
|
(b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
|
|
199
213
|
).map((b) => b.text).join("\n");
|
|
200
214
|
};
|
|
215
|
+
var SPOKEN = /* @__PURE__ */ new Set(["human", "peer"]);
|
|
216
|
+
var PEER_INTRO = /sent a message[^\n]*:$/;
|
|
217
|
+
var PEER_OUTRO = "\n\nThis came from another ";
|
|
218
|
+
var ENVELOPE = /^<cross-session-message\b[^>]*>\n([\s\S]*)\n<\/cross-session-message>$/;
|
|
219
|
+
function peerAsk(origin, text) {
|
|
220
|
+
const body = origin?.body;
|
|
221
|
+
let t = typeof body === "string" && body.trim() ? body : text;
|
|
222
|
+
if (t !== body) {
|
|
223
|
+
const nl = t.indexOf("\n");
|
|
224
|
+
if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);
|
|
225
|
+
const outro = t.indexOf(PEER_OUTRO);
|
|
226
|
+
if (outro > 0) t = t.slice(0, outro);
|
|
227
|
+
}
|
|
228
|
+
return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();
|
|
229
|
+
}
|
|
201
230
|
var queuedPrompt = (a) => {
|
|
202
231
|
if (!a || typeof a !== "object") return "";
|
|
203
232
|
const q = a;
|
|
204
233
|
if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
|
|
205
|
-
|
|
206
|
-
|
|
234
|
+
const kind = q.origin?.kind ?? "human";
|
|
235
|
+
if (!SPOKEN.has(kind)) return "";
|
|
236
|
+
const text = textParts(q.prompt, ["text"]).trim();
|
|
237
|
+
return kind === "peer" ? peerAsk(q.origin, text) : text;
|
|
207
238
|
};
|
|
239
|
+
var channelText = (text) => /^<channel\b[^>]*>([\s\S]*)<\/channel>$/.exec(text)?.[1]?.trim() ?? "";
|
|
208
240
|
function readClaude(lines, from) {
|
|
209
241
|
const msgs = [];
|
|
210
242
|
for (let i = from; i < lines.length; i++) {
|
|
211
243
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
212
244
|
if (!j || j.isSidechain) continue;
|
|
213
245
|
const origin = j.origin;
|
|
214
|
-
|
|
215
|
-
|
|
246
|
+
const channel = j.type === "user" && origin?.kind === "channel";
|
|
247
|
+
const peer = origin?.kind === "peer";
|
|
248
|
+
if (j.type === "user" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;
|
|
249
|
+
if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) continue;
|
|
216
250
|
const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
|
|
217
251
|
const c = j.message?.content;
|
|
218
|
-
if (
|
|
219
|
-
const text = textParts(c, ["text"]).trim();
|
|
252
|
+
if (channel) {
|
|
253
|
+
const text = channelText(textParts(c, ["text"]).trim());
|
|
254
|
+
if (text) msgs.push({ role: "user", text, at, line: i, injected: true });
|
|
255
|
+
} else if (j.type === "user") {
|
|
256
|
+
const raw = textParts(c, ["text"]).trim();
|
|
257
|
+
const text = peer ? peerAsk(origin, raw) : raw;
|
|
220
258
|
if (!text || isInjected(text)) continue;
|
|
221
|
-
msgs.push({ role: "user", text, at, line: i });
|
|
259
|
+
msgs.push({ role: "user", text, at, line: i, ...peer ? { injected: true } : {} });
|
|
222
260
|
} else if (j.type === "assistant" && Array.isArray(c)) {
|
|
223
261
|
const text = textParts(c, ["text"]).trim();
|
|
224
262
|
if (text) msgs.push({ role: "assistant", text, at, line: i });
|
|
@@ -330,6 +368,41 @@ function readJson(file) {
|
|
|
330
368
|
const f = readJsonFile(file);
|
|
331
369
|
return f.ok ? f.value : {};
|
|
332
370
|
}
|
|
371
|
+
function refused(file, e, what, existed) {
|
|
372
|
+
const { code, syscall } = e;
|
|
373
|
+
const why = `can't ${what} it (${code ?? String(e)})`;
|
|
374
|
+
if (what === "remove" || syscall === "open" || syscall === "mkdir")
|
|
375
|
+
return { file, changed: false, skipped: true, note: `${why}; not touched` };
|
|
376
|
+
return {
|
|
377
|
+
file,
|
|
378
|
+
changed: true,
|
|
379
|
+
skipped: true,
|
|
380
|
+
...existed ? {} : { created: true },
|
|
381
|
+
note: `${why} part way; check the file before the harness reads it`
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function writeBack(file, text, existed = true) {
|
|
385
|
+
try {
|
|
386
|
+
mkdirSync(path2.dirname(file), { recursive: true });
|
|
387
|
+
writeFileSync(file, text);
|
|
388
|
+
return null;
|
|
389
|
+
} catch (e) {
|
|
390
|
+
return refused(file, e, "write", existed);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function removeEmptied(file, empty) {
|
|
394
|
+
try {
|
|
395
|
+
unlinkSync(file);
|
|
396
|
+
return { file, changed: true, note: "removed (sessionpipe created it)" };
|
|
397
|
+
} catch (e) {
|
|
398
|
+
const bad = refused(file, e, "remove", true);
|
|
399
|
+
return writeBack(file, empty) ?? {
|
|
400
|
+
file,
|
|
401
|
+
changed: true,
|
|
402
|
+
note: `emptied; ${bad.note.replace(/; not touched$/, "")}, so the empty file stays`
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
333
406
|
function entryFor(event, harness, cmd, o) {
|
|
334
407
|
const matcher = o.matcherFor(event);
|
|
335
408
|
return {
|
|
@@ -371,8 +444,8 @@ function installClaudeShaped(file, harness, events, cmd, o) {
|
|
|
371
444
|
}
|
|
372
445
|
if (changed) {
|
|
373
446
|
settings2.hooks = hooks;
|
|
374
|
-
|
|
375
|
-
|
|
447
|
+
const bad = writeBack(file, formatJson(settings2, f), f.existed);
|
|
448
|
+
if (bad) return bad;
|
|
376
449
|
}
|
|
377
450
|
return { file, changed, created: !f.existed && changed };
|
|
378
451
|
}
|
|
@@ -395,12 +468,8 @@ function uninstallClaudeShaped(file, events, opts = {}) {
|
|
|
395
468
|
}
|
|
396
469
|
if (!changed) return { file, changed: false };
|
|
397
470
|
if (!Object.keys(hooks).length) delete settings2.hooks;
|
|
398
|
-
if (opts.created && !Object.keys(settings2).length)
|
|
399
|
-
|
|
400
|
-
return { file, changed: true, note: "removed (sessionpipe created it)" };
|
|
401
|
-
}
|
|
402
|
-
writeFileSync(file, formatJson(settings2, f));
|
|
403
|
-
return { file, changed: true };
|
|
471
|
+
if (opts.created && !Object.keys(settings2).length) return removeEmptied(file, formatJson(settings2, f));
|
|
472
|
+
return writeBack(file, formatJson(settings2, f)) ?? { file, changed: true };
|
|
404
473
|
}
|
|
405
474
|
function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
406
475
|
const f = readJsonFile(file);
|
|
@@ -479,7 +548,7 @@ var claudeCode = {
|
|
|
479
548
|
configFiles: (env = process.env) => claudeDirs(env).map(settingsIn),
|
|
480
549
|
install: (cmd, env = process.env, opts = {}) => claudeDirs(env).flatMap((d) => {
|
|
481
550
|
const hooks = installClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS);
|
|
482
|
-
const line = opts.statusLine === void 0 ? null : statusLine(settingsIn(d), opts.statusLine, cmd);
|
|
551
|
+
const line = opts.statusLine === void 0 || hooks.skipped ? null : statusLine(settingsIn(d), opts.statusLine, cmd);
|
|
483
552
|
return line ? [hooks, line] : [hooks];
|
|
484
553
|
}),
|
|
485
554
|
uninstall: (env = process.env, opts = {}) => claudeDirs(env).map((d) => {
|
|
@@ -487,7 +556,9 @@ var claudeCode = {
|
|
|
487
556
|
const hooks = uninstallClaudeShaped(settingsIn(d), CLAUDE_EVENTS, {
|
|
488
557
|
created: opts.created?.includes(settingsIn(d))
|
|
489
558
|
});
|
|
490
|
-
|
|
559
|
+
if (line?.changed && !line.skipped && hooks.skipped)
|
|
560
|
+
return { ...hooks, note: `status line removed, hooks not: ${hooks.note?.replace(/; not touched$/, "")}` };
|
|
561
|
+
return line && !hooks.changed && !hooks.skipped ? line : hooks;
|
|
491
562
|
}),
|
|
492
563
|
installed: (cmd, env = process.env, opts = {}) => claudeDirs(env).map((d) => ({
|
|
493
564
|
file: settingsIn(d),
|
|
@@ -582,7 +653,8 @@ var claudeCode = {
|
|
|
582
653
|
attention_id: call_id ?? permissionId(),
|
|
583
654
|
kind: "permission",
|
|
584
655
|
tool,
|
|
585
|
-
message: tool ? `${tool}${describe(s.tool_input)}` : void 0
|
|
656
|
+
message: tool ? `${tool}${describe(s.tool_input)}` : void 0,
|
|
657
|
+
agent_id
|
|
586
658
|
})
|
|
587
659
|
);
|
|
588
660
|
break;
|
|
@@ -591,7 +663,8 @@ var claudeCode = {
|
|
|
591
663
|
ev("attention.needed", {
|
|
592
664
|
attention_id: `notif-${Date.now()}`,
|
|
593
665
|
kind: notifKind(s.notification_type),
|
|
594
|
-
message: typeof s.message === "string" ? s.message.slice(0, 200) : void 0
|
|
666
|
+
message: typeof s.message === "string" ? s.message.slice(0, 200) : void 0,
|
|
667
|
+
agent_id
|
|
595
668
|
})
|
|
596
669
|
);
|
|
597
670
|
break;
|
|
@@ -716,8 +789,11 @@ function statusLine(file, want, cmd) {
|
|
|
716
789
|
if (want ? ours && cur?.command === command && cur?.refreshInterval === LINE_REFRESH_S : !ours) return null;
|
|
717
790
|
if (want) f.value.statusLine = { type: "command", command, refreshInterval: LINE_REFRESH_S };
|
|
718
791
|
else delete f.value.statusLine;
|
|
719
|
-
|
|
720
|
-
|
|
792
|
+
return writeBack(file, formatJson(f.value, f), f.existed) ?? {
|
|
793
|
+
file,
|
|
794
|
+
changed: true,
|
|
795
|
+
note: want ? "status line written (account limits)" : "status line removed"
|
|
796
|
+
};
|
|
721
797
|
}
|
|
722
798
|
var epochIso = (t) => typeof t === "number" && t > 0 && t < 1e11 ? new Date(t * 1e3).toISOString() : void 0;
|
|
723
799
|
var WINDOW_MINUTES = { five_hour: 300, seven_day: 10080 };
|
|
@@ -1134,6 +1210,34 @@ async function claudeAiTitle(bridge, token) {
|
|
|
1134
1210
|
// ../core/src/adapters/headless.ts
|
|
1135
1211
|
import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
1136
1212
|
import path4 from "node:path";
|
|
1213
|
+
|
|
1214
|
+
// ../core/src/launched-by.ts
|
|
1215
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
1216
|
+
var DELEGATED_BY_ENV = "SESSIONPIPE_ASKED_BY";
|
|
1217
|
+
var SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
1218
|
+
function refIn(env, name) {
|
|
1219
|
+
const v = env[name]?.trim();
|
|
1220
|
+
return v && SESSION_REF.test(v) ? v : void 0;
|
|
1221
|
+
}
|
|
1222
|
+
function launchedBy(env) {
|
|
1223
|
+
return refIn(env, LAUNCHED_BY_ENV);
|
|
1224
|
+
}
|
|
1225
|
+
function delegatedBy(env) {
|
|
1226
|
+
return refIn(env, DELEGATED_BY_ENV);
|
|
1227
|
+
}
|
|
1228
|
+
function readHook(adapter, input) {
|
|
1229
|
+
const r = adapter.fromHook(input);
|
|
1230
|
+
if (r && !r.session.parent_id) {
|
|
1231
|
+
const self = `${adapter.name}:${r.session.id}`;
|
|
1232
|
+
const launcher = launchedBy(input.env);
|
|
1233
|
+
if (launcher && launcher !== self) r.session.launched_by = launcher;
|
|
1234
|
+
const asker = delegatedBy(input.env);
|
|
1235
|
+
if (asker && asker !== self) r.session.delegated_by = asker;
|
|
1236
|
+
}
|
|
1237
|
+
return r;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// ../core/src/adapters/headless.ts
|
|
1137
1241
|
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
1138
1242
|
var OUT_CAP = 1024 * 1024;
|
|
1139
1243
|
function isExe(p) {
|
|
@@ -1181,16 +1285,7 @@ function probe(bin, args) {
|
|
|
1181
1285
|
}
|
|
1182
1286
|
|
|
1183
1287
|
// ../core/src/adapters/antigravity.ts
|
|
1184
|
-
import {
|
|
1185
|
-
existsSync as existsSync3,
|
|
1186
|
-
mkdirSync as mkdirSync2,
|
|
1187
|
-
readdirSync as readdirSync3,
|
|
1188
|
-
readFileSync as readFileSync4,
|
|
1189
|
-
realpathSync,
|
|
1190
|
-
statSync as statSync4,
|
|
1191
|
-
unlinkSync as unlinkSync2,
|
|
1192
|
-
writeFileSync as writeFileSync3
|
|
1193
|
-
} from "node:fs";
|
|
1288
|
+
import { existsSync as existsSync3, readdirSync as readdirSync3, readFileSync as readFileSync4, realpathSync, statSync as statSync4 } from "node:fs";
|
|
1194
1289
|
import path5 from "node:path";
|
|
1195
1290
|
var NAME2 = "antigravity";
|
|
1196
1291
|
var HOOK_NAME = "sessionpipe";
|
|
@@ -1222,10 +1317,8 @@ var antigravity = {
|
|
|
1222
1317
|
const e = entry(cmd);
|
|
1223
1318
|
if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];
|
|
1224
1319
|
hooks[HOOK_NAME] = e;
|
|
1225
|
-
mkdirSync2(path5.dirname(file), { recursive: true });
|
|
1226
|
-
writeFileSync3(file, formatJson(hooks, f));
|
|
1227
1320
|
return [
|
|
1228
|
-
{
|
|
1321
|
+
writeBack(file, formatJson(hooks, f), f.existed) ?? {
|
|
1229
1322
|
file,
|
|
1230
1323
|
changed: true,
|
|
1231
1324
|
created: !f.existed,
|
|
@@ -1241,12 +1334,8 @@ var antigravity = {
|
|
|
1241
1334
|
const hooks = f.value;
|
|
1242
1335
|
if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];
|
|
1243
1336
|
delete hooks[HOOK_NAME];
|
|
1244
|
-
if (opts.created?.includes(file) && !Object.keys(hooks).length)
|
|
1245
|
-
|
|
1246
|
-
return [{ file, changed: true, note: "removed (sessionpipe created it)" }];
|
|
1247
|
-
}
|
|
1248
|
-
writeFileSync3(file, formatJson(hooks, f));
|
|
1249
|
-
return [{ file, changed: true }];
|
|
1337
|
+
if (opts.created?.includes(file) && !Object.keys(hooks).length) return [removeEmptied(file, formatJson(hooks, f))];
|
|
1338
|
+
return [writeBack(file, formatJson(hooks, f)) ?? { file, changed: true }];
|
|
1250
1339
|
},
|
|
1251
1340
|
installed(cmd, env = process.env) {
|
|
1252
1341
|
const file = hooksFile(env);
|
|
@@ -1469,7 +1558,7 @@ function antigravityFacts(file, opts) {
|
|
|
1469
1558
|
}
|
|
1470
1559
|
|
|
1471
1560
|
// ../core/src/adapters/codex.ts
|
|
1472
|
-
import { existsSync as existsSync4,
|
|
1561
|
+
import { existsSync as existsSync4, readdirSync as readdirSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
1473
1562
|
import path6 from "node:path";
|
|
1474
1563
|
|
|
1475
1564
|
// ../core/src/adapters/codex-config.ts
|
|
@@ -1622,29 +1711,30 @@ var configFile = (env) => path6.join(codexHome(env), "config.toml");
|
|
|
1622
1711
|
var sessionsDir = (env) => path6.join(codexHome(env), "sessions");
|
|
1623
1712
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1624
1713
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
1714
|
+
var UNTIL_APPROVED = "Codex will report nothing until you approve the hooks: start `codex` and accept the hook review";
|
|
1625
1715
|
function installNotify(cmd, env) {
|
|
1626
1716
|
const file = configFile(env);
|
|
1627
1717
|
let text = "";
|
|
1718
|
+
let existed = true;
|
|
1628
1719
|
try {
|
|
1629
1720
|
text = readFileSync5(file, "utf8");
|
|
1630
|
-
} catch {
|
|
1721
|
+
} catch (e) {
|
|
1722
|
+
existed = e.code !== "ENOENT";
|
|
1631
1723
|
}
|
|
1632
1724
|
const argv = cmd([NAME3, "notify"]).argv;
|
|
1633
1725
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
1634
1726
|
const n = readNotify(text);
|
|
1635
|
-
const write = (next) => {
|
|
1636
|
-
|
|
1637
|
-
|
|
1727
|
+
const write = (next, note) => {
|
|
1728
|
+
const bad = writeBack(file, next, existed);
|
|
1729
|
+
return bad ? { ...bad, note: `${bad.note}. ${UNTIL_APPROVED}` } : { file, changed: true, note };
|
|
1638
1730
|
};
|
|
1639
1731
|
if (n && !n.other) {
|
|
1640
1732
|
if (!n.misplaced && JSON.stringify(n.argv) === JSON.stringify(argv))
|
|
1641
1733
|
return { file, changed: false, note: "notify current" };
|
|
1642
|
-
write(
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
note: n.misplaced ? "notify moved to the top level (it sat inside a [table], where Codex never reads it)" : "notify updated"
|
|
1647
|
-
};
|
|
1734
|
+
return write(
|
|
1735
|
+
withNotify(text, line),
|
|
1736
|
+
n.misplaced ? "notify moved to the top level (it sat inside a [table], where Codex never reads it)" : "notify updated"
|
|
1737
|
+
);
|
|
1648
1738
|
}
|
|
1649
1739
|
if (n?.other) {
|
|
1650
1740
|
const prev = previousNotify(n.argv);
|
|
@@ -1653,20 +1743,20 @@ function installNotify(cmd, env) {
|
|
|
1653
1743
|
if (JSON.stringify(prev) === JSON.stringify(argv))
|
|
1654
1744
|
return { file, changed: false, note: "notify current (after Codex Computer Use)" };
|
|
1655
1745
|
}
|
|
1656
|
-
if (!prev || OURS2.test(JSON.stringify(prev)))
|
|
1657
|
-
write(
|
|
1658
|
-
|
|
1659
|
-
|
|
1746
|
+
if (!prev || OURS2.test(JSON.stringify(prev)))
|
|
1747
|
+
return write(
|
|
1748
|
+
replaceNotify(text, n, `notify = ${withPreviousRaw(n.raw, argv)}`),
|
|
1749
|
+
"chained after Codex Computer Use (--previous-notify)"
|
|
1750
|
+
);
|
|
1660
1751
|
}
|
|
1661
1752
|
return {
|
|
1662
1753
|
file,
|
|
1663
1754
|
changed: false,
|
|
1664
1755
|
skipped: true,
|
|
1665
|
-
note: `notify belongs to ${(n.argv?.[0] ?? n.raw).replace(/^.*\//, "").slice(0, 40)}; left alone.
|
|
1756
|
+
note: `notify belongs to ${(n.argv?.[0] ?? n.raw).replace(/^.*\//, "").slice(0, 40)}; left alone. ${UNTIL_APPROVED}`
|
|
1666
1757
|
};
|
|
1667
1758
|
}
|
|
1668
|
-
write(withNotify(text, line));
|
|
1669
|
-
return { file, changed: true, note: "notify set at the top level" };
|
|
1759
|
+
return write(withNotify(text, line), "notify set at the top level");
|
|
1670
1760
|
}
|
|
1671
1761
|
function uninstallNotify(env) {
|
|
1672
1762
|
const file = configFile(env);
|
|
@@ -1678,15 +1768,13 @@ function uninstallNotify(env) {
|
|
|
1678
1768
|
}
|
|
1679
1769
|
const n = readNotify(text);
|
|
1680
1770
|
if (!n) return { file, changed: false };
|
|
1681
|
-
if (!n.other) {
|
|
1682
|
-
writeFileSync4(file, withoutNotify(text));
|
|
1683
|
-
return { file, changed: true };
|
|
1684
|
-
}
|
|
1771
|
+
if (!n.other) return writeBack(file, withoutNotify(text)) ?? { file, changed: true };
|
|
1685
1772
|
const prev = previousNotify(n.argv);
|
|
1686
|
-
if (isComputerUse(n.argv) && prev && OURS2.test(JSON.stringify(prev)))
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1773
|
+
if (isComputerUse(n.argv) && prev && OURS2.test(JSON.stringify(prev)))
|
|
1774
|
+
return writeBack(file, replaceNotify(text, n, `notify = ${stripPreviousRaw(n.raw)}`)) ?? {
|
|
1775
|
+
file,
|
|
1776
|
+
changed: true
|
|
1777
|
+
};
|
|
1690
1778
|
return { file, changed: false };
|
|
1691
1779
|
}
|
|
1692
1780
|
function findRollout(threadId, env = process.env) {
|
|
@@ -1718,8 +1806,12 @@ function rolloutMeta(file) {
|
|
|
1718
1806
|
return scanLines(file, (line) => parseLines([line])[0] ?? {});
|
|
1719
1807
|
}
|
|
1720
1808
|
function ofAnotherThread(file, id) {
|
|
1809
|
+
const other = threadOf(file);
|
|
1810
|
+
return !!other && other !== id.toLowerCase();
|
|
1811
|
+
}
|
|
1812
|
+
function threadOf(file) {
|
|
1721
1813
|
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);
|
|
1722
|
-
return
|
|
1814
|
+
return m?.[1] ? m[1].toLowerCase() : null;
|
|
1723
1815
|
}
|
|
1724
1816
|
var codex = {
|
|
1725
1817
|
name: NAME3,
|
|
@@ -1798,6 +1890,8 @@ var codex = {
|
|
|
1798
1890
|
const turn_id = typeof s.turn_id === "string" ? s.turn_id : void 0;
|
|
1799
1891
|
const tool = validTool3(s.tool_name) ? s.tool_name : void 0;
|
|
1800
1892
|
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
1893
|
+
const thread = typeof s.transcript_path === "string" ? threadOf(s.transcript_path) : null;
|
|
1894
|
+
const agent_id = typeof s.agent_id === "string" && s.agent_id ? s.agent_id : typeof s.subagent_id === "string" && s.subagent_id ? s.subagent_id : thread && thread !== String(id).toLowerCase() ? thread : void 0;
|
|
1801
1895
|
const ev = (type, data) => ({ type, data: prune3(data), harnessEvent: event });
|
|
1802
1896
|
const events = [];
|
|
1803
1897
|
switch (event) {
|
|
@@ -1817,17 +1911,30 @@ var codex = {
|
|
|
1817
1911
|
);
|
|
1818
1912
|
break;
|
|
1819
1913
|
case "PreToolUse":
|
|
1820
|
-
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, input: s.tool_input }));
|
|
1914
|
+
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, agent_id, input: s.tool_input }));
|
|
1821
1915
|
break;
|
|
1822
1916
|
case "PostToolUse":
|
|
1823
1917
|
if (tool)
|
|
1824
1918
|
events.push(
|
|
1825
|
-
ev("tool.ended", {
|
|
1919
|
+
ev("tool.ended", {
|
|
1920
|
+
tool,
|
|
1921
|
+
call_id,
|
|
1922
|
+
turn_id,
|
|
1923
|
+
agent_id,
|
|
1924
|
+
ok: true,
|
|
1925
|
+
input: s.tool_input,
|
|
1926
|
+
output: s.tool_response
|
|
1927
|
+
})
|
|
1826
1928
|
);
|
|
1827
1929
|
break;
|
|
1828
1930
|
case "PermissionRequest":
|
|
1829
1931
|
events.push(
|
|
1830
|
-
ev("attention.needed", {
|
|
1932
|
+
ev("attention.needed", {
|
|
1933
|
+
attention_id: call_id ?? `perm-${Date.now()}`,
|
|
1934
|
+
kind: "permission",
|
|
1935
|
+
tool,
|
|
1936
|
+
agent_id
|
|
1937
|
+
})
|
|
1831
1938
|
);
|
|
1832
1939
|
break;
|
|
1833
1940
|
case "Stop":
|
|
@@ -2317,7 +2424,7 @@ var CHAIN_LIMIT = 4e3;
|
|
|
2317
2424
|
var CHAIN_SQL = `WITH RECURSIVE chain(node_id, parent_node_id, chat_message, created_at) AS (
|
|
2318
2425
|
SELECT node_id, parent_node_id, chat_message, created_at FROM message_nodes
|
|
2319
2426
|
WHERE session_id = ? AND node_id = (SELECT main_chain_id FROM sessions WHERE id = ?)
|
|
2320
|
-
UNION
|
|
2427
|
+
UNION
|
|
2321
2428
|
SELECT m.node_id, m.parent_node_id, m.chat_message, m.created_at FROM message_nodes m
|
|
2322
2429
|
JOIN chain c ON m.node_id = c.parent_node_id AND m.session_id = ? AND m.node_id >= ?
|
|
2323
2430
|
)
|
|
@@ -2585,22 +2692,6 @@ function prune5(o) {
|
|
|
2585
2692
|
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
2586
2693
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
2587
2694
|
|
|
2588
|
-
// ../core/src/launched-by.ts
|
|
2589
|
-
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
2590
|
-
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
2591
|
-
function launchedBy(env) {
|
|
2592
|
-
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
2593
|
-
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
2594
|
-
}
|
|
2595
|
-
function readHook(adapter, input) {
|
|
2596
|
-
const r = adapter.fromHook(input);
|
|
2597
|
-
if (r && !r.session.parent_id) {
|
|
2598
|
-
const launcher = launchedBy(input.env);
|
|
2599
|
-
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
2600
|
-
}
|
|
2601
|
-
return r;
|
|
2602
|
-
}
|
|
2603
|
-
|
|
2604
2695
|
// src/index.ts
|
|
2605
2696
|
function matches(expect, got, pathStr = "") {
|
|
2606
2697
|
if (expect === "*") return null;
|
|
@@ -2625,7 +2716,18 @@ function matches(expect, got, pathStr = "") {
|
|
|
2625
2716
|
async function runAdapterFixtures(files) {
|
|
2626
2717
|
const results = [];
|
|
2627
2718
|
for (const file of files) {
|
|
2628
|
-
|
|
2719
|
+
let fx;
|
|
2720
|
+
try {
|
|
2721
|
+
fx = JSON.parse(readFileSync7(file, "utf8"));
|
|
2722
|
+
} catch (e) {
|
|
2723
|
+
const code = e.code;
|
|
2724
|
+
results.push({ file, ok: false, why: code ? `not readable: ${code}` : `not JSON: ${e.message}` });
|
|
2725
|
+
continue;
|
|
2726
|
+
}
|
|
2727
|
+
if (typeof fx?.harness !== "string" || !fx.input || !Array.isArray(fx.expect)) {
|
|
2728
|
+
results.push({ file, ok: false, why: "not an adapter fixture: it needs harness, input and expect" });
|
|
2729
|
+
continue;
|
|
2730
|
+
}
|
|
2629
2731
|
const adapter = adapterByName(fx.harness);
|
|
2630
2732
|
if (!adapter) {
|
|
2631
2733
|
results.push({ file, ok: false, why: `no adapter ${fx.harness}` });
|