sessionpipe-conformance 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +133 -54
- package/dist/cli.js.map +4 -4
- package/dist/index.js +133 -54
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// sessionpipe-conformance 0.
|
|
1
|
+
// sessionpipe-conformance 0.13.0 · Apache-2.0 · https://sessionpipe.org
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
5
5
|
|
|
6
6
|
// ../core/src/paths.ts
|
|
7
7
|
import os from "node:os";
|
|
@@ -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 });
|
|
@@ -596,13 +634,7 @@ var claudeCode = {
|
|
|
596
634
|
);
|
|
597
635
|
break;
|
|
598
636
|
case "Stop":
|
|
599
|
-
events.push(
|
|
600
|
-
ev("turn.ended", {
|
|
601
|
-
turn_id,
|
|
602
|
-
reason: "stop",
|
|
603
|
-
background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
|
|
604
|
-
})
|
|
605
|
-
);
|
|
637
|
+
events.push(ev("turn.ended", { turn_id, reason: "stop", background: backgroundCount(s.background_tasks) }));
|
|
606
638
|
break;
|
|
607
639
|
case "StopFailure":
|
|
608
640
|
events.push(
|
|
@@ -625,7 +657,8 @@ var claudeCode = {
|
|
|
625
657
|
events.push(
|
|
626
658
|
ev("subagent.ended", {
|
|
627
659
|
agent_id: String(s.agent_id ?? ""),
|
|
628
|
-
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
660
|
+
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0,
|
|
661
|
+
background: backgroundCount(s.background_tasks, String(s.agent_id ?? ""))
|
|
629
662
|
})
|
|
630
663
|
);
|
|
631
664
|
break;
|
|
@@ -700,6 +733,15 @@ var claudeCode = {
|
|
|
700
733
|
return rows2;
|
|
701
734
|
}
|
|
702
735
|
};
|
|
736
|
+
var WAITER_RE = /(?:^|[\s"'`/\\])sessionpipe(?:\.\w+)?["']?\s+wait(?:\s|$)/;
|
|
737
|
+
function backgroundCount(tasks, ending) {
|
|
738
|
+
if (!Array.isArray(tasks)) return void 0;
|
|
739
|
+
return tasks.filter((t) => {
|
|
740
|
+
const task = t;
|
|
741
|
+
if (ending !== void 0 && task?.id === ending) return false;
|
|
742
|
+
return !(typeof task?.command === "string" && WAITER_RE.test(task.command));
|
|
743
|
+
}).length;
|
|
744
|
+
}
|
|
703
745
|
var LINE_REFRESH_S = 60;
|
|
704
746
|
function statusLine(file, want, cmd) {
|
|
705
747
|
const f = readJsonFile(file);
|
|
@@ -1128,8 +1170,36 @@ async function claudeAiTitle(bridge, token) {
|
|
|
1128
1170
|
}
|
|
1129
1171
|
|
|
1130
1172
|
// ../core/src/adapters/headless.ts
|
|
1131
|
-
import { accessSync, constants, statSync as statSync3 } from "node:fs";
|
|
1173
|
+
import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
1132
1174
|
import path4 from "node:path";
|
|
1175
|
+
|
|
1176
|
+
// ../core/src/launched-by.ts
|
|
1177
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
1178
|
+
var DELEGATED_BY_ENV = "SESSIONPIPE_ASKED_BY";
|
|
1179
|
+
var SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
1180
|
+
function refIn(env, name) {
|
|
1181
|
+
const v = env[name]?.trim();
|
|
1182
|
+
return v && SESSION_REF.test(v) ? v : void 0;
|
|
1183
|
+
}
|
|
1184
|
+
function launchedBy(env) {
|
|
1185
|
+
return refIn(env, LAUNCHED_BY_ENV);
|
|
1186
|
+
}
|
|
1187
|
+
function delegatedBy(env) {
|
|
1188
|
+
return refIn(env, DELEGATED_BY_ENV);
|
|
1189
|
+
}
|
|
1190
|
+
function readHook(adapter, input) {
|
|
1191
|
+
const r = adapter.fromHook(input);
|
|
1192
|
+
if (r && !r.session.parent_id) {
|
|
1193
|
+
const self = `${adapter.name}:${r.session.id}`;
|
|
1194
|
+
const launcher = launchedBy(input.env);
|
|
1195
|
+
if (launcher && launcher !== self) r.session.launched_by = launcher;
|
|
1196
|
+
const asker = delegatedBy(input.env);
|
|
1197
|
+
if (asker && asker !== self) r.session.delegated_by = asker;
|
|
1198
|
+
}
|
|
1199
|
+
return r;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// ../core/src/adapters/headless.ts
|
|
1133
1203
|
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
1134
1204
|
var OUT_CAP = 1024 * 1024;
|
|
1135
1205
|
function isExe(p) {
|
|
@@ -1150,13 +1220,38 @@ function findBinary(o) {
|
|
|
1150
1220
|
for (const d of o.dirs) for (const n of o.names) if (isExe(path4.join(d, n))) return path4.join(d, n);
|
|
1151
1221
|
return null;
|
|
1152
1222
|
}
|
|
1223
|
+
function shimTarget(shim) {
|
|
1224
|
+
let text = "";
|
|
1225
|
+
try {
|
|
1226
|
+
text = readFileSync3(shim, "utf8");
|
|
1227
|
+
} catch {
|
|
1228
|
+
return null;
|
|
1229
|
+
}
|
|
1230
|
+
const m = /"%(?:~dp0|dp0%)\\([^"]+)"\s+%\*/.exec(text);
|
|
1231
|
+
if (!m) return null;
|
|
1232
|
+
const target = path4.resolve(path4.dirname(shim), m[1].split("\\").join(path4.sep));
|
|
1233
|
+
try {
|
|
1234
|
+
return statSync3(target).isFile() ? target : null;
|
|
1235
|
+
} catch {
|
|
1236
|
+
return null;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
function spawnable(bin, args) {
|
|
1240
|
+
if (!/\.(cmd|bat)$/i.test(bin)) return { file: bin, args };
|
|
1241
|
+
const target = shimTarget(bin);
|
|
1242
|
+
return target ? { file: process.execPath, args: [target, ...args] } : null;
|
|
1243
|
+
}
|
|
1244
|
+
function probe(bin, args) {
|
|
1245
|
+
const s = spawnable(bin, args);
|
|
1246
|
+
return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
|
|
1247
|
+
}
|
|
1153
1248
|
|
|
1154
1249
|
// ../core/src/adapters/antigravity.ts
|
|
1155
1250
|
import {
|
|
1156
1251
|
existsSync as existsSync3,
|
|
1157
1252
|
mkdirSync as mkdirSync2,
|
|
1158
1253
|
readdirSync as readdirSync3,
|
|
1159
|
-
readFileSync as
|
|
1254
|
+
readFileSync as readFileSync4,
|
|
1160
1255
|
realpathSync,
|
|
1161
1256
|
statSync as statSync4,
|
|
1162
1257
|
unlinkSync as unlinkSync2,
|
|
@@ -1285,7 +1380,7 @@ var antigravity = {
|
|
|
1285
1380
|
readTranscript(file, fromLine) {
|
|
1286
1381
|
let text = "";
|
|
1287
1382
|
try {
|
|
1288
|
-
text =
|
|
1383
|
+
text = readFileSync4(file, "utf8");
|
|
1289
1384
|
} catch {
|
|
1290
1385
|
return { turns: [], line: fromLine };
|
|
1291
1386
|
}
|
|
@@ -1364,7 +1459,7 @@ function agProjects(dir) {
|
|
|
1364
1459
|
if (!ID_RE.test(id)) continue;
|
|
1365
1460
|
let j;
|
|
1366
1461
|
try {
|
|
1367
|
-
j = JSON.parse(
|
|
1462
|
+
j = JSON.parse(readFileSync4(path5.join(dir, n), "utf8"));
|
|
1368
1463
|
} catch {
|
|
1369
1464
|
continue;
|
|
1370
1465
|
}
|
|
@@ -1401,7 +1496,7 @@ function antigravityRemoteUrl(transcript, opts) {
|
|
|
1401
1496
|
for (const d of dirs) {
|
|
1402
1497
|
try {
|
|
1403
1498
|
const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
|
|
1404
|
-
|
|
1499
|
+
readFileSync4(path5.join(d, "antigravity_state.pbtxt"), "utf8")
|
|
1405
1500
|
);
|
|
1406
1501
|
if (!m) continue;
|
|
1407
1502
|
const base = `https://antigravity.google.com/r/${m[1]}-v2`;
|
|
@@ -1440,7 +1535,7 @@ function antigravityFacts(file, opts) {
|
|
|
1440
1535
|
}
|
|
1441
1536
|
|
|
1442
1537
|
// ../core/src/adapters/codex.ts
|
|
1443
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as
|
|
1538
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1444
1539
|
import path6 from "node:path";
|
|
1445
1540
|
|
|
1446
1541
|
// ../core/src/adapters/codex-config.ts
|
|
@@ -1597,7 +1692,7 @@ function installNotify(cmd, env) {
|
|
|
1597
1692
|
const file = configFile(env);
|
|
1598
1693
|
let text = "";
|
|
1599
1694
|
try {
|
|
1600
|
-
text =
|
|
1695
|
+
text = readFileSync5(file, "utf8");
|
|
1601
1696
|
} catch {
|
|
1602
1697
|
}
|
|
1603
1698
|
const argv = cmd([NAME3, "notify"]).argv;
|
|
@@ -1643,7 +1738,7 @@ function uninstallNotify(env) {
|
|
|
1643
1738
|
const file = configFile(env);
|
|
1644
1739
|
let text;
|
|
1645
1740
|
try {
|
|
1646
|
-
text =
|
|
1741
|
+
text = readFileSync5(file, "utf8");
|
|
1647
1742
|
} catch {
|
|
1648
1743
|
return { file, changed: false };
|
|
1649
1744
|
}
|
|
@@ -1715,7 +1810,7 @@ var codex = {
|
|
|
1715
1810
|
];
|
|
1716
1811
|
let text = "";
|
|
1717
1812
|
try {
|
|
1718
|
-
text =
|
|
1813
|
+
text = readFileSync5(configFile(env), "utf8");
|
|
1719
1814
|
} catch {
|
|
1720
1815
|
}
|
|
1721
1816
|
const n = readNotify(text);
|
|
@@ -1842,7 +1937,7 @@ var codex = {
|
|
|
1842
1937
|
readTranscript(file, fromLine) {
|
|
1843
1938
|
let text = "";
|
|
1844
1939
|
try {
|
|
1845
|
-
text =
|
|
1940
|
+
text = readFileSync5(file, "utf8");
|
|
1846
1941
|
} catch {
|
|
1847
1942
|
return { turns: [], line: fromLine };
|
|
1848
1943
|
}
|
|
@@ -2363,7 +2458,7 @@ function devinAccount(env = process.env) {
|
|
|
2363
2458
|
|
|
2364
2459
|
// ../core/src/adapters/gemini-cli.ts
|
|
2365
2460
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2366
|
-
import { existsSync as existsSync7, readFileSync as
|
|
2461
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
|
|
2367
2462
|
import path8 from "node:path";
|
|
2368
2463
|
var NAME5 = "gemini-cli";
|
|
2369
2464
|
var GEMINI_EVENTS = [
|
|
@@ -2385,14 +2480,14 @@ function geminiVersion(platform = process.platform, env = process.env) {
|
|
|
2385
2480
|
const win = platform === "win32";
|
|
2386
2481
|
const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path8.dirname(process.execPath)], env }) : "gemini";
|
|
2387
2482
|
if (!bin) throw new Error("no gemini on PATH");
|
|
2388
|
-
const
|
|
2483
|
+
const c = probe(bin, ["--version"]);
|
|
2484
|
+
return execFileSync4(c.file, c.args, {
|
|
2389
2485
|
encoding: "utf8",
|
|
2390
2486
|
timeout: 4e3,
|
|
2391
2487
|
stdio: ["ignore", "pipe", "ignore"],
|
|
2392
|
-
env
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
return out.trim();
|
|
2488
|
+
env,
|
|
2489
|
+
shell: c.shell
|
|
2490
|
+
}).trim();
|
|
2396
2491
|
}
|
|
2397
2492
|
function geminiHooksActive(settingsFile) {
|
|
2398
2493
|
const tools = readJson(settingsFile).tools;
|
|
@@ -2517,7 +2612,7 @@ var geminiCli = {
|
|
|
2517
2612
|
readTranscript(file, fromLine) {
|
|
2518
2613
|
let text = "";
|
|
2519
2614
|
try {
|
|
2520
|
-
text =
|
|
2615
|
+
text = readFileSync6(file, "utf8");
|
|
2521
2616
|
} catch {
|
|
2522
2617
|
return { turns: [], line: fromLine };
|
|
2523
2618
|
}
|
|
@@ -2556,22 +2651,6 @@ function prune5(o) {
|
|
|
2556
2651
|
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
2557
2652
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
2558
2653
|
|
|
2559
|
-
// ../core/src/launched-by.ts
|
|
2560
|
-
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
2561
|
-
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
2562
|
-
function launchedBy(env) {
|
|
2563
|
-
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
2564
|
-
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
2565
|
-
}
|
|
2566
|
-
function readHook(adapter, input) {
|
|
2567
|
-
const r = adapter.fromHook(input);
|
|
2568
|
-
if (r && !r.session.parent_id) {
|
|
2569
|
-
const launcher = launchedBy(input.env);
|
|
2570
|
-
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
2571
|
-
}
|
|
2572
|
-
return r;
|
|
2573
|
-
}
|
|
2574
|
-
|
|
2575
2654
|
// src/index.ts
|
|
2576
2655
|
function matches(expect, got, pathStr = "") {
|
|
2577
2656
|
if (expect === "*") return null;
|
|
@@ -2596,7 +2675,7 @@ function matches(expect, got, pathStr = "") {
|
|
|
2596
2675
|
async function runAdapterFixtures(files) {
|
|
2597
2676
|
const results = [];
|
|
2598
2677
|
for (const file of files) {
|
|
2599
|
-
const fx = JSON.parse(
|
|
2678
|
+
const fx = JSON.parse(readFileSync7(file, "utf8"));
|
|
2600
2679
|
const adapter = adapterByName(fx.harness);
|
|
2601
2680
|
if (!adapter) {
|
|
2602
2681
|
results.push({ file, ok: false, why: `no adapter ${fx.harness}` });
|