sessionpipe-conformance 0.9.0 → 0.10.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.9.0 · Apache-2.0 · https://sessionpipe.org
1
+ // sessionpipe-conformance 0.10.0 · Apache-2.0 · https://sessionpipe.org
2
2
 
3
3
  // src/index.ts
4
4
  import { readFileSync as readFileSync6 } from "node:fs";
@@ -798,14 +798,28 @@ var claudeCode = {
798
798
  case "SessionStart":
799
799
  events.push(ev("session.started", { source: pickSource(s.source ?? s.trigger) }));
800
800
  break;
801
- case "UserPromptSubmit":
801
+ case "UserPromptSubmit": {
802
+ const prompt = typeof s.prompt === "string" ? s.prompt : void 0;
802
803
  events.push(
803
804
  ev("turn.started", {
804
805
  turn_id,
805
- prompt_chars: typeof s.prompt === "string" ? [...s.prompt].length : void 0
806
+ prompt_chars: prompt === void 0 ? void 0 : [...prompt].length,
807
+ // Claude Code's hook schema documents `source`
808
+ // (user | sdk | system | loop_wakeup | schedule_wakeup | poll_event) and says a
809
+ // payload may omit it while the field rolls out; 2.1.304's payload has no such
810
+ // key at all. Forwarded verbatim, shape-guarded only: the list is the harness's
811
+ // to extend.
812
+ submitted_by: harnessWord(s.source),
813
+ // One of Claude Code's own prompt wrappers: the prompt is a block the harness
814
+ // wrote, so it is not the person's words. Absent is unknown, never "the person" —
815
+ // and `true` is never "no person caused this turn", because a Talk or
816
+ // `sessionpipe wait` delivery hands the person's own message over inside a
817
+ // <task-notification> (PROTOCOL.md §4).
818
+ injected: prompt !== void 0 && injectedBlock(prompt) ? true : void 0
806
819
  })
807
820
  );
808
821
  break;
822
+ }
809
823
  case "PreToolUse":
810
824
  if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, agent_id, input: s.tool_input }));
811
825
  break;
@@ -860,7 +874,13 @@ var claudeCode = {
860
874
  );
861
875
  break;
862
876
  case "Stop":
863
- events.push(ev("turn.ended", { turn_id, reason: "stop" }));
877
+ events.push(
878
+ ev("turn.ended", {
879
+ turn_id,
880
+ reason: "stop",
881
+ background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
882
+ })
883
+ );
864
884
  break;
865
885
  case "StopFailure":
866
886
  events.push(
@@ -903,6 +923,8 @@ var claudeCode = {
903
923
  if (event === "UserPromptSubmit" && typeof s.prompt === "string") hints.prompt = s.prompt;
904
924
  const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
905
925
  if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
926
+ const attended = input.env.CLAUDE_CODE_SESSION_ATTENDED;
927
+ if (attended === "1" || attended === "0") hints.attended = attended === "1" || !!input.env.SESSIONPIPE_CONTROL_JOB;
906
928
  if (event !== "PreToolUse" && event !== "PostToolUse") hints.claude_pid = claudePid(input.env);
907
929
  const r = { session, events, hints };
908
930
  if (typeof s.transcript_path === "string") r.transcript = s.transcript_path;
@@ -923,6 +945,7 @@ var claudeCode = {
923
945
  const cwd = out.cwd ?? session.cwd;
924
946
  Object.assign(out, gitFacts(cwd), out.branch ? { branch: out.branch } : {});
925
947
  if (!out.url && typeof hints.desktop_id === "string") out.url = `claude://code/continue/${hints.desktop_id}`;
948
+ if (typeof hints.attended === "boolean") out.attended = hints.attended || !!out.url;
926
949
  return out;
927
950
  },
928
951
  backfill(sinceMs, env = process.env) {
@@ -958,6 +981,9 @@ function notClaudeCode(id, s) {
958
981
  function pickSource(v) {
959
982
  return typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
960
983
  }
984
+ var harnessWord = (v) => typeof v === "string" && /^[A-Za-z0-9][A-Za-z0-9_.-]{0,39}$/.test(v) ? v : void 0;
985
+ var WRAPPER = /^<(?:task-notification|cross-session-message|system-reminder|local-command-stdout|command-[a-z-]+)[\s/>]/i;
986
+ var injectedBlock = (prompt) => WRAPPER.test(prompt.trim());
961
987
  function errText(v) {
962
988
  if (typeof v === "string") return v.slice(0, 2e3);
963
989
  if (v && typeof v === "object") {
@@ -1074,7 +1100,27 @@ function claudeLogin(dir, home = homeDir()) {
1074
1100
  } catch {
1075
1101
  }
1076
1102
  }
1077
- return null;
1103
+ return apiAccount(dir);
1104
+ }
1105
+ var ANTHROPIC_HOST = /(^|\.)anthropic\.com$/i;
1106
+ function apiAccount(dir) {
1107
+ let base;
1108
+ try {
1109
+ base = JSON.parse(readFileSync3(settingsIn(dir), "utf8")).env?.ANTHROPIC_BASE_URL;
1110
+ } catch {
1111
+ return null;
1112
+ }
1113
+ if (typeof base !== "string") return null;
1114
+ let u;
1115
+ try {
1116
+ u = new URL(base.trim());
1117
+ } catch {
1118
+ return null;
1119
+ }
1120
+ if (u.protocol !== "https:" && u.protocol !== "http:" || !u.hostname || ANTHROPIC_HOST.test(u.hostname))
1121
+ return null;
1122
+ const origin = `${u.protocol}//${u.host}`;
1123
+ return { id: `api-${createHash("sha256").update(origin).digest("hex").slice(0, 16)}`, name: u.host };
1078
1124
  }
1079
1125
  var accountCache = /* @__PURE__ */ new Map();
1080
1126
  function claudeAccount(dir) {
@@ -1555,7 +1601,7 @@ var codex = {
1555
1601
  if (j && typeof j === "object" && !Array.isArray(j)) s = j;
1556
1602
  } catch {
1557
1603
  }
1558
- const id = s.session_id;
1604
+ const id = s.session_id ?? s.thread_id ?? s.conversation_id ?? s["thread-id"] ?? s["session-id"];
1559
1605
  if (!id) return null;
1560
1606
  if (s.transcript_path === null) return null;
1561
1607
  const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
@@ -2317,6 +2363,22 @@ function prune5(o) {
2317
2363
  var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
2318
2364
  var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
2319
2365
 
2366
+ // ../core/src/launched-by.ts
2367
+ var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
2368
+ var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
2369
+ function launchedBy(env) {
2370
+ const v = env[LAUNCHED_BY_ENV]?.trim();
2371
+ return v && LAUNCHED_BY.test(v) ? v : void 0;
2372
+ }
2373
+ function readHook(adapter, input) {
2374
+ const r = adapter.fromHook(input);
2375
+ if (r && !r.session.parent_id) {
2376
+ const launcher = launchedBy(input.env);
2377
+ if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
2378
+ }
2379
+ return r;
2380
+ }
2381
+
2320
2382
  // src/index.ts
2321
2383
  function matches(expect, got, pathStr = "") {
2322
2384
  if (expect === "*") return null;
@@ -2347,7 +2409,7 @@ async function runAdapterFixtures(files) {
2347
2409
  results.push({ file, ok: false, why: `no adapter ${fx.harness}` });
2348
2410
  continue;
2349
2411
  }
2350
- const r = adapter.fromHook({
2412
+ const r = readHook(adapter, {
2351
2413
  argv: fx.input.argv,
2352
2414
  stdin: fx.input.stdin,
2353
2415
  env: fx.input.env ?? {},