@atbash/sdk 0.19.2-dev.0 → 0.20.0-dev.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
@@ -42,6 +42,7 @@ __export(src_ts_exports, {
42
42
  EciesDomain: () => EciesDomain,
43
43
  HttpClient: () => HttpClient,
44
44
  HttpTransportError: () => HttpTransportError,
45
+ JudgeRefusalError: () => JudgeRefusalError,
45
46
  KEY_FILENAMES: () => KEY_FILENAMES,
46
47
  MemoryGuardManager: () => MemoryGuardManager,
47
48
  MemoryIntegrityError: () => MemoryIntegrityError,
@@ -60,6 +61,7 @@ __export(src_ts_exports, {
60
61
  columnAad: () => columnAad,
61
62
  commitMemoryVersion: () => commitMemoryVersion,
62
63
  configureEvents: () => configureEvents,
64
+ configureJailHold: () => configureJailHold,
63
65
  containsEvasionCharacters: () => containsEvasionCharacters,
64
66
  containsSecret: () => containsSecret,
65
67
  createFileLogger: () => createFileLogger,
@@ -88,12 +90,14 @@ __export(src_ts_exports, {
88
90
  getRollbackHistory: () => getRollbackHistory,
89
91
  guardMemoryWrite: () => guardMemoryWrite,
90
92
  isEnvelope: () => isEnvelope,
93
+ isJailHeld: () => isJailHeld,
91
94
  isValidPrivateKey: () => isValidPrivateKey,
92
95
  keyFingerprintOf: () => keyFingerprintOf,
93
96
  keyPathCandidates: () => keyPathCandidates,
94
97
  loadAgent: () => loadAgent,
95
98
  loadAgentFromFile: () => loadAgentFromFile,
96
99
  loadUserConfig: () => loadUserConfig,
100
+ memorySlotOf: () => memorySlotOf,
97
101
  normalizeActionForHash: () => normalizeActionForHash,
98
102
  normalizeActionType: () => normalizeActionType,
99
103
  normalizeForMatching: () => normalizeForMatching,
@@ -107,6 +111,7 @@ __export(src_ts_exports, {
107
111
  recordEvent: () => recordEvent,
108
112
  redactJsonStrings: () => redactJsonStrings,
109
113
  redactSecrets: () => redactSecrets,
114
+ releaseJailHolds: () => releaseJailHolds,
110
115
  resolve: () => resolve,
111
116
  resolveKeyPath: () => resolveKeyPath,
112
117
  rollbackMemory: () => rollbackMemory,
@@ -293,12 +298,12 @@ var AtbashAPIError = class extends Error {
293
298
  * ran does not carry. The path is the fact that settles it.
294
299
  */
295
300
  path;
296
- constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT, path5) {
297
- super(enrich(status, body, statusText, endpoint, path5));
301
+ constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT, path4) {
302
+ super(enrich(status, body, statusText, endpoint, path4));
298
303
  this.name = "AtbashAPIError";
299
304
  this.status = status;
300
305
  this.body = body;
301
- this.path = path5;
306
+ this.path = path4;
302
307
  }
303
308
  };
304
309
  var SignatureVerificationError = class extends Error {
@@ -307,9 +312,18 @@ var SignatureVerificationError = class extends Error {
307
312
  this.name = "SignatureVerificationError";
308
313
  }
309
314
  };
310
- function enrich(status, body, statusText, endpoint, path5) {
315
+ var JudgeRefusalError = class extends Error {
316
+ constructor(message) {
317
+ super(message);
318
+ this.name = "JudgeRefusalError";
319
+ }
320
+ };
321
+ function isRefusalStatus(status) {
322
+ return status >= 400 && status < 500 && status !== 408;
323
+ }
324
+ function enrich(status, body, statusText, endpoint, path4) {
311
325
  const dashboard = endpoint.replace(/\/+$/, "") || DEFAULT_ENDPOINT;
312
- let msg = `API error ${status}${path5 ? ` from ${path5}` : ""}: ${body || statusText}`;
326
+ let msg = `API error ${status}${path4 ? ` from ${path4}` : ""}: ${body || statusText}`;
313
327
  const lowered = body.toLowerCase();
314
328
  if (lowered.includes("agent not registered")) {
315
329
  msg += `
@@ -330,6 +344,61 @@ function enrich(status, body, statusText, endpoint, path5) {
330
344
  return msg;
331
345
  }
332
346
 
347
+ // src-ts/jail-hold.ts
348
+ var DEFAULTS = { ceilingMs: 3e4, pollMs: 1e3 };
349
+ var holds = /* @__PURE__ */ new Map();
350
+ var blockedAt = /* @__PURE__ */ new Map();
351
+ var BLOCK_MEMORY_MS = 10 * 6e4;
352
+ var config = { ...DEFAULTS };
353
+ function isJailHeld(pubkey) {
354
+ return holds.has(pubkey);
355
+ }
356
+ function recordBlock(pubkey) {
357
+ const now = Date.now();
358
+ for (const [key, at] of blockedAt) if (now - at > BLOCK_MEMORY_MS) blockedAt.delete(key);
359
+ blockedAt.set(pubkey, now);
360
+ }
361
+ function blockedSince(pubkey, startedAt) {
362
+ const at = blockedAt.get(pubkey);
363
+ return at !== void 0 && startedAt <= at;
364
+ }
365
+ function holdJail(pubkey, confirmJailed, onGiveUp) {
366
+ if (holds.has(pubkey)) return;
367
+ const hold = { timer: null };
368
+ holds.set(pubkey, hold);
369
+ const deadline = Date.now() + config.ceilingMs;
370
+ const tick = async () => {
371
+ if (holds.get(pubkey) !== hold) return;
372
+ let jailed = false;
373
+ try {
374
+ jailed = await confirmJailed();
375
+ } catch {
376
+ }
377
+ if (holds.get(pubkey) !== hold) return;
378
+ if (jailed) {
379
+ holds.delete(pubkey);
380
+ return;
381
+ }
382
+ if (Date.now() >= deadline) {
383
+ holds.delete(pubkey);
384
+ onGiveUp?.();
385
+ return;
386
+ }
387
+ const timer = setTimeout(() => void tick(), config.pollMs);
388
+ timer.unref?.();
389
+ hold.timer = timer;
390
+ };
391
+ void tick();
392
+ }
393
+ function releaseJailHolds() {
394
+ for (const hold of holds.values()) if (hold.timer) clearTimeout(hold.timer);
395
+ holds.clear();
396
+ blockedAt.clear();
397
+ }
398
+ function configureJailHold(next) {
399
+ config = { ...DEFAULTS, ...next };
400
+ }
401
+
333
402
  // src-ts/http/client.ts
334
403
  var HttpClient = class {
335
404
  baseUrl;
@@ -338,8 +407,8 @@ var HttpClient = class {
338
407
  this.baseUrl = baseUrl.replace(/\/+$/, "");
339
408
  this.timeoutMs = timeoutMs;
340
409
  }
341
- buildUrl(path5, query) {
342
- const url = new URL(this.baseUrl + path5);
410
+ buildUrl(path4, query) {
411
+ const url = new URL(this.baseUrl + path4);
343
412
  if (query) {
344
413
  for (const [k, v] of Object.entries(query)) {
345
414
  if (v !== void 0 && v !== null && v !== "") {
@@ -349,14 +418,14 @@ var HttpClient = class {
349
418
  }
350
419
  return url.toString();
351
420
  }
352
- async get(path5, query, headers) {
353
- return this.fetch(this.buildUrl(path5, query), {
421
+ async get(path4, query, headers) {
422
+ return this.fetch(this.buildUrl(path4, query), {
354
423
  method: "GET",
355
424
  ...headers && { headers }
356
425
  });
357
426
  }
358
- async post(path5, body, headers) {
359
- return this.fetch(this.buildUrl(path5), {
427
+ async post(path4, body, headers) {
428
+ return this.fetch(this.buildUrl(path4), {
360
429
  method: "POST",
361
430
  headers: { "Content-Type": "application/json", ...headers },
362
431
  body: JSON.stringify(body)
@@ -674,7 +743,7 @@ var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
674
743
  var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
675
744
  var import_sdk_logs = require("@opentelemetry/sdk-logs");
676
745
  var import_node_crypto = require("crypto");
677
- var SDK_VERSION = true ? "0.19.2-dev.0" : "";
746
+ var SDK_VERSION = true ? "0.20.0-dev.0" : "";
678
747
  var SDK_LANGUAGE = "node";
679
748
  function sdkResourceAttributes() {
680
749
  const attrs = {
@@ -705,8 +774,8 @@ function isTelemetryOptedOut() {
705
774
  const filePath = (0, import_node_path2.join)(home2, ".config", "atbash", "telemetry.json");
706
775
  const raw = (0, import_node_fs2.readFileSync)(filePath, "utf-8").trim();
707
776
  if (!raw) return false;
708
- const config = JSON.parse(raw);
709
- return config.enabled === false;
777
+ const config2 = JSON.parse(raw);
778
+ return config2.enabled === false;
710
779
  } catch {
711
780
  return false;
712
781
  }
@@ -751,26 +820,26 @@ function customCollector() {
751
820
  }
752
821
  return { url: url.replace(/\/+$/, ""), headers: () => headers };
753
822
  }
754
- function setupTelemetry(config) {
755
- if (!config.enabled) return;
823
+ function setupTelemetry(config2) {
824
+ if (!config2.enabled) return;
756
825
  if (isTelemetryOptedOut()) return;
757
- if (config.source && !sourceExplicitlySet && SOURCE_SHAPE.test(config.source)) {
758
- defaultSource = config.source;
826
+ if (config2.source && !sourceExplicitlySet && SOURCE_SHAPE.test(config2.source)) {
827
+ defaultSource = config2.source;
759
828
  sourceExplicitlySet = true;
760
829
  }
761
- if (config.exportIntervalMs !== void 0 && requestedExportIntervalMs === void 0) {
762
- requestedExportIntervalMs = config.exportIntervalMs;
830
+ if (config2.exportIntervalMs !== void 0 && requestedExportIntervalMs === void 0) {
831
+ requestedExportIntervalMs = config2.exportIntervalMs;
763
832
  }
764
833
  if (meterProvider) return;
765
834
  const collector = customCollector();
766
- if (!collector && (!config.endpoint || !config.getAuthHeaders)) return;
835
+ if (!collector && (!config2.endpoint || !config2.getAuthHeaders)) return;
767
836
  if (!collector && /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(
768
- config.endpoint
837
+ config2.endpoint
769
838
  ))
770
839
  return;
771
840
  const destination = collector ?? {
772
- url: `${config.endpoint.replace(/\/+$/, "")}/api/telemetry`,
773
- headers: () => config.getAuthHeaders()
841
+ url: `${config2.endpoint.replace(/\/+$/, "")}/api/telemetry`,
842
+ headers: () => config2.getAuthHeaders()
774
843
  };
775
844
  const proxyUrl = destination.url;
776
845
  const exporter = new import_exporter_metrics_otlp_http.OTLPMetricExporter({
@@ -805,7 +874,7 @@ function setupTelemetry(config) {
805
874
  description: "SDK function execution duration",
806
875
  unit: "ms"
807
876
  });
808
- counterHasher = config.hashAgentKey ?? defaultAgentKeyHasher;
877
+ counterHasher = config2.hashAgentKey ?? defaultAgentKeyHasher;
809
878
  const logExporter = new import_exporter_logs_otlp_http.OTLPLogExporter({
810
879
  // A real collector takes OTLP at its own signal paths; the Atbash proxy
811
880
  // takes a query parameter, because it is one authenticated route.
@@ -824,8 +893,8 @@ function setupTelemetry(config) {
824
893
  });
825
894
  }
826
895
  configureEvents(loggerProvider.getLogger("atbash-sdk"), {
827
- source: sourceOrDefault(config.source),
828
- hashAgentKey: config.hashAgentKey ?? defaultAgentKeyHasher,
896
+ source: sourceOrDefault(config2.source),
897
+ hashAgentKey: config2.hashAgentKey ?? defaultAgentKeyHasher,
829
898
  version: SDK_VERSION
830
899
  });
831
900
  }
@@ -897,13 +966,13 @@ function loadUserConfig() {
897
966
  return {};
898
967
  }
899
968
  }
900
- function saveUserConfig(config) {
969
+ function saveUserConfig(config2) {
901
970
  const dir = getConfigDir();
902
971
  if (!(0, import_node_fs3.existsSync)(dir)) {
903
972
  (0, import_node_fs3.mkdirSync)(dir, { recursive: true, mode: 448 });
904
973
  }
905
974
  const filePath = getConfigPath();
906
- (0, import_node_fs3.writeFileSync)(filePath, JSON.stringify(config, null, 2) + "\n", {
975
+ (0, import_node_fs3.writeFileSync)(filePath, JSON.stringify(config2, null, 2) + "\n", {
907
976
  mode: 384
908
977
  });
909
978
  (0, import_node_fs3.chmodSync)(filePath, 384);
@@ -938,6 +1007,7 @@ function forcedChainNetwork(flagValue) {
938
1007
  }
939
1008
 
940
1009
  // src-ts/client.ts
1010
+ var AGENT_NOT_REGISTERED = "Agent not registered. Onboard the agent at the dashboard before submitting actions.";
941
1011
  function generateToolCallId() {
942
1012
  return `tc-${Date.now()}-${randomHex(4)}`;
943
1013
  }
@@ -1286,6 +1356,15 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1286
1356
  * return the signed tx hex. The server broadcasts to chain.
1287
1357
  */
1288
1358
  async logToolCall(action, context = "", options = {}) {
1359
+ return this._logToolCall(action, context, options, false);
1360
+ }
1361
+ /**
1362
+ * `raisePreflightErrors` (the judge path, like Python's
1363
+ * `raise_preflight_errors`): a failed registration check throws instead of
1364
+ * becoming `{ success: false }`, so the caller can tell Atbash's "no"
1365
+ * (not registered, or a 4xx) from an outage.
1366
+ */
1367
+ async _logToolCall(action, context, options, raisePreflightErrors) {
1289
1368
  const start = performance.now();
1290
1369
  recordCall("logToolCall", void 0, this.auth.pubkey);
1291
1370
  let exists;
@@ -1295,18 +1374,20 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1295
1374
  });
1296
1375
  } catch (err) {
1297
1376
  recordDuration("logToolCall", performance.now() - start, "error");
1377
+ if (raisePreflightErrors) throw err;
1298
1378
  return { success: false, toolCallId: null, error: errorMessage(err) };
1299
1379
  }
1300
1380
  if (!exists) {
1301
1381
  recordDuration("logToolCall", performance.now() - start, "error");
1382
+ if (raisePreflightErrors) throw new JudgeRefusalError(AGENT_NOT_REGISTERED);
1302
1383
  return {
1303
1384
  success: false,
1304
1385
  toolCallId: null,
1305
- error: "Agent not registered. Onboard the agent at the dashboard before submitting actions."
1386
+ error: AGENT_NOT_REGISTERED
1306
1387
  };
1307
1388
  }
1308
1389
  const toolCallId = generateToolCallId();
1309
- const brid = options.chainOpts?.blockchainRid || options.chainOpts?.network ? this.bridFromChainOpts(options.chainOpts) : await this.defaultOrgBrid() ?? this.blockchainRid;
1390
+ const brid = await this.callBrid(options.chainOpts);
1310
1391
  const orgKey = options.orgEncryptionPubKey ?? this.orgEncryptionPubKey ?? this._orgKeyFromChain;
1311
1392
  try {
1312
1393
  const signedHex = orgKey ? signEncryptedToolCall(
@@ -1356,6 +1437,8 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1356
1437
  if (!action?.trim()) {
1357
1438
  throw new Error("action is required and cannot be empty.");
1358
1439
  }
1440
+ if (isJailHeld(this.auth.pubkey)) return this.localJailBlock();
1441
+ const startedAt = Date.now();
1359
1442
  let chainOpts = options.chainOpts;
1360
1443
  if (options.orgName && this._explicitChain) {
1361
1444
  chainOpts = options.chainOpts ?? { network: this._defaultChain.network };
@@ -1380,12 +1463,13 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1380
1463
  }
1381
1464
  }
1382
1465
  }
1383
- const brid = this.bridFromChainOpts(chainOpts);
1384
- const logResult = await this.logToolCall(action, context, {
1385
- toolName: options.toolName,
1386
- toolArgsJson: options.toolArgsJson,
1387
- chainOpts
1388
- });
1466
+ const brid = await this.callBrid(chainOpts);
1467
+ const logResult = await this._logToolCall(
1468
+ action,
1469
+ context,
1470
+ { toolName: options.toolName, toolArgsJson: options.toolArgsJson, chainOpts },
1471
+ true
1472
+ );
1389
1473
  if (!logResult.success || !logResult.toolCallId || !logResult.signedHex) {
1390
1474
  throw new Error(logResult.error || "Failed to sign log_tool_call");
1391
1475
  }
@@ -1422,10 +1506,16 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1422
1506
  if (verifyPubKey !== void 0) {
1423
1507
  verifyResponseSignature(bodyBytes, resp, verifyPubKey);
1424
1508
  }
1425
- const data = parseJson(bodyBytes);
1509
+ const parsed = tryParseJson(bodyBytes.toString("utf-8"));
1510
+ if (!isRecord2(parsed)) {
1511
+ throw new JudgeRefusalError(
1512
+ "The judge's reply could not be read, so the action was not approved."
1513
+ );
1514
+ }
1515
+ const data = parsed;
1426
1516
  const rawScore = data.score;
1427
1517
  const score = typeof rawScore === "number" && Number.isInteger(rawScore) && rawScore >= 1 && rawScore <= 10 ? rawScore : void 0;
1428
- return {
1518
+ const result = {
1429
1519
  verdict: normalizeVerdict(data.verdict),
1430
1520
  allow: this.executable(data),
1431
1521
  actionType: normalizeActionType(data.action_type),
@@ -1441,6 +1531,45 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1441
1531
  score,
1442
1532
  refusal: explicitRefusal(data)
1443
1533
  };
1534
+ return this.applyJailHold(result, startedAt);
1535
+ }
1536
+ /**
1537
+ * Start a local hold on an enforced, on-chain BLOCK (the verdict that jails).
1538
+ * Refuse any other result that lands while the agent is held, or whose
1539
+ * request was already in flight when the block arrived: the server judged it
1540
+ * on pre-jail state.
1541
+ */
1542
+ applyJailHold(result, startedAt) {
1543
+ const pubkey = this.auth.pubkey;
1544
+ if (result.verdict === "BLOCK" && result.enforced && result.onChain) {
1545
+ recordBlock(pubkey);
1546
+ holdJail(
1547
+ pubkey,
1548
+ () => this.getAgentPolicy(pubkey).then((p) => p.isJailed),
1549
+ () => this.logger.warn?.("[atbash] local jail hold released: the chain did not confirm the jail in time")
1550
+ );
1551
+ return result;
1552
+ }
1553
+ return isJailHeld(pubkey) || blockedSince(pubkey, startedAt) ? this.localJailBlock(result.toolCallId) : result;
1554
+ }
1555
+ /** The server's jailed refusal, produced locally while the agent is held. */
1556
+ localJailBlock(toolCallId = generateToolCallId()) {
1557
+ return {
1558
+ verdict: "BLOCK",
1559
+ allow: false,
1560
+ actionType: "block",
1561
+ reason: "Agent is jailed. All actions are blocked until the organization owner unjails this agent.",
1562
+ confidence: 1,
1563
+ provider: "policy",
1564
+ latencyMs: 0,
1565
+ toolCallId,
1566
+ onChain: false,
1567
+ enforced: true,
1568
+ enforcementMode: "enforce",
1569
+ status: "",
1570
+ score: void 0,
1571
+ refusal: "BLOCK"
1572
+ };
1444
1573
  }
1445
1574
  /* ── audit_tool_call (redact → judge → decision) ───────────────────────── */
1446
1575
  /**
@@ -1492,6 +1621,9 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1492
1621
  reason: `The judge's reply failed its signature check (${errorMessage(err)}), so it was not trusted.`
1493
1622
  };
1494
1623
  }
1624
+ if (err instanceof JudgeRefusalError || err instanceof AtbashAPIError && isRefusalStatus(err.status)) {
1625
+ return { ...this.failJudge(errorMessage(err), err), allow: false };
1626
+ }
1495
1627
  return this.failJudge(errorMessage(err), err);
1496
1628
  }
1497
1629
  }
@@ -1521,6 +1653,15 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1521
1653
  * never overrides a refusal.
1522
1654
  */
1523
1655
  decide(result) {
1656
+ const inner = this.decideInner(result);
1657
+ if (!result.enforcementMode) return inner;
1658
+ return {
1659
+ ...inner,
1660
+ enforced: result.enforced,
1661
+ enforcementMode: result.enforcementMode
1662
+ };
1663
+ }
1664
+ decideInner(result) {
1524
1665
  const toolCallId = result.toolCallId;
1525
1666
  const deny = (verdict, reason) => ({
1526
1667
  allow: false,
@@ -1634,17 +1775,17 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1634
1775
  /**
1635
1776
  * Return the current status of a previously submitted judgment.
1636
1777
  *
1637
- * `chainOpts` names which chain the judgment was signed against. The
1638
- * server's GET /api/v1/judge routes to that chain when the SDK sends
1639
- * a `brid` query param; without it, the server falls back to public.
1640
- * Callers on the private chain must pass a `chainOpts` (or configure
1641
- * the client on the private chain) — otherwise polling a POSTed
1642
- * judgment on the private chain 404s at the server.
1778
+ * `chainOpts` names which chain the judgment was signed against; the SDK
1779
+ * sends it as the `brid` query param the server routes by. Without it the
1780
+ * read uses the same chain `judgeAction` signs for (see `callBrid`): the
1781
+ * client's org chain when it has an `orgName`, else the client default.
1782
+ * A judgment signed with an explicit per-call `chainOpts` (or a per-call
1783
+ * `orgName` on another chain) must be polled with that same `chainOpts`.
1643
1784
  */
1644
1785
  async getJudgmentStatus(judgmentId, agentPubkey, chainOpts) {
1645
1786
  const pk = agentPubkey ?? this.auth.pubkey;
1646
- const brid = this.bridFromChainOpts(chainOpts);
1647
1787
  return this.track("getJudgmentStatus", pk, async () => {
1788
+ const brid = await this.callBrid(chainOpts);
1648
1789
  const resp = await this.http.get(
1649
1790
  "/api/v1/judge",
1650
1791
  { tool_call_id: judgmentId, agent_pubkey: pk, brid },
@@ -1982,6 +2123,18 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1982
2123
  if (chainOpts?.network === "public") return PUBLIC_CHAIN.blockchainRid;
1983
2124
  return this.blockchainRid;
1984
2125
  }
2126
+ /**
2127
+ * The chain one call's transactions are signed for: the per-call override
2128
+ * when given, otherwise the client's org, otherwise the client default.
2129
+ * log_tool_call, judge_action and the judgment-status read all use this, so
2130
+ * a request never pairs transactions signed for two different chains.
2131
+ */
2132
+ async callBrid(chainOpts) {
2133
+ if (chainOpts?.blockchainRid || chainOpts?.network) {
2134
+ return this.bridFromChainOpts(chainOpts);
2135
+ }
2136
+ return await this.defaultOrgBrid() ?? this.blockchainRid;
2137
+ }
1985
2138
  /**
1986
2139
  * Resolve the dashboard chain used by agent metadata/policy reads.
1987
2140
  * Explicit per-call network overrides win; otherwise use the supplied org
@@ -2027,8 +2180,8 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
2027
2180
  async riskEnginePost(body, network, brid) {
2028
2181
  let resp;
2029
2182
  try {
2030
- const path5 = network ? `/api/risk-engine?network=${encodeURIComponent(network)}` : "/api/risk-engine";
2031
- resp = await this.http.post(path5, body, this.authHeaders(brid));
2183
+ const path4 = network ? `/api/risk-engine?network=${encodeURIComponent(network)}` : "/api/risk-engine";
2184
+ resp = await this.http.post(path4, body, this.authHeaders(brid));
2032
2185
  } catch (err) {
2033
2186
  throw this.transportError(err);
2034
2187
  }
@@ -2289,10 +2442,6 @@ function tryParseJson(text) {
2289
2442
  return void 0;
2290
2443
  }
2291
2444
  }
2292
- function parseJson(bytes) {
2293
- const parsed = tryParseJson(bytes.toString("utf-8"));
2294
- return parsed === void 0 ? {} : parsed;
2295
- }
2296
2445
  function requestPath(url) {
2297
2446
  if (!url) return void 0;
2298
2447
  try {
@@ -2571,10 +2720,10 @@ function resolveBlockchainRid(chainOpts) {
2571
2720
  'chainOpts.nodeUrls no longer selects a chain on its own \u2014 memory reaches the chain through the Atbash service, not a node. Set `blockchainRid`, or `network: "public" | "private"`.'
2572
2721
  );
2573
2722
  }
2574
- const config = chainForNetwork(
2723
+ const config2 = chainForNetwork(
2575
2724
  chainOpts?.network ?? forcedChainNetwork() ?? "private"
2576
2725
  );
2577
- return config.blockchainRid;
2726
+ return config2.blockchainRid;
2578
2727
  }
2579
2728
  async function commitMemoryVersion(plaintext, auth, opts) {
2580
2729
  const key = await deriveMemoryKey(auth.privkey);
@@ -2800,7 +2949,34 @@ function classifyMemoryWrite(event, ctx, opts = {}) {
2800
2949
  // src-ts/memory/guard.ts
2801
2950
  var import_node_fs4 = require("fs");
2802
2951
  var import_node_os4 = __toESM(require("os"));
2952
+
2953
+ // src-ts/memory/slot.ts
2803
2954
  var import_node_path4 = __toESM(require("path"));
2955
+ function asciiLower(s) {
2956
+ return s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) | 32));
2957
+ }
2958
+ function spelledAs(target, base, p = import_node_path4.default) {
2959
+ const resolved = p.resolve(base);
2960
+ if (p.sep !== "\\") return resolved;
2961
+ const prefix = resolved.replace(/[\\/]+$/, "");
2962
+ const t = asciiLower(target);
2963
+ const b = asciiLower(prefix);
2964
+ if (t === b || t.startsWith(b + "\\") || t.startsWith(b + "/")) {
2965
+ return target.slice(0, prefix.length);
2966
+ }
2967
+ return resolved;
2968
+ }
2969
+ function memorySlotOf(target, home2, root, extraHomes, p = import_node_path4.default) {
2970
+ const resolved = p.resolve(target);
2971
+ return native.memorySlotForPath(
2972
+ resolved,
2973
+ home2 ? spelledAs(resolved, home2, p) : "",
2974
+ root ? spelledAs(resolved, root, p) : void 0,
2975
+ extraHomes
2976
+ );
2977
+ }
2978
+
2979
+ // src-ts/memory/guard.ts
2804
2980
  function emitDebugProbe(event, ctx, memEntry, logger2) {
2805
2981
  if (!logger2?.info) return;
2806
2982
  const ev = event ?? {};
@@ -2826,7 +3002,7 @@ function emitDebugProbe(event, ctx, memEntry, logger2) {
2826
3002
  }
2827
3003
  function memorySlotFor(key, root, extraHomes) {
2828
3004
  const home2 = process.env.HOME || import_node_os4.default.homedir() || "";
2829
- return native.memorySlotForPath(import_node_path4.default.resolve(key), home2, root, extraHomes);
3005
+ return memorySlotOf(key, home2, root, extraHomes);
2830
3006
  }
2831
3007
  async function resultingDocument(memEntry, event, ctx) {
2832
3008
  const edits = native.extractMemoryEdits(event, ctx);
@@ -3579,12 +3755,7 @@ var MemoryGuardManager = class {
3579
3755
  */
3580
3756
  slotFor(target) {
3581
3757
  const home2 = process.env.HOME || import_node_os5.default.homedir() || "";
3582
- return native.memorySlotForPath(
3583
- import_node_path7.default.resolve(target),
3584
- home2,
3585
- this.opts.memoryRoot,
3586
- this.opts.agentHomeDirs
3587
- );
3758
+ return memorySlotOf(target, home2, this.opts.memoryRoot, this.opts.agentHomeDirs);
3588
3759
  }
3589
3760
  /**
3590
3761
  * Verify every memory file this call reads, each against its own chain slot.
@@ -3874,6 +4045,7 @@ function diffMemorySnapshots(before, after) {
3874
4045
  EciesDomain,
3875
4046
  HttpClient,
3876
4047
  HttpTransportError,
4048
+ JudgeRefusalError,
3877
4049
  KEY_FILENAMES,
3878
4050
  MemoryGuardManager,
3879
4051
  MemoryIntegrityError,
@@ -3892,6 +4064,7 @@ function diffMemorySnapshots(before, after) {
3892
4064
  columnAad,
3893
4065
  commitMemoryVersion,
3894
4066
  configureEvents,
4067
+ configureJailHold,
3895
4068
  containsEvasionCharacters,
3896
4069
  containsSecret,
3897
4070
  createFileLogger,
@@ -3920,12 +4093,14 @@ function diffMemorySnapshots(before, after) {
3920
4093
  getRollbackHistory,
3921
4094
  guardMemoryWrite,
3922
4095
  isEnvelope,
4096
+ isJailHeld,
3923
4097
  isValidPrivateKey,
3924
4098
  keyFingerprintOf,
3925
4099
  keyPathCandidates,
3926
4100
  loadAgent,
3927
4101
  loadAgentFromFile,
3928
4102
  loadUserConfig,
4103
+ memorySlotOf,
3929
4104
  normalizeActionForHash,
3930
4105
  normalizeActionType,
3931
4106
  normalizeForMatching,
@@ -3939,6 +4114,7 @@ function diffMemorySnapshots(before, after) {
3939
4114
  recordEvent,
3940
4115
  redactJsonStrings,
3941
4116
  redactSecrets,
4117
+ releaseJailHolds,
3942
4118
  resolve,
3943
4119
  resolveKeyPath,
3944
4120
  rollbackMemory,