@atbash/sdk 0.16.1-dev.0 → 0.16.3-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.
@@ -721,7 +721,18 @@ declare class AtbashAPIError extends Error {
721
721
  readonly status: number;
722
722
  /** Raw response body text (may be empty). */
723
723
  readonly body: string;
724
- constructor(status: number, body: string, statusText?: string, endpoint?: string);
724
+ /**
725
+ * Request path this status came from, e.g. `/api/v1/judge` — absent when the
726
+ * request never reached a response (transport failures carry status 0).
727
+ *
728
+ * A status alone does not say what failed. Several routes answer one call, and
729
+ * more than one of them can 404: a caller reading only `404` cannot tell "this
730
+ * agent is not registered" from "the chain-routing lookup did not resolve", and
731
+ * has to guess from the body's prose — which a 404 raised before any handler
732
+ * ran does not carry. The path is the fact that settles it.
733
+ */
734
+ readonly path?: string;
735
+ constructor(status: number, body: string, statusText?: string, endpoint?: string, path?: string);
725
736
  }
726
737
  declare class SignatureVerificationError extends Error {
727
738
  constructor(message: string);
@@ -1068,6 +1079,13 @@ interface ClassifyMemoryWriteOptions {
1068
1079
  *
1069
1080
  * Empty-content writes return `null`: they can't carry a poisoning
1070
1081
  * payload, and the regular audit path still sees them.
1082
+ *
1083
+ * THROWS on host input it cannot read without side effects: a proxy, an accessor where a data
1084
+ * property is expected, a value with no safe projection, or one past the size bounds. Refusing is
1085
+ * deliberate - a silent `null` would report "not a memory write" for a call nobody could classify,
1086
+ * and memory protection would be skipped exactly where the input was strange. Callers that sit in a
1087
+ * host hook MUST catch it and turn it into a decision; `guardMemoryWrite` and
1088
+ * `MemoryGuardManager.handleBeforeToolCall` already do.
1071
1089
  */
1072
1090
  declare function classifyMemoryWrite(event: unknown, ctx: unknown, opts?: ClassifyMemoryWriteOptions): MemoryEntry | null;
1073
1091
 
@@ -1236,6 +1254,13 @@ interface ClassifyMemoryReadOptions {
1236
1254
  * Caller-supplied `patterns` are MERGED with the defaults (matches
1237
1255
  * Node's original behavior — extending in one plugin doesn't disable
1238
1256
  * standard coverage).
1257
+ *
1258
+ * THROWS on host input it cannot read without side effects: a proxy, an accessor where a data
1259
+ * property is expected, a value with no safe projection, or one past the size bounds. Refusing is
1260
+ * deliberate - a silent `null` would report "not a memory write" for a call nobody could classify,
1261
+ * and memory protection would be skipped exactly where the input was strange. Callers that sit in a
1262
+ * host hook MUST catch it and turn it into a decision; `guardMemoryWrite` and
1263
+ * `MemoryGuardManager.handleBeforeToolCall` already do.
1239
1264
  */
1240
1265
  declare function classifyMemoryRead(event: unknown, ctx: unknown, opts?: ClassifyMemoryReadOptions): boolean;
1241
1266
 
package/dist/browser.mjs CHANGED
@@ -44409,11 +44409,23 @@ var AtbashAPIError = class extends Error {
44409
44409
  status;
44410
44410
  /** Raw response body text (may be empty). */
44411
44411
  body;
44412
- constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT) {
44413
- super(enrich(status, body, statusText, endpoint));
44412
+ /**
44413
+ * Request path this status came from, e.g. `/api/v1/judge` — absent when the
44414
+ * request never reached a response (transport failures carry status 0).
44415
+ *
44416
+ * A status alone does not say what failed. Several routes answer one call, and
44417
+ * more than one of them can 404: a caller reading only `404` cannot tell "this
44418
+ * agent is not registered" from "the chain-routing lookup did not resolve", and
44419
+ * has to guess from the body's prose — which a 404 raised before any handler
44420
+ * ran does not carry. The path is the fact that settles it.
44421
+ */
44422
+ path;
44423
+ constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT, path4) {
44424
+ super(enrich(status, body, statusText, endpoint, path4));
44414
44425
  this.name = "AtbashAPIError";
44415
44426
  this.status = status;
44416
44427
  this.body = body;
44428
+ this.path = path4;
44417
44429
  }
44418
44430
  };
44419
44431
  var SignatureVerificationError = class extends Error {
@@ -44422,9 +44434,9 @@ var SignatureVerificationError = class extends Error {
44422
44434
  this.name = "SignatureVerificationError";
44423
44435
  }
44424
44436
  };
44425
- function enrich(status, body, statusText, endpoint) {
44437
+ function enrich(status, body, statusText, endpoint, path4) {
44426
44438
  const dashboard = endpoint.replace(/\/+$/, "") || DEFAULT_ENDPOINT;
44427
- let msg = `API error ${status}: ${body || statusText}`;
44439
+ let msg = `API error ${status}${path4 ? ` from ${path4}` : ""}: ${body || statusText}`;
44428
44440
  const lowered = body.toLowerCase();
44429
44441
  if (lowered.includes("agent not registered")) {
44430
44442
  msg += `
@@ -44516,6 +44528,13 @@ function classifyTransportError(err, url, method, timeoutMs) {
44516
44528
  { cause: err }
44517
44529
  );
44518
44530
  }
44531
+ if (code2 === "UND_ERR_CONNECT_TIMEOUT") {
44532
+ return new HttpTransportError(
44533
+ "timeout",
44534
+ `${method} ${url} could not establish a connection before the transport deadline \u2014 check the endpoint and network`,
44535
+ { cause: err }
44536
+ );
44537
+ }
44519
44538
  if (code2 === "ENOTFOUND" || code2 === "EAI_AGAIN") {
44520
44539
  return new HttpTransportError(
44521
44540
  "dns",
@@ -44780,7 +44799,6 @@ var Atbash = class _Atbash {
44780
44799
  try {
44781
44800
  setupTelemetry({
44782
44801
  enabled: true,
44783
- source: "sdk",
44784
44802
  endpoint: this.endpoint,
44785
44803
  getAuthHeaders: () => this.authHeaders()
44786
44804
  });
@@ -45668,7 +45686,8 @@ var Atbash = class _Atbash {
45668
45686
  resp.status,
45669
45687
  await safeText(resp),
45670
45688
  resp.statusText,
45671
- this.endpoint
45689
+ this.endpoint,
45690
+ requestPath(resp.url)
45672
45691
  );
45673
45692
  }
45674
45693
  /**
@@ -45810,6 +45829,14 @@ function parseJson(bytes) {
45810
45829
  const parsed = tryParseJson(bytes.toString("utf-8"));
45811
45830
  return parsed === void 0 ? {} : parsed;
45812
45831
  }
45832
+ function requestPath(url) {
45833
+ if (!url) return void 0;
45834
+ try {
45835
+ return new URL(url).pathname || void 0;
45836
+ } catch {
45837
+ return void 0;
45838
+ }
45839
+ }
45813
45840
  async function safeText(resp) {
45814
45841
  try {
45815
45842
  return await resp.text();
@@ -46121,7 +46148,22 @@ async function guardMemoryWrite(input) {
46121
46148
  debug: debug2 = false,
46122
46149
  logger: logger2
46123
46150
  } = input;
46124
- const memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
46151
+ let memEntry;
46152
+ try {
46153
+ memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
46154
+ } catch (err) {
46155
+ const why = err instanceof Error ? err.message : String(err);
46156
+ logger2?.warn?.("[atbash] memory classifier refused this tool call's input", { reason: why });
46157
+ if (!enforce) return { handled: false };
46158
+ return {
46159
+ handled: true,
46160
+ decision: {
46161
+ allow: false,
46162
+ block: true,
46163
+ reason: `Memory protection could not read this tool call (${why}); blocked because it could not be checked.`
46164
+ }
46165
+ };
46166
+ }
46125
46167
  if (debug2) emitDebugProbe(event, ctx, memEntry, logger2);
46126
46168
  if (!memEntry) return { handled: false };
46127
46169
  let scanResult;
package/dist/index.d.mts CHANGED
@@ -721,7 +721,18 @@ declare class AtbashAPIError extends Error {
721
721
  readonly status: number;
722
722
  /** Raw response body text (may be empty). */
723
723
  readonly body: string;
724
- constructor(status: number, body: string, statusText?: string, endpoint?: string);
724
+ /**
725
+ * Request path this status came from, e.g. `/api/v1/judge` — absent when the
726
+ * request never reached a response (transport failures carry status 0).
727
+ *
728
+ * A status alone does not say what failed. Several routes answer one call, and
729
+ * more than one of them can 404: a caller reading only `404` cannot tell "this
730
+ * agent is not registered" from "the chain-routing lookup did not resolve", and
731
+ * has to guess from the body's prose — which a 404 raised before any handler
732
+ * ran does not carry. The path is the fact that settles it.
733
+ */
734
+ readonly path?: string;
735
+ constructor(status: number, body: string, statusText?: string, endpoint?: string, path?: string);
725
736
  }
726
737
  declare class SignatureVerificationError extends Error {
727
738
  constructor(message: string);
@@ -1068,6 +1079,13 @@ interface ClassifyMemoryWriteOptions {
1068
1079
  *
1069
1080
  * Empty-content writes return `null`: they can't carry a poisoning
1070
1081
  * payload, and the regular audit path still sees them.
1082
+ *
1083
+ * THROWS on host input it cannot read without side effects: a proxy, an accessor where a data
1084
+ * property is expected, a value with no safe projection, or one past the size bounds. Refusing is
1085
+ * deliberate - a silent `null` would report "not a memory write" for a call nobody could classify,
1086
+ * and memory protection would be skipped exactly where the input was strange. Callers that sit in a
1087
+ * host hook MUST catch it and turn it into a decision; `guardMemoryWrite` and
1088
+ * `MemoryGuardManager.handleBeforeToolCall` already do.
1071
1089
  */
1072
1090
  declare function classifyMemoryWrite(event: unknown, ctx: unknown, opts?: ClassifyMemoryWriteOptions): MemoryEntry | null;
1073
1091
 
@@ -1236,6 +1254,13 @@ interface ClassifyMemoryReadOptions {
1236
1254
  * Caller-supplied `patterns` are MERGED with the defaults (matches
1237
1255
  * Node's original behavior — extending in one plugin doesn't disable
1238
1256
  * standard coverage).
1257
+ *
1258
+ * THROWS on host input it cannot read without side effects: a proxy, an accessor where a data
1259
+ * property is expected, a value with no safe projection, or one past the size bounds. Refusing is
1260
+ * deliberate - a silent `null` would report "not a memory write" for a call nobody could classify,
1261
+ * and memory protection would be skipped exactly where the input was strange. Callers that sit in a
1262
+ * host hook MUST catch it and turn it into a decision; `guardMemoryWrite` and
1263
+ * `MemoryGuardManager.handleBeforeToolCall` already do.
1239
1264
  */
1240
1265
  declare function classifyMemoryRead(event: unknown, ctx: unknown, opts?: ClassifyMemoryReadOptions): boolean;
1241
1266
 
package/dist/index.d.ts CHANGED
@@ -721,7 +721,18 @@ declare class AtbashAPIError extends Error {
721
721
  readonly status: number;
722
722
  /** Raw response body text (may be empty). */
723
723
  readonly body: string;
724
- constructor(status: number, body: string, statusText?: string, endpoint?: string);
724
+ /**
725
+ * Request path this status came from, e.g. `/api/v1/judge` — absent when the
726
+ * request never reached a response (transport failures carry status 0).
727
+ *
728
+ * A status alone does not say what failed. Several routes answer one call, and
729
+ * more than one of them can 404: a caller reading only `404` cannot tell "this
730
+ * agent is not registered" from "the chain-routing lookup did not resolve", and
731
+ * has to guess from the body's prose — which a 404 raised before any handler
732
+ * ran does not carry. The path is the fact that settles it.
733
+ */
734
+ readonly path?: string;
735
+ constructor(status: number, body: string, statusText?: string, endpoint?: string, path?: string);
725
736
  }
726
737
  declare class SignatureVerificationError extends Error {
727
738
  constructor(message: string);
@@ -1068,6 +1079,13 @@ interface ClassifyMemoryWriteOptions {
1068
1079
  *
1069
1080
  * Empty-content writes return `null`: they can't carry a poisoning
1070
1081
  * payload, and the regular audit path still sees them.
1082
+ *
1083
+ * THROWS on host input it cannot read without side effects: a proxy, an accessor where a data
1084
+ * property is expected, a value with no safe projection, or one past the size bounds. Refusing is
1085
+ * deliberate - a silent `null` would report "not a memory write" for a call nobody could classify,
1086
+ * and memory protection would be skipped exactly where the input was strange. Callers that sit in a
1087
+ * host hook MUST catch it and turn it into a decision; `guardMemoryWrite` and
1088
+ * `MemoryGuardManager.handleBeforeToolCall` already do.
1071
1089
  */
1072
1090
  declare function classifyMemoryWrite(event: unknown, ctx: unknown, opts?: ClassifyMemoryWriteOptions): MemoryEntry | null;
1073
1091
 
@@ -1236,6 +1254,13 @@ interface ClassifyMemoryReadOptions {
1236
1254
  * Caller-supplied `patterns` are MERGED with the defaults (matches
1237
1255
  * Node's original behavior — extending in one plugin doesn't disable
1238
1256
  * standard coverage).
1257
+ *
1258
+ * THROWS on host input it cannot read without side effects: a proxy, an accessor where a data
1259
+ * property is expected, a value with no safe projection, or one past the size bounds. Refusing is
1260
+ * deliberate - a silent `null` would report "not a memory write" for a call nobody could classify,
1261
+ * and memory protection would be skipped exactly where the input was strange. Callers that sit in a
1262
+ * host hook MUST catch it and turn it into a decision; `guardMemoryWrite` and
1263
+ * `MemoryGuardManager.handleBeforeToolCall` already do.
1239
1264
  */
1240
1265
  declare function classifyMemoryRead(event: unknown, ctx: unknown, opts?: ClassifyMemoryReadOptions): boolean;
1241
1266
 
package/dist/index.js CHANGED
@@ -3073,11 +3073,23 @@ var AtbashAPIError = class extends Error {
3073
3073
  status;
3074
3074
  /** Raw response body text (may be empty). */
3075
3075
  body;
3076
- constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT) {
3077
- super(enrich(status, body, statusText, endpoint));
3076
+ /**
3077
+ * Request path this status came from, e.g. `/api/v1/judge` — absent when the
3078
+ * request never reached a response (transport failures carry status 0).
3079
+ *
3080
+ * A status alone does not say what failed. Several routes answer one call, and
3081
+ * more than one of them can 404: a caller reading only `404` cannot tell "this
3082
+ * agent is not registered" from "the chain-routing lookup did not resolve", and
3083
+ * has to guess from the body's prose — which a 404 raised before any handler
3084
+ * ran does not carry. The path is the fact that settles it.
3085
+ */
3086
+ path;
3087
+ constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT, path7) {
3088
+ super(enrich(status, body, statusText, endpoint, path7));
3078
3089
  this.name = "AtbashAPIError";
3079
3090
  this.status = status;
3080
3091
  this.body = body;
3092
+ this.path = path7;
3081
3093
  }
3082
3094
  };
3083
3095
  var SignatureVerificationError = class extends Error {
@@ -3086,9 +3098,9 @@ var SignatureVerificationError = class extends Error {
3086
3098
  this.name = "SignatureVerificationError";
3087
3099
  }
3088
3100
  };
3089
- function enrich(status, body, statusText, endpoint) {
3101
+ function enrich(status, body, statusText, endpoint, path7) {
3090
3102
  const dashboard = endpoint.replace(/\/+$/, "") || DEFAULT_ENDPOINT;
3091
- let msg = `API error ${status}: ${body || statusText}`;
3103
+ let msg = `API error ${status}${path7 ? ` from ${path7}` : ""}: ${body || statusText}`;
3092
3104
  const lowered = body.toLowerCase();
3093
3105
  if (lowered.includes("agent not registered")) {
3094
3106
  msg += `
@@ -3178,6 +3190,13 @@ function classifyTransportError(err, url, method, timeoutMs) {
3178
3190
  { cause: err }
3179
3191
  );
3180
3192
  }
3193
+ if (code2 === "UND_ERR_CONNECT_TIMEOUT") {
3194
+ return new HttpTransportError(
3195
+ "timeout",
3196
+ `${method} ${url} could not establish a connection before the transport deadline \u2014 check the endpoint and network`,
3197
+ { cause: err }
3198
+ );
3199
+ }
3181
3200
  if (code2 === "ENOTFOUND" || code2 === "EAI_AGAIN") {
3182
3201
  return new HttpTransportError(
3183
3202
  "dns",
@@ -3342,6 +3361,7 @@ var meterProvider = null;
3342
3361
  var callCounter = null;
3343
3362
  var durationHistogram = null;
3344
3363
  var defaultSource = "sdk";
3364
+ var sourceExplicitlySet = false;
3345
3365
  function isTelemetryOptedOut() {
3346
3366
  const disabled = process.env.ATBASH_TELEMETRY_DISABLED?.trim().toLowerCase();
3347
3367
  if (disabled && ["1", "true", "yes", "on"].includes(disabled)) {
@@ -3365,11 +3385,14 @@ function autoInit() {
3365
3385
  }
3366
3386
  function setupTelemetry(config2) {
3367
3387
  if (!config2.enabled) return;
3368
- if (meterProvider) return;
3369
3388
  if (isTelemetryOptedOut()) return;
3389
+ if (config2.source && !sourceExplicitlySet) {
3390
+ defaultSource = config2.source;
3391
+ sourceExplicitlySet = true;
3392
+ }
3393
+ if (meterProvider) return;
3370
3394
  if (!config2.endpoint || !config2.getAuthHeaders) return;
3371
3395
  if (/^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(config2.endpoint)) return;
3372
- defaultSource = config2.source ?? "sdk";
3373
3396
  const proxyUrl = `${config2.endpoint.replace(/\/+$/, "")}/api/telemetry`;
3374
3397
  const getAuthHeaders = config2.getAuthHeaders;
3375
3398
  const exporter = new import_exporter_metrics_otlp_http.OTLPMetricExporter({
@@ -3422,6 +3445,8 @@ async function shutdownTelemetry() {
3422
3445
  meterProvider = null;
3423
3446
  callCounter = null;
3424
3447
  durationHistogram = null;
3448
+ defaultSource = "sdk";
3449
+ sourceExplicitlySet = false;
3425
3450
  }
3426
3451
 
3427
3452
  // src-ts/userConfig.ts
@@ -3612,7 +3637,6 @@ var Atbash = class _Atbash {
3612
3637
  try {
3613
3638
  setupTelemetry({
3614
3639
  enabled: true,
3615
- source: "sdk",
3616
3640
  endpoint: this.endpoint,
3617
3641
  getAuthHeaders: () => this.authHeaders()
3618
3642
  });
@@ -4500,7 +4524,8 @@ var Atbash = class _Atbash {
4500
4524
  resp.status,
4501
4525
  await safeText(resp),
4502
4526
  resp.statusText,
4503
- this.endpoint
4527
+ this.endpoint,
4528
+ requestPath(resp.url)
4504
4529
  );
4505
4530
  }
4506
4531
  /**
@@ -4642,6 +4667,14 @@ function parseJson(bytes) {
4642
4667
  const parsed = tryParseJson(bytes.toString("utf-8"));
4643
4668
  return parsed === void 0 ? {} : parsed;
4644
4669
  }
4670
+ function requestPath(url) {
4671
+ if (!url) return void 0;
4672
+ try {
4673
+ return new URL(url).pathname || void 0;
4674
+ } catch {
4675
+ return void 0;
4676
+ }
4677
+ }
4645
4678
  async function safeText(resp) {
4646
4679
  try {
4647
4680
  return await resp.text();
@@ -35299,9 +35332,9 @@ var ZodUnion = class extends ZodType {
35299
35332
  }
35300
35333
  };
35301
35334
  types.ZodUnion = ZodUnion;
35302
- ZodUnion.create = (types2, params) => {
35335
+ ZodUnion.create = (types3, params) => {
35303
35336
  return new ZodUnion({
35304
- options: types2,
35337
+ options: types3,
35305
35338
  typeName: ZodFirstPartyTypeKind.ZodUnion,
35306
35339
  ...processCreateParams(params)
35307
35340
  });
@@ -38758,9 +38791,9 @@ _nodeUtil.exports;
38758
38791
  var freeProcess = moduleExports && freeGlobal2.process;
38759
38792
  var nodeUtil2 = (function() {
38760
38793
  try {
38761
- var types2 = freeModule && freeModule.require && freeModule.require("util").types;
38762
- if (types2) {
38763
- return types2;
38794
+ var types3 = freeModule && freeModule.require && freeModule.require("util").types;
38795
+ if (types3) {
38796
+ return types3;
38764
38797
  }
38765
38798
  return freeProcess && freeProcess.binding && freeProcess.binding("util");
38766
38799
  } catch (e) {
@@ -43140,12 +43173,96 @@ async function rollbackMemory(toId, reason, auth, opts) {
43140
43173
  }
43141
43174
 
43142
43175
  // src-ts/memory/_json_safe.ts
43176
+ var import_node_util = require("util");
43177
+ var replacementKeys = [
43178
+ "newText",
43179
+ "new_string",
43180
+ "new_str",
43181
+ "newStr",
43182
+ "replacement"
43183
+ ];
43184
+ var argumentKeys = [
43185
+ "file_path",
43186
+ "path",
43187
+ "filename",
43188
+ "target",
43189
+ "file",
43190
+ "filePath",
43191
+ "content",
43192
+ ...replacementKeys,
43193
+ "value",
43194
+ "text"
43195
+ ];
43196
+ var maxPrototypeDepth = 32;
43197
+ var maxEditSlots = 65536;
43198
+ var maxTextUnits = 16 * 1024 * 1024;
43199
+ function invalid() {
43200
+ throw new TypeError("Unreadable or unsupported memory classifier input");
43201
+ }
43202
+ function dataProperty(value, key3) {
43203
+ let current = value;
43204
+ for (let depth = 0; current !== null; depth++) {
43205
+ if (depth >= maxPrototypeDepth || import_node_util.types.isProxy(current)) invalid();
43206
+ const descriptor = Object.getOwnPropertyDescriptor(current, key3);
43207
+ if (descriptor) {
43208
+ if (!("value" in descriptor)) invalid();
43209
+ return descriptor.value;
43210
+ }
43211
+ current = Object.getPrototypeOf(current);
43212
+ }
43213
+ return void 0;
43214
+ }
43143
43215
  function toJsonSafe(value) {
43144
- try {
43145
- return JSON.parse(JSON.stringify(value ?? null));
43146
- } catch {
43147
- return null;
43216
+ let textUnits = 0;
43217
+ function scalar(input) {
43218
+ if (input == null) return null;
43219
+ if (typeof input === "string") {
43220
+ textUnits += input.length;
43221
+ if (textUnits > maxTextUnits) invalid();
43222
+ return input;
43223
+ }
43224
+ if (typeof input === "boolean") return input;
43225
+ if (typeof input === "number" && Number.isFinite(input)) return input;
43226
+ return invalid();
43227
+ }
43228
+ function project(input, shape) {
43229
+ if (input == null || typeof input !== "object") return scalar(input);
43230
+ if (import_node_util.types.isProxy(input) || Array.isArray(input)) invalid();
43231
+ const output = /* @__PURE__ */ Object.create(null);
43232
+ const keys2 = shape === "envelope" ? ["toolName", "name"] : shape === "tool" ? ["name"] : shape === "args" ? argumentKeys : replacementKeys;
43233
+ for (const key3 of keys2) {
43234
+ const field = dataProperty(input, key3);
43235
+ if (field !== void 0) output[key3] = scalar(field);
43236
+ }
43237
+ if (shape === "envelope") {
43238
+ for (const key3 of ["params", "args", "arguments", "tool"]) {
43239
+ const field = dataProperty(input, key3);
43240
+ if (field !== void 0)
43241
+ output[key3] = project(field, key3 === "tool" ? "tool" : "args");
43242
+ }
43243
+ }
43244
+ if (shape === "args") {
43245
+ const edits = dataProperty(input, "edits");
43246
+ if (edits !== void 0) {
43247
+ if (edits !== null && typeof edits === "object" && import_node_util.types.isProxy(edits))
43248
+ invalid();
43249
+ if (Array.isArray(edits)) {
43250
+ const length = dataProperty(edits, "length");
43251
+ if (typeof length !== "number" || !Number.isInteger(length) || length < 0 || length > maxEditSlots)
43252
+ invalid();
43253
+ const entries = [];
43254
+ for (let index2 = 0; index2 < length; index2++) {
43255
+ entries.push(project(dataProperty(edits, String(index2)), "edit"));
43256
+ }
43257
+ output.edits = entries;
43258
+ } else {
43259
+ output.edits = scalar(edits);
43260
+ }
43261
+ }
43262
+ }
43263
+ return output;
43148
43264
  }
43265
+ return project(value, "envelope");
43149
43266
  }
43150
43267
 
43151
43268
  // src-ts/memory/classifier.ts
@@ -43198,7 +43315,22 @@ async function guardMemoryWrite(input) {
43198
43315
  debug: debug2 = false,
43199
43316
  logger: logger2
43200
43317
  } = input;
43201
- const memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
43318
+ let memEntry;
43319
+ try {
43320
+ memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
43321
+ } catch (err) {
43322
+ const why = err instanceof Error ? err.message : String(err);
43323
+ logger2?.warn?.("[atbash] memory classifier refused this tool call's input", { reason: why });
43324
+ if (!enforce) return { handled: false };
43325
+ return {
43326
+ handled: true,
43327
+ decision: {
43328
+ allow: false,
43329
+ block: true,
43330
+ reason: `Memory protection could not read this tool call (${why}); blocked because it could not be checked.`
43331
+ }
43332
+ };
43333
+ }
43202
43334
  if (debug2) emitDebugProbe(event, ctx, memEntry, logger2);
43203
43335
  if (!memEntry) return { handled: false };
43204
43336
  let scanResult;
@@ -43542,7 +43674,21 @@ var MemoryGuardManager = class {
43542
43674
  * that allows is a call the host still needs to judge.
43543
43675
  */
43544
43676
  async handleBeforeToolCall(event, ctx) {
43545
- if (classifyMemoryRead(event, ctx, this.opts.memoryReadClassifier)) {
43677
+ let isMemoryRead;
43678
+ try {
43679
+ isMemoryRead = classifyMemoryRead(event, ctx, this.opts.memoryReadClassifier);
43680
+ } catch (err) {
43681
+ const why = err instanceof Error ? err.message : String(err);
43682
+ this.logger.warn("[atbash] memory classifier refused this tool call's input", { reason: why });
43683
+ if (!this.enforce) return null;
43684
+ return {
43685
+ allow: false,
43686
+ block: true,
43687
+ blockReason: `Memory protection could not read this tool call (${why}); blocked because it could not be checked.`,
43688
+ audited: true
43689
+ };
43690
+ }
43691
+ if (isMemoryRead) {
43546
43692
  this.logger.info("[atbash] memory read intercepted \u2014 running sync check");
43547
43693
  return await this.handleMemoryRead(event, ctx);
43548
43694
  }
package/dist/index.mjs CHANGED
@@ -2977,11 +2977,23 @@ var AtbashAPIError = class extends Error {
2977
2977
  status;
2978
2978
  /** Raw response body text (may be empty). */
2979
2979
  body;
2980
- constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT) {
2981
- super(enrich(status, body, statusText, endpoint));
2980
+ /**
2981
+ * Request path this status came from, e.g. `/api/v1/judge` — absent when the
2982
+ * request never reached a response (transport failures carry status 0).
2983
+ *
2984
+ * A status alone does not say what failed. Several routes answer one call, and
2985
+ * more than one of them can 404: a caller reading only `404` cannot tell "this
2986
+ * agent is not registered" from "the chain-routing lookup did not resolve", and
2987
+ * has to guess from the body's prose — which a 404 raised before any handler
2988
+ * ran does not carry. The path is the fact that settles it.
2989
+ */
2990
+ path;
2991
+ constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT, path7) {
2992
+ super(enrich(status, body, statusText, endpoint, path7));
2982
2993
  this.name = "AtbashAPIError";
2983
2994
  this.status = status;
2984
2995
  this.body = body;
2996
+ this.path = path7;
2985
2997
  }
2986
2998
  };
2987
2999
  var SignatureVerificationError = class extends Error {
@@ -2990,9 +3002,9 @@ var SignatureVerificationError = class extends Error {
2990
3002
  this.name = "SignatureVerificationError";
2991
3003
  }
2992
3004
  };
2993
- function enrich(status, body, statusText, endpoint) {
3005
+ function enrich(status, body, statusText, endpoint, path7) {
2994
3006
  const dashboard = endpoint.replace(/\/+$/, "") || DEFAULT_ENDPOINT;
2995
- let msg = `API error ${status}: ${body || statusText}`;
3007
+ let msg = `API error ${status}${path7 ? ` from ${path7}` : ""}: ${body || statusText}`;
2996
3008
  const lowered = body.toLowerCase();
2997
3009
  if (lowered.includes("agent not registered")) {
2998
3010
  msg += `
@@ -3082,6 +3094,13 @@ function classifyTransportError(err, url, method, timeoutMs) {
3082
3094
  { cause: err }
3083
3095
  );
3084
3096
  }
3097
+ if (code2 === "UND_ERR_CONNECT_TIMEOUT") {
3098
+ return new HttpTransportError(
3099
+ "timeout",
3100
+ `${method} ${url} could not establish a connection before the transport deadline \u2014 check the endpoint and network`,
3101
+ { cause: err }
3102
+ );
3103
+ }
3085
3104
  if (code2 === "ENOTFOUND" || code2 === "EAI_AGAIN") {
3086
3105
  return new HttpTransportError(
3087
3106
  "dns",
@@ -3249,6 +3268,7 @@ var meterProvider = null;
3249
3268
  var callCounter = null;
3250
3269
  var durationHistogram = null;
3251
3270
  var defaultSource = "sdk";
3271
+ var sourceExplicitlySet = false;
3252
3272
  function isTelemetryOptedOut() {
3253
3273
  const disabled = process.env.ATBASH_TELEMETRY_DISABLED?.trim().toLowerCase();
3254
3274
  if (disabled && ["1", "true", "yes", "on"].includes(disabled)) {
@@ -3272,11 +3292,14 @@ function autoInit() {
3272
3292
  }
3273
3293
  function setupTelemetry(config2) {
3274
3294
  if (!config2.enabled) return;
3275
- if (meterProvider) return;
3276
3295
  if (isTelemetryOptedOut()) return;
3296
+ if (config2.source && !sourceExplicitlySet) {
3297
+ defaultSource = config2.source;
3298
+ sourceExplicitlySet = true;
3299
+ }
3300
+ if (meterProvider) return;
3277
3301
  if (!config2.endpoint || !config2.getAuthHeaders) return;
3278
3302
  if (/^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(config2.endpoint)) return;
3279
- defaultSource = config2.source ?? "sdk";
3280
3303
  const proxyUrl = `${config2.endpoint.replace(/\/+$/, "")}/api/telemetry`;
3281
3304
  const getAuthHeaders = config2.getAuthHeaders;
3282
3305
  const exporter = new OTLPMetricExporter({
@@ -3329,6 +3352,8 @@ async function shutdownTelemetry() {
3329
3352
  meterProvider = null;
3330
3353
  callCounter = null;
3331
3354
  durationHistogram = null;
3355
+ defaultSource = "sdk";
3356
+ sourceExplicitlySet = false;
3332
3357
  }
3333
3358
 
3334
3359
  // src-ts/userConfig.ts
@@ -3525,7 +3550,6 @@ var Atbash = class _Atbash {
3525
3550
  try {
3526
3551
  setupTelemetry({
3527
3552
  enabled: true,
3528
- source: "sdk",
3529
3553
  endpoint: this.endpoint,
3530
3554
  getAuthHeaders: () => this.authHeaders()
3531
3555
  });
@@ -4413,7 +4437,8 @@ var Atbash = class _Atbash {
4413
4437
  resp.status,
4414
4438
  await safeText(resp),
4415
4439
  resp.statusText,
4416
- this.endpoint
4440
+ this.endpoint,
4441
+ requestPath(resp.url)
4417
4442
  );
4418
4443
  }
4419
4444
  /**
@@ -4555,6 +4580,14 @@ function parseJson(bytes) {
4555
4580
  const parsed = tryParseJson(bytes.toString("utf-8"));
4556
4581
  return parsed === void 0 ? {} : parsed;
4557
4582
  }
4583
+ function requestPath(url) {
4584
+ if (!url) return void 0;
4585
+ try {
4586
+ return new URL(url).pathname || void 0;
4587
+ } catch {
4588
+ return void 0;
4589
+ }
4590
+ }
4558
4591
  async function safeText(resp) {
4559
4592
  try {
4560
4593
  return await resp.text();
@@ -35212,9 +35245,9 @@ var ZodUnion = class extends ZodType {
35212
35245
  }
35213
35246
  };
35214
35247
  types.ZodUnion = ZodUnion;
35215
- ZodUnion.create = (types2, params) => {
35248
+ ZodUnion.create = (types3, params) => {
35216
35249
  return new ZodUnion({
35217
- options: types2,
35250
+ options: types3,
35218
35251
  typeName: ZodFirstPartyTypeKind.ZodUnion,
35219
35252
  ...processCreateParams(params)
35220
35253
  });
@@ -38671,9 +38704,9 @@ _nodeUtil.exports;
38671
38704
  var freeProcess = moduleExports && freeGlobal2.process;
38672
38705
  var nodeUtil2 = (function() {
38673
38706
  try {
38674
- var types2 = freeModule && freeModule.require && freeModule.require("util").types;
38675
- if (types2) {
38676
- return types2;
38707
+ var types3 = freeModule && freeModule.require && freeModule.require("util").types;
38708
+ if (types3) {
38709
+ return types3;
38677
38710
  }
38678
38711
  return freeProcess && freeProcess.binding && freeProcess.binding("util");
38679
38712
  } catch (e) {
@@ -43053,12 +43086,96 @@ async function rollbackMemory(toId, reason, auth, opts) {
43053
43086
  }
43054
43087
 
43055
43088
  // src-ts/memory/_json_safe.ts
43089
+ import { types as types2 } from "util";
43090
+ var replacementKeys = [
43091
+ "newText",
43092
+ "new_string",
43093
+ "new_str",
43094
+ "newStr",
43095
+ "replacement"
43096
+ ];
43097
+ var argumentKeys = [
43098
+ "file_path",
43099
+ "path",
43100
+ "filename",
43101
+ "target",
43102
+ "file",
43103
+ "filePath",
43104
+ "content",
43105
+ ...replacementKeys,
43106
+ "value",
43107
+ "text"
43108
+ ];
43109
+ var maxPrototypeDepth = 32;
43110
+ var maxEditSlots = 65536;
43111
+ var maxTextUnits = 16 * 1024 * 1024;
43112
+ function invalid() {
43113
+ throw new TypeError("Unreadable or unsupported memory classifier input");
43114
+ }
43115
+ function dataProperty(value, key3) {
43116
+ let current = value;
43117
+ for (let depth = 0; current !== null; depth++) {
43118
+ if (depth >= maxPrototypeDepth || types2.isProxy(current)) invalid();
43119
+ const descriptor = Object.getOwnPropertyDescriptor(current, key3);
43120
+ if (descriptor) {
43121
+ if (!("value" in descriptor)) invalid();
43122
+ return descriptor.value;
43123
+ }
43124
+ current = Object.getPrototypeOf(current);
43125
+ }
43126
+ return void 0;
43127
+ }
43056
43128
  function toJsonSafe(value) {
43057
- try {
43058
- return JSON.parse(JSON.stringify(value ?? null));
43059
- } catch {
43060
- return null;
43129
+ let textUnits = 0;
43130
+ function scalar(input) {
43131
+ if (input == null) return null;
43132
+ if (typeof input === "string") {
43133
+ textUnits += input.length;
43134
+ if (textUnits > maxTextUnits) invalid();
43135
+ return input;
43136
+ }
43137
+ if (typeof input === "boolean") return input;
43138
+ if (typeof input === "number" && Number.isFinite(input)) return input;
43139
+ return invalid();
43140
+ }
43141
+ function project(input, shape) {
43142
+ if (input == null || typeof input !== "object") return scalar(input);
43143
+ if (types2.isProxy(input) || Array.isArray(input)) invalid();
43144
+ const output = /* @__PURE__ */ Object.create(null);
43145
+ const keys2 = shape === "envelope" ? ["toolName", "name"] : shape === "tool" ? ["name"] : shape === "args" ? argumentKeys : replacementKeys;
43146
+ for (const key3 of keys2) {
43147
+ const field = dataProperty(input, key3);
43148
+ if (field !== void 0) output[key3] = scalar(field);
43149
+ }
43150
+ if (shape === "envelope") {
43151
+ for (const key3 of ["params", "args", "arguments", "tool"]) {
43152
+ const field = dataProperty(input, key3);
43153
+ if (field !== void 0)
43154
+ output[key3] = project(field, key3 === "tool" ? "tool" : "args");
43155
+ }
43156
+ }
43157
+ if (shape === "args") {
43158
+ const edits = dataProperty(input, "edits");
43159
+ if (edits !== void 0) {
43160
+ if (edits !== null && typeof edits === "object" && types2.isProxy(edits))
43161
+ invalid();
43162
+ if (Array.isArray(edits)) {
43163
+ const length = dataProperty(edits, "length");
43164
+ if (typeof length !== "number" || !Number.isInteger(length) || length < 0 || length > maxEditSlots)
43165
+ invalid();
43166
+ const entries = [];
43167
+ for (let index2 = 0; index2 < length; index2++) {
43168
+ entries.push(project(dataProperty(edits, String(index2)), "edit"));
43169
+ }
43170
+ output.edits = entries;
43171
+ } else {
43172
+ output.edits = scalar(edits);
43173
+ }
43174
+ }
43175
+ }
43176
+ return output;
43061
43177
  }
43178
+ return project(value, "envelope");
43062
43179
  }
43063
43180
 
43064
43181
  // src-ts/memory/classifier.ts
@@ -43111,7 +43228,22 @@ async function guardMemoryWrite(input) {
43111
43228
  debug: debug2 = false,
43112
43229
  logger: logger2
43113
43230
  } = input;
43114
- const memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
43231
+ let memEntry;
43232
+ try {
43233
+ memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
43234
+ } catch (err) {
43235
+ const why = err instanceof Error ? err.message : String(err);
43236
+ logger2?.warn?.("[atbash] memory classifier refused this tool call's input", { reason: why });
43237
+ if (!enforce) return { handled: false };
43238
+ return {
43239
+ handled: true,
43240
+ decision: {
43241
+ allow: false,
43242
+ block: true,
43243
+ reason: `Memory protection could not read this tool call (${why}); blocked because it could not be checked.`
43244
+ }
43245
+ };
43246
+ }
43115
43247
  if (debug2) emitDebugProbe(event, ctx, memEntry, logger2);
43116
43248
  if (!memEntry) return { handled: false };
43117
43249
  let scanResult;
@@ -43455,7 +43587,21 @@ var MemoryGuardManager = class {
43455
43587
  * that allows is a call the host still needs to judge.
43456
43588
  */
43457
43589
  async handleBeforeToolCall(event, ctx) {
43458
- if (classifyMemoryRead(event, ctx, this.opts.memoryReadClassifier)) {
43590
+ let isMemoryRead;
43591
+ try {
43592
+ isMemoryRead = classifyMemoryRead(event, ctx, this.opts.memoryReadClassifier);
43593
+ } catch (err) {
43594
+ const why = err instanceof Error ? err.message : String(err);
43595
+ this.logger.warn("[atbash] memory classifier refused this tool call's input", { reason: why });
43596
+ if (!this.enforce) return null;
43597
+ return {
43598
+ allow: false,
43599
+ block: true,
43600
+ blockReason: `Memory protection could not read this tool call (${why}); blocked because it could not be checked.`,
43601
+ audited: true
43602
+ };
43603
+ }
43604
+ if (isMemoryRead) {
43459
43605
  this.logger.info("[atbash] memory read intercepted \u2014 running sync check");
43460
43606
  return await this.handleMemoryRead(event, ctx);
43461
43607
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atbash/sdk",
3
- "version": "0.16.1-dev.0",
3
+ "version": "0.16.3-dev.0",
4
4
  "description": "TypeScript SDK for Atbash — the safety layer that evaluates AI agent actions against operator-defined policies before execution.",
5
5
  "keywords": [
6
6
  "atbash",
@@ -89,10 +89,10 @@
89
89
  "typescript-eslint": "^8.62.1"
90
90
  },
91
91
  "optionalDependencies": {
92
- "@atbash/sdk-linux-x64-gnu": "0.16.1-dev.0",
93
- "@atbash/sdk-linux-arm64-gnu": "0.16.1-dev.0",
94
- "@atbash/sdk-linux-x64-musl": "0.16.1-dev.0",
95
- "@atbash/sdk-darwin-arm64": "0.16.1-dev.0",
96
- "@atbash/sdk-win32-x64-msvc": "0.16.1-dev.0"
92
+ "@atbash/sdk-linux-x64-gnu": "0.16.3-dev.0",
93
+ "@atbash/sdk-linux-arm64-gnu": "0.16.3-dev.0",
94
+ "@atbash/sdk-linux-x64-musl": "0.16.3-dev.0",
95
+ "@atbash/sdk-darwin-arm64": "0.16.3-dev.0",
96
+ "@atbash/sdk-win32-x64-msvc": "0.16.3-dev.0"
97
97
  }
98
98
  }