sessionpipe-conformance 0.12.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 CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // sessionpipe-conformance 0.12.0 · Apache-2.0 · https://sessionpipe.org
2
+ // sessionpipe-conformance 0.13.0 · Apache-2.0 · https://sessionpipe.org
3
3
 
4
4
  // src/cli.ts
5
5
  import { readdirSync as readdirSync5, realpathSync as realpathSync2, statSync as statSync5 } from "node:fs";
@@ -168,7 +168,14 @@ ${m.text}`;
168
168
  continue;
169
169
  }
170
170
  if (cur) turns.push(cur);
171
- cur = { user: m.text, assistant: "", at: m.at, startLine: m.line, endLine: m.line };
171
+ cur = {
172
+ user: m.text,
173
+ assistant: "",
174
+ at: m.at,
175
+ startLine: m.line,
176
+ endLine: m.line,
177
+ ...m.injected ? { injected: true } : {}
178
+ };
172
179
  } else {
173
180
  if (!cur) cur = { user: "", assistant: "", at: m.at, startLine: m.line, endLine: m.line };
174
181
  cur.assistant += (cur.assistant ? "\n\n" : "") + m.text;
@@ -178,10 +185,17 @@ ${m.text}`;
178
185
  if (cur?.assistant) turns.push(cur);
179
186
  const line = cur && !cur.assistant ? cur.startLine : endLine;
180
187
  const out = {
181
- turns: turns.map((t) => ({ user: t.user, assistant: t.assistant, at: t.at, endLine: t.endLine })),
188
+ turns: turns.map((t) => ({
189
+ user: t.user,
190
+ assistant: t.assistant,
191
+ at: t.at,
192
+ endLine: t.endLine,
193
+ ...t.injected ? { injected: true } : {}
194
+ })),
182
195
  line
183
196
  };
184
- if (cur && !cur.assistant && cur.user) out.open = { user: cur.user, at: cur.at };
197
+ if (cur && !cur.assistant && cur.user)
198
+ out.open = { user: cur.user, at: cur.at, ...cur.injected ? { injected: true } : {} };
185
199
  return out;
186
200
  }
187
201
  var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
@@ -204,27 +218,51 @@ var textParts = (c, kinds) => {
204
218
  (b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
205
219
  ).map((b) => b.text).join("\n");
206
220
  };
221
+ var SPOKEN = /* @__PURE__ */ new Set(["human", "peer"]);
222
+ var PEER_INTRO = /sent a message[^\n]*:$/;
223
+ var PEER_OUTRO = "\n\nThis came from another ";
224
+ var ENVELOPE = /^<cross-session-message\b[^>]*>\n([\s\S]*)\n<\/cross-session-message>$/;
225
+ function peerAsk(origin, text) {
226
+ const body = origin?.body;
227
+ let t = typeof body === "string" && body.trim() ? body : text;
228
+ if (t !== body) {
229
+ const nl = t.indexOf("\n");
230
+ if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);
231
+ const outro = t.indexOf(PEER_OUTRO);
232
+ if (outro > 0) t = t.slice(0, outro);
233
+ }
234
+ return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();
235
+ }
207
236
  var queuedPrompt = (a) => {
208
237
  if (!a || typeof a !== "object") return "";
209
238
  const q = a;
210
239
  if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
211
- if (q.origin?.kind && q.origin.kind !== "human") return "";
212
- return textParts(q.prompt, ["text"]).trim();
240
+ const kind = q.origin?.kind ?? "human";
241
+ if (!SPOKEN.has(kind)) return "";
242
+ const text = textParts(q.prompt, ["text"]).trim();
243
+ return kind === "peer" ? peerAsk(q.origin, text) : text;
213
244
  };
245
+ var channelText = (text) => /^<channel\b[^>]*>([\s\S]*)<\/channel>$/.exec(text)?.[1]?.trim() ?? "";
214
246
  function readClaude(lines, from) {
215
247
  const msgs = [];
216
248
  for (let i = from; i < lines.length; i++) {
217
249
  const j = lines[i] ? parse(lines[i]) : null;
218
250
  if (!j || j.isSidechain) continue;
219
251
  const origin = j.origin;
220
- if (j.type === "user" && (j.isMeta || j.isCompactSummary || j.repeatsCompactSummary || origin?.kind && origin.kind !== "human"))
221
- continue;
252
+ const channel = j.type === "user" && origin?.kind === "channel";
253
+ const peer = origin?.kind === "peer";
254
+ if (j.type === "user" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;
255
+ if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) continue;
222
256
  const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
223
257
  const c = j.message?.content;
224
- if (j.type === "user") {
225
- const text = textParts(c, ["text"]).trim();
258
+ if (channel) {
259
+ const text = channelText(textParts(c, ["text"]).trim());
260
+ if (text) msgs.push({ role: "user", text, at, line: i, injected: true });
261
+ } else if (j.type === "user") {
262
+ const raw = textParts(c, ["text"]).trim();
263
+ const text = peer ? peerAsk(origin, raw) : raw;
226
264
  if (!text || isInjected(text)) continue;
227
- msgs.push({ role: "user", text, at, line: i });
265
+ msgs.push({ role: "user", text, at, line: i, ...peer ? { injected: true } : {} });
228
266
  } else if (j.type === "assistant" && Array.isArray(c)) {
229
267
  const text = textParts(c, ["text"]).trim();
230
268
  if (text) msgs.push({ role: "assistant", text, at, line: i });
@@ -1140,6 +1178,34 @@ async function claudeAiTitle(bridge, token) {
1140
1178
  // ../core/src/adapters/headless.ts
1141
1179
  import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
1142
1180
  import path4 from "node:path";
1181
+
1182
+ // ../core/src/launched-by.ts
1183
+ var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
1184
+ var DELEGATED_BY_ENV = "SESSIONPIPE_ASKED_BY";
1185
+ var SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
1186
+ function refIn(env, name) {
1187
+ const v = env[name]?.trim();
1188
+ return v && SESSION_REF.test(v) ? v : void 0;
1189
+ }
1190
+ function launchedBy(env) {
1191
+ return refIn(env, LAUNCHED_BY_ENV);
1192
+ }
1193
+ function delegatedBy(env) {
1194
+ return refIn(env, DELEGATED_BY_ENV);
1195
+ }
1196
+ function readHook(adapter, input) {
1197
+ const r = adapter.fromHook(input);
1198
+ if (r && !r.session.parent_id) {
1199
+ const self = `${adapter.name}:${r.session.id}`;
1200
+ const launcher = launchedBy(input.env);
1201
+ if (launcher && launcher !== self) r.session.launched_by = launcher;
1202
+ const asker = delegatedBy(input.env);
1203
+ if (asker && asker !== self) r.session.delegated_by = asker;
1204
+ }
1205
+ return r;
1206
+ }
1207
+
1208
+ // ../core/src/adapters/headless.ts
1143
1209
  var JOB_TIMEOUT_MS = 30 * 6e4;
1144
1210
  var OUT_CAP = 1024 * 1024;
1145
1211
  function isExe(p) {
@@ -2591,22 +2657,6 @@ function prune5(o) {
2591
2657
  var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
2592
2658
  var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
2593
2659
 
2594
- // ../core/src/launched-by.ts
2595
- var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
2596
- var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
2597
- function launchedBy(env) {
2598
- const v = env[LAUNCHED_BY_ENV]?.trim();
2599
- return v && LAUNCHED_BY.test(v) ? v : void 0;
2600
- }
2601
- function readHook(adapter, input) {
2602
- const r = adapter.fromHook(input);
2603
- if (r && !r.session.parent_id) {
2604
- const launcher = launchedBy(input.env);
2605
- if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
2606
- }
2607
- return r;
2608
- }
2609
-
2610
2660
  // src/index.ts
2611
2661
  function matches(expect, got, pathStr = "") {
2612
2662
  if (expect === "*") return null;