@sechroom/cli 2026.9.26-rc.fa5538238 → 2026.9.27-rc.1ea98ba09

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.
Files changed (2) hide show
  1. package/dist/index.js +77 -20
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8308,7 +8308,7 @@ function str(v) {
8308
8308
  }
8309
8309
 
8310
8310
  // src/commands/hook.ts
8311
- import { createHash as createHash4 } from "crypto";
8311
+ import { createHash as createHash5 } from "crypto";
8312
8312
  import { existsSync as existsSync13, mkdirSync as mkdirSync15, readFileSync as readFileSync14, statSync as statSync4, writeFileSync as writeFileSync14 } from "fs";
8313
8313
  import { dirname as dirname14, join as join17 } from "path";
8314
8314
 
@@ -8454,7 +8454,7 @@ function installLaneCommitHook(start) {
8454
8454
  }
8455
8455
 
8456
8456
  // src/commands/session-context.ts
8457
- import { randomUUID as randomUUID3 } from "crypto";
8457
+ import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
8458
8458
  import { mkdirSync as mkdirSync14, renameSync as renameSync3, rmSync as rmSync8, writeFileSync as writeFileSync13 } from "fs";
8459
8459
  import { dirname as dirname13, join as join16 } from "path";
8460
8460
 
@@ -8795,6 +8795,31 @@ function removeDirectoryIfEmpty(path) {
8795
8795
 
8796
8796
  // src/commands/session-context.ts
8797
8797
  var DYNAMIC_AGENT_CONTEXT_FILE = join16(".sechroom", "CLAUDE.md");
8798
+ function deliveredContext(result, lane) {
8799
+ if (result.status !== "ready") return renderSessionContext(result, lane);
8800
+ const contract = [
8801
+ result.renderVersion,
8802
+ result.text,
8803
+ result.digest,
8804
+ result.revision
8805
+ ];
8806
+ if (contract.every((value) => value === void 0)) {
8807
+ return renderSessionContext(result, lane);
8808
+ }
8809
+ if (contract.some((value) => typeof value !== "string" || value.length === 0)) {
8810
+ throw new Error(
8811
+ "session context resolution returned a partial or malformed render contract (renderVersion, text, digest and revision must all be present); refusing to write it and preserving existing compiled skills"
8812
+ );
8813
+ }
8814
+ const text2 = result.text;
8815
+ const actual = createHash4("sha256").update(text2, "utf8").digest("hex");
8816
+ if (result.digest !== actual) {
8817
+ throw new Error(
8818
+ "session context text does not match its digest; refusing to write it and preserving existing compiled skills"
8819
+ );
8820
+ }
8821
+ return text2;
8822
+ }
8798
8823
  function checkoutRoot(start) {
8799
8824
  const semPath = resolveSemPathForRead(start);
8800
8825
  return semPath ? dirname13(dirname13(semPath)) : start;
@@ -8819,6 +8844,9 @@ function renderSessionContext(result, lane) {
8819
8844
  `<!-- sechroom-context lane=${JSON.stringify(lane)} workspace=${JSON.stringify(result.workspaceId ?? "")} -->`,
8820
8845
  ""
8821
8846
  ];
8847
+ for (const notice of result.notices ?? []) {
8848
+ lines.push(`> Notice: ${notice}`, "");
8849
+ }
8822
8850
  for (const member of result.members) {
8823
8851
  lines.push(
8824
8852
  `<!-- sechroom-context-member key=${JSON.stringify(member.key)} layer=${JSON.stringify(member.layer)} bundle=${JSON.stringify(`${member.bundleSlug}@${member.bundleVersion}`)} component=${JSON.stringify(member.componentSlug)} id=${JSON.stringify(member.sourceId)} sourceVersion=${member.sourceVersion} -->`
@@ -8832,39 +8860,54 @@ function renderSessionContext(result, lane) {
8832
8860
  function writeSessionContext(start, lane, result, options = {}) {
8833
8861
  const root = checkoutRoot(start);
8834
8862
  const path = join16(root, DYNAMIC_AGENT_CONTEXT_FILE);
8863
+ const context = deliveredContext(result, lane);
8835
8864
  const skills = materialiseCompiledSkillCompositions(
8836
8865
  root,
8837
8866
  result.status === "hold" ? {
8838
8867
  skills: [],
8839
- holds: [{
8840
- code: "session-context-hold",
8841
- reason: result.reason?.trim() || "Session context resolution held without a reason.",
8842
- skillName: null
8843
- }],
8868
+ holds: [
8869
+ {
8870
+ code: "session-context-hold",
8871
+ reason: result.reason?.trim() || "Session context resolution held without a reason.",
8872
+ skillName: null
8873
+ }
8874
+ ],
8844
8875
  preserveExisting: true
8845
8876
  } : { skills: result.skills },
8846
8877
  options
8847
8878
  );
8848
- const context = renderSessionContext(result, lane);
8849
8879
  mkdirSync14(dirname13(path), { recursive: true });
8850
8880
  const temporaryPath = `${path}.${process.pid}.${randomUUID3()}.tmp`;
8851
8881
  try {
8852
- writeFileSync13(temporaryPath, context.endsWith("\n") ? context : `${context}
8853
- `, "utf8");
8882
+ writeFileSync13(
8883
+ temporaryPath,
8884
+ context.endsWith("\n") ? context : `${context}
8885
+ `,
8886
+ "utf8"
8887
+ );
8854
8888
  renameSync3(temporaryPath, path);
8855
8889
  } catch (error) {
8856
8890
  rmSync8(temporaryPath, { force: true });
8857
8891
  throw error;
8858
8892
  }
8859
- return { status: result.status, path, context, skills };
8893
+ return {
8894
+ status: result.status,
8895
+ path,
8896
+ context,
8897
+ skills,
8898
+ revision: result.revision ?? null
8899
+ };
8860
8900
  }
8861
8901
  async function materializeSessionContext(client, cfg, lane, start, options = {}) {
8862
- const { data, error, response } = await client.POST("/session-context/resolve", {
8863
- body: {
8864
- laneId: lane,
8865
- workspaceId: cfg.workspaceId ?? null
8902
+ const { data, error, response } = await client.POST(
8903
+ "/session-context/resolve",
8904
+ {
8905
+ body: {
8906
+ laneId: lane,
8907
+ workspaceId: cfg.workspaceId ?? null
8908
+ }
8866
8909
  }
8867
- });
8910
+ );
8868
8911
  if (!data) {
8869
8912
  const detail = error ? JSON.stringify(error) : `HTTP ${response?.status ?? "unknown"}`;
8870
8913
  throw new Error(`session context resolution failed: ${detail}`);
@@ -8873,7 +8916,9 @@ async function materializeSessionContext(client, cfg, lane, start, options = {})
8873
8916
  return writeSessionContext(start, lane, data, options);
8874
8917
  }
8875
8918
  function assertSessionContextResult(value) {
8876
- if (value === null || typeof value !== "object" || !["ready", "hold"].includes(String(value.status)) || !Array.isArray(value.members) || !Array.isArray(value.skills)) {
8919
+ if (value === null || typeof value !== "object" || !["ready", "hold"].includes(
8920
+ String(value.status)
8921
+ ) || !Array.isArray(value.members) || !Array.isArray(value.skills)) {
8877
8922
  throw new Error(
8878
8923
  "session context resolution returned an invalid payload; preserving existing compiled skills"
8879
8924
  );
@@ -9062,7 +9107,7 @@ function intentHash(i) {
9062
9107
  artifacts: i.artifacts ?? [],
9063
9108
  confidence: i.confidence ?? null
9064
9109
  });
9065
- return createHash4("sha256").update(canonical, "utf8").digest("hex");
9110
+ return createHash5("sha256").update(canonical, "utf8").digest("hex");
9066
9111
  }
9067
9112
  function recentlyCheckpointed(start, minutes) {
9068
9113
  const { lastEpochMs } = readLedger(start);
@@ -9879,6 +9924,18 @@ Displaced snapshot recovery surfaces:
9879
9924
  });
9880
9925
  emit(data, cmd.optsWithGlobals().json);
9881
9926
  });
9927
+ continuity.command("snapshot-seed <id>").description(
9928
+ "Render a snapshot you own or were granted as seed text, at most 16 KiB, with its revision and sha256 digest (GET /continuity/snapshots/{id}/seed)"
9929
+ ).action(async (id, _opts, cmd) => {
9930
+ const cfg = resolveConfig(cmd.optsWithGlobals());
9931
+ const data = await runApi(
9932
+ "Rendering snapshot seed",
9933
+ async () => (await makeClient(cfg)).GET("/continuity/snapshots/{id}/seed", {
9934
+ params: { path: { id } }
9935
+ })
9936
+ );
9937
+ emit(data, cmd.optsWithGlobals().json);
9938
+ });
9882
9939
  const snapshots = continuity.command("snapshots").description(
9883
9940
  "List the caller's own snapshots, including displaced recovery candidates (GET /me/continuity/snapshots)"
9884
9941
  ).option("--scope <scope>", "Filter by scope").option("--lane <laneId>", "Filter by lane id").action(async (opts, cmd) => {
@@ -12987,7 +13044,7 @@ Note: CLI memory get omits relationships by default; MCP get_memory includes the
12987
13044
  }
12988
13045
 
12989
13046
  // src/setup/apply.ts
12990
- import { createHash as createHash5, randomUUID as randomUUID4 } from "crypto";
13047
+ import { createHash as createHash6, randomUUID as randomUUID4 } from "crypto";
12991
13048
  import {
12992
13049
  chmodSync as chmodSync2,
12993
13050
  copyFileSync,
@@ -13014,7 +13071,7 @@ function normalizeBody(s) {
13014
13071
  return s.replace(/\r\n/g, "\n").trim();
13015
13072
  }
13016
13073
  function bodySha256(body) {
13017
- return createHash5("sha256").update(normalizeBody(body), "utf8").digest("hex");
13074
+ return createHash6("sha256").update(normalizeBody(body), "utf8").digest("hex");
13018
13075
  }
13019
13076
  function renderBlock(write) {
13020
13077
  const body = normalizeBody(write.body);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sechroom/cli",
3
- "version": "2026.9.26-rc.fa5538238",
3
+ "version": "2026.9.27-rc.1ea98ba09",
4
4
  "description": "Command-line interface for Sechroom — sign in, wire your AI tools, and work with your sechroom from the terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",