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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // sessionpipe-conformance 0.12.0 · Apache-2.0 · https://sessionpipe.org
1
+ // sessionpipe-conformance 0.13.0 · Apache-2.0 · https://sessionpipe.org
2
2
 
3
3
  // src/index.ts
4
4
  import { readFileSync as readFileSync7 } from "node:fs";
@@ -162,7 +162,14 @@ ${m.text}`;
162
162
  continue;
163
163
  }
164
164
  if (cur) turns.push(cur);
165
- cur = { user: m.text, assistant: "", at: m.at, startLine: m.line, endLine: m.line };
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) => ({ user: t.user, assistant: t.assistant, at: t.at, endLine: t.endLine })),
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) out.open = { user: cur.user, at: cur.at };
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
- if (q.origin?.kind && q.origin.kind !== "human") return "";
206
- return textParts(q.prompt, ["text"]).trim();
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
- if (j.type === "user" && (j.isMeta || j.isCompactSummary || j.repeatsCompactSummary || origin?.kind && origin.kind !== "human"))
215
- continue;
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 (j.type === "user") {
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 });
@@ -1134,6 +1172,34 @@ async function claudeAiTitle(bridge, token) {
1134
1172
  // ../core/src/adapters/headless.ts
1135
1173
  import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
1136
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
1137
1203
  var JOB_TIMEOUT_MS = 30 * 6e4;
1138
1204
  var OUT_CAP = 1024 * 1024;
1139
1205
  function isExe(p) {
@@ -2585,22 +2651,6 @@ function prune5(o) {
2585
2651
  var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
2586
2652
  var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
2587
2653
 
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
2654
  // src/index.ts
2605
2655
  function matches(expect, got, pathStr = "") {
2606
2656
  if (expect === "*") return null;