@atbash/sdk 0.18.0-dev.0 → 0.19.1-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.mjs CHANGED
@@ -374,9 +374,19 @@ function loadAgentFromFile(keyPath) {
374
374
  }
375
375
 
376
376
  // src-ts/normalize.ts
377
+ function wireText(raw, fallback = "") {
378
+ if (raw === null || raw === void 0) return fallback;
379
+ if (typeof raw === "object" || typeof raw === "function") return fallback;
380
+ return String(raw);
381
+ }
382
+ function wireNumber(raw, fallback = 0) {
383
+ if (raw === null || raw === void 0) return fallback;
384
+ if (typeof raw === "object" || typeof raw === "function") return fallback;
385
+ return Number(raw);
386
+ }
377
387
  function normalizeVerdict(raw) {
378
388
  if (raw === null || raw === void 0) return "No verdict";
379
- const v = String(raw).trim().toUpperCase();
389
+ const v = wireText(raw).trim().toUpperCase();
380
390
  if (v === "ALLOW" || v === "GREEN") return "ALLOW";
381
391
  if (v === "HOLD" || v === "YELLOW") return "HOLD";
382
392
  if (v === "BLOCK" || v === "RED") return "BLOCK";
@@ -386,7 +396,7 @@ function normalizeActionType(raw) {
386
396
  return typeof raw === "string" ? raw.trim().toLowerCase() : "";
387
397
  }
388
398
  function normalizeStatus(raw) {
389
- const s = String(raw ?? "").toLowerCase();
399
+ const s = wireText(raw).toLowerCase();
390
400
  if (s === "pending" || s === "answered" || s === "error") return s;
391
401
  return "error";
392
402
  }
@@ -473,6 +483,59 @@ function canonicalAllow(data) {
473
483
  return verdict === "ALLOW" || verdict === "GREEN";
474
484
  }
475
485
 
486
+ // src-ts/opentel/events.ts
487
+ var SLOW_MS = 1500;
488
+ var SUCCESS_SAMPLE_RATE = 10;
489
+ var logger = null;
490
+ var eventsEnabled = false;
491
+ var sdkVersion = "";
492
+ var eventSource = "sdk";
493
+ var hashAgentKey = null;
494
+ function eventsOptedOut() {
495
+ const flag = process.env.ATBASH_TELEMETRY_EVENTS_DISABLED?.trim().toLowerCase();
496
+ return Boolean(flag && ["1", "true", "yes", "on"].includes(flag));
497
+ }
498
+ function configureEvents(next, opts = {}) {
499
+ logger = next;
500
+ eventsEnabled = next !== null && !eventsOptedOut();
501
+ if (opts.version) sdkVersion = opts.version;
502
+ if (opts.source) eventSource = opts.source;
503
+ hashAgentKey = opts.hashAgentKey ?? null;
504
+ }
505
+ function eventsAreOn() {
506
+ return eventsEnabled && logger !== null;
507
+ }
508
+ function sampleRateFor(e) {
509
+ if (e.status === "error") return 1;
510
+ if (e.durationMs >= SLOW_MS) return 1;
511
+ return Math.random() < 1 / SUCCESS_SAMPLE_RATE ? SUCCESS_SAMPLE_RATE : 0;
512
+ }
513
+ function recordEvent(e) {
514
+ if (!eventsAreOn()) return;
515
+ const sampleRate = sampleRateFor(e);
516
+ if (sampleRate === 0) return;
517
+ try {
518
+ const attributes = {
519
+ "function.name": e.functionName,
520
+ "duration_ms": Math.round(e.durationMs),
521
+ status: e.status,
522
+ source: eventSource,
523
+ // Honeycomb reweights counts and percentiles by this. It is per-event
524
+ // because the sampling is biased by outcome and latency.
525
+ sampleRate
526
+ };
527
+ if (sdkVersion) attributes["sdk.version"] = sdkVersion;
528
+ if (e.agentPubkey && hashAgentKey) attributes["agent.hash"] = hashAgentKey(e.agentPubkey);
529
+ if (e.chain) attributes["chain"] = e.chain;
530
+ if (e.errorClass) attributes["error.class"] = e.errorClass;
531
+ if (e.errorStatus !== void 0) attributes["error.status"] = e.errorStatus;
532
+ if (e.errorPath) attributes["error.path"] = e.errorPath;
533
+ if (e.outcome) attributes["outcome"] = e.outcome;
534
+ logger.emit({ body: e.functionName, attributes });
535
+ } catch {
536
+ }
537
+ }
538
+
476
539
  // src-ts/opentel/telemetry.ts
477
540
  import { readFileSync as readFileSync2 } from "fs";
478
541
  import { homedir as homedir2 } from "os";
@@ -486,7 +549,11 @@ import {
486
549
  MeterProvider,
487
550
  PeriodicExportingMetricReader
488
551
  } from "@opentelemetry/sdk-metrics";
552
+ import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
553
+ import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
554
+ import { createHash } from "crypto";
489
555
  var meterProvider = null;
556
+ var loggerProvider = null;
490
557
  var callCounter = null;
491
558
  var durationHistogram = null;
492
559
  var defaultSource = "sdk";
@@ -513,6 +580,41 @@ function autoInit() {
513
580
  if (isTelemetryOptedOut()) return;
514
581
  setupTelemetry({ enabled: true });
515
582
  }
583
+ function defaultAgentKeyHasher(pubkeyHex) {
584
+ return createHash("sha256").update(pubkeyHex).digest("hex");
585
+ }
586
+ var counterHasher = null;
587
+ function hashAgentKeyOrDrop(pubkeyHex) {
588
+ return counterHasher ? counterHasher(pubkeyHex) : void 0;
589
+ }
590
+ function customCollector() {
591
+ const url = process.env.ATBASH_TELEMETRY_COLLECTOR_URL?.trim();
592
+ if (!url) return null;
593
+ const loopback = /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(:|\/|$)/i.test(url);
594
+ if (loopback && process.env.ATBASH_TELEMETRY_ALLOW_LOOPBACK !== "1") {
595
+ throw new Error(
596
+ "ATBASH_TELEMETRY_COLLECTOR_URL is a loopback address; set ATBASH_TELEMETRY_ALLOW_LOOPBACK=1 to allow it."
597
+ );
598
+ }
599
+ if (!loopback && !/^https:\/\//i.test(url)) {
600
+ throw new Error("ATBASH_TELEMETRY_COLLECTOR_URL must be https (loopback excepted).");
601
+ }
602
+ let headers = {};
603
+ const raw = process.env.ATBASH_TELEMETRY_COLLECTOR_HEADERS?.trim();
604
+ if (raw) {
605
+ try {
606
+ const parsed = JSON.parse(raw);
607
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
608
+ headers = Object.fromEntries(
609
+ Object.entries(parsed).map(([k, v]) => [k, String(v)])
610
+ );
611
+ }
612
+ } catch {
613
+ throw new Error("ATBASH_TELEMETRY_COLLECTOR_HEADERS must be a JSON object of header names to values.");
614
+ }
615
+ }
616
+ return { url: url.replace(/\/+$/, ""), headers: () => headers };
617
+ }
516
618
  function setupTelemetry(config) {
517
619
  if (!config.enabled) return;
518
620
  if (isTelemetryOptedOut()) return;
@@ -524,16 +626,20 @@ function setupTelemetry(config) {
524
626
  requestedExportIntervalMs = config.exportIntervalMs;
525
627
  }
526
628
  if (meterProvider) return;
527
- if (!config.endpoint || !config.getAuthHeaders) return;
528
- if (/^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(
629
+ const collector = customCollector();
630
+ if (!collector && (!config.endpoint || !config.getAuthHeaders)) return;
631
+ if (!collector && /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(
529
632
  config.endpoint
530
633
  ))
531
634
  return;
532
- const proxyUrl = `${config.endpoint.replace(/\/+$/, "")}/api/telemetry`;
533
- const getAuthHeaders = config.getAuthHeaders;
635
+ const destination = collector ?? {
636
+ url: `${config.endpoint.replace(/\/+$/, "")}/api/telemetry`,
637
+ headers: () => config.getAuthHeaders()
638
+ };
639
+ const proxyUrl = destination.url;
534
640
  const exporter = new OTLPMetricExporter({
535
- url: proxyUrl,
536
- headers: async () => getAuthHeaders(),
641
+ url: collector ? `${proxyUrl}/v1/metrics` : proxyUrl,
642
+ headers: async () => destination.headers(),
537
643
  // DELTA, not the OTel default of CUMULATIVE.
538
644
  //
539
645
  // A cumulative counter reports a running total, and the backend recovers
@@ -565,6 +671,21 @@ function setupTelemetry(config) {
565
671
  description: "SDK function execution duration",
566
672
  unit: "ms"
567
673
  });
674
+ counterHasher = config.hashAgentKey ?? defaultAgentKeyHasher;
675
+ const logExporter = new OTLPLogExporter({
676
+ // A real collector takes OTLP at its own signal paths; the Atbash proxy
677
+ // takes a query parameter, because it is one authenticated route.
678
+ url: collector ? `${proxyUrl}/v1/logs` : `${proxyUrl}?signal=logs`,
679
+ headers: async () => destination.headers()
680
+ });
681
+ loggerProvider = new LoggerProvider({
682
+ resource: resourceFromAttributes({ "service.name": "atbash-sdk" }),
683
+ processors: [new BatchLogRecordProcessor({ exporter: logExporter })]
684
+ });
685
+ configureEvents(loggerProvider.getLogger("atbash-sdk"), {
686
+ source: config.source ?? defaultSource,
687
+ hashAgentKey: config.hashAgentKey ?? defaultAgentKeyHasher
688
+ });
568
689
  }
569
690
  function recordCall(functionName, source, agentPubkey) {
570
691
  autoInit();
@@ -572,7 +693,10 @@ function recordCall(functionName, source, agentPubkey) {
572
693
  callCounter.add(1, {
573
694
  "function.name": functionName,
574
695
  source: source ?? defaultSource,
575
- ...agentPubkey && { "agent.pubkey": agentPubkey }
696
+ // Hashed on this path too. The counters and the events export to the same
697
+ // place in the same request, so a raw key here would make hashing it on
698
+ // the event beside it pointless.
699
+ ...agentPubkey && { "agent.hash": hashAgentKeyOrDrop(agentPubkey) }
576
700
  });
577
701
  }
578
702
  function recordDuration(functionName, durationMs, status, source) {
@@ -584,15 +708,16 @@ function recordDuration(functionName, durationMs, status, source) {
584
708
  });
585
709
  }
586
710
  async function flushTelemetry() {
587
- if (!meterProvider) return;
588
- await meterProvider.forceFlush();
711
+ await Promise.all([meterProvider?.forceFlush(), loggerProvider?.forceFlush()]);
589
712
  }
590
713
  async function shutdownTelemetry() {
591
- if (!meterProvider) return;
592
- await meterProvider.shutdown();
714
+ await Promise.all([meterProvider?.shutdown(), loggerProvider?.shutdown()]);
593
715
  meterProvider = null;
716
+ loggerProvider = null;
594
717
  callCounter = null;
595
718
  durationHistogram = null;
719
+ counterHasher = null;
720
+ configureEvents(null);
596
721
  defaultSource = "sdk";
597
722
  sourceExplicitlySet = false;
598
723
  requestedExportIntervalMs = void 0;
@@ -700,6 +825,13 @@ function resolveConstructorChain(options) {
700
825
  }
701
826
  return chainForNetwork(options.network ?? forcedChainNetwork() ?? "private");
702
827
  }
828
+ function errorDetail(err) {
829
+ if (!(err instanceof AtbashAPIError)) return {};
830
+ return {
831
+ errorStatus: err.status,
832
+ ...err.path ? { errorPath: err.path } : {}
833
+ };
834
+ }
703
835
  var Atbash = class _Atbash {
704
836
  auth;
705
837
  endpoint;
@@ -910,7 +1042,7 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
910
1042
  *
911
1043
  * @param logger - The host logger, when one was supplied.
912
1044
  */
913
- static warnOnStrandedKey(logger) {
1045
+ static warnOnStrandedKey(logger2) {
914
1046
  const stranded = strandedAgentKey();
915
1047
  if (!stranded) return;
916
1048
  let identity;
@@ -919,7 +1051,7 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
919
1051
  } catch {
920
1052
  identity = "(unreadable)";
921
1053
  }
922
- logger?.warn?.(
1054
+ logger2?.warn?.(
923
1055
  `[atbash] "agentKey" in config.json is ignored \u2014 settings do not choose the signing key. It holds ${identity}; this client signs as the key file or ATBASH_AGENT_KEY instead. Move it into the key file, or delete the field, to silence this.`
924
1056
  );
925
1057
  }
@@ -1161,15 +1293,15 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1161
1293
  verdict: normalizeVerdict(data.verdict),
1162
1294
  allow: this.executable(data),
1163
1295
  actionType: normalizeActionType(data.action_type),
1164
- reason: String(data.reason ?? ""),
1165
- confidence: Number(data.confidence ?? 0),
1166
- provider: String(data.provider ?? ""),
1167
- latencyMs: Number(data.latency_ms ?? 0),
1168
- toolCallId: String(data.tool_call_id ?? logResult.toolCallId),
1296
+ reason: wireText(data.reason),
1297
+ confidence: wireNumber(data.confidence),
1298
+ provider: wireText(data.provider),
1299
+ latencyMs: wireNumber(data.latency_ms),
1300
+ toolCallId: wireText(data.tool_call_id, logResult.toolCallId),
1169
1301
  onChain: Boolean(data.on_chain),
1170
1302
  enforced: Boolean(data.enforced),
1171
- enforcementMode: String(data.enforcement_mode ?? ""),
1172
- status: String(data.status ?? ""),
1303
+ enforcementMode: wireText(data.enforcement_mode),
1304
+ status: wireText(data.status),
1173
1305
  score,
1174
1306
  refusal: explicitRefusal(data)
1175
1307
  };
@@ -1393,8 +1525,8 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1393
1525
  status: normalizeStatus(data.status),
1394
1526
  verdict: normalizeVerdict(data.verdict),
1395
1527
  allow: this.executable(data),
1396
- reason: String(data.reason ?? ""),
1397
- judgmentId: String(data.judgmentId ?? judgmentId),
1528
+ reason: wireText(data.reason),
1529
+ judgmentId: wireText(data.judgmentId, judgmentId),
1398
1530
  onChain: optBool(data.onChain),
1399
1531
  cached: optBool(data.cached),
1400
1532
  responseTimeMs: optNumber(data.responseTimeMs)
@@ -1678,10 +1810,28 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
1678
1810
  recordCall(name, void 0, agentPubkey);
1679
1811
  try {
1680
1812
  const result = await fn();
1681
- recordDuration(name, performance.now() - start, "success");
1813
+ const durationMs = performance.now() - start;
1814
+ recordDuration(name, durationMs, "success");
1815
+ recordEvent({
1816
+ functionName: name,
1817
+ durationMs,
1818
+ status: "success",
1819
+ agentPubkey,
1820
+ chain: this.blockchainRid
1821
+ });
1682
1822
  return result;
1683
1823
  } catch (err) {
1684
- recordDuration(name, performance.now() - start, "error");
1824
+ const durationMs = performance.now() - start;
1825
+ recordDuration(name, durationMs, "error");
1826
+ recordEvent({
1827
+ functionName: name,
1828
+ durationMs,
1829
+ status: "error",
1830
+ agentPubkey,
1831
+ chain: this.blockchainRid,
1832
+ errorClass: err instanceof Error ? err.constructor.name : typeof err,
1833
+ ...errorDetail(err)
1834
+ });
1685
1835
  throw err;
1686
1836
  }
1687
1837
  }
@@ -1983,10 +2133,12 @@ function isRecord2(v) {
1983
2133
  }
1984
2134
  function optString(v) {
1985
2135
  if (v === null || v === void 0) return void 0;
2136
+ if (typeof v === "object" || typeof v === "function") return void 0;
1986
2137
  return typeof v === "string" ? v : String(v);
1987
2138
  }
1988
2139
  function optNumber(v) {
1989
2140
  if (v === null || v === void 0) return void 0;
2141
+ if (typeof v === "object" || typeof v === "function") return void 0;
1990
2142
  const n = Number(v);
1991
2143
  return Number.isFinite(n) ? n : void 0;
1992
2144
  }
@@ -2174,6 +2326,47 @@ async function scanMemoryBatch(entries, auth, opts) {
2174
2326
  return results;
2175
2327
  }
2176
2328
 
2329
+ // src-ts/memory/telemetry.ts
2330
+ function metricName(action) {
2331
+ return `memory.${action}`;
2332
+ }
2333
+ function recordMemoryCall(action, startedAt, agentPubkey, status, err) {
2334
+ const name = metricName(action);
2335
+ const durationMs = performance.now() - startedAt;
2336
+ recordCall(name, void 0, agentPubkey);
2337
+ recordDuration(name, durationMs, status);
2338
+ recordEvent({
2339
+ functionName: name,
2340
+ durationMs,
2341
+ status,
2342
+ agentPubkey,
2343
+ ...err !== void 0 && {
2344
+ errorClass: err instanceof Error ? err.constructor.name : typeof err,
2345
+ // The chain transport is where the failures are, so the status and the
2346
+ // route are the whole of what makes one diagnosable.
2347
+ ...err instanceof AtbashAPIError && {
2348
+ errorStatus: err.status,
2349
+ ...err.path ? { errorPath: err.path } : {}
2350
+ }
2351
+ }
2352
+ });
2353
+ }
2354
+ function recordMemoryGuard(verdict, committed, blocked, startedAt, agentPubkey) {
2355
+ const durationMs = performance.now() - startedAt;
2356
+ recordCall("memory.guard", void 0, agentPubkey);
2357
+ recordDuration("memory.guard", durationMs, blocked ? "error" : "success");
2358
+ recordEvent({
2359
+ functionName: "memory.guard",
2360
+ durationMs,
2361
+ // A block is the outcome worth keeping every one of; the sampler keeps
2362
+ // errors whole, and a refused write is an error from the agent's side.
2363
+ status: blocked ? "error" : "success",
2364
+ agentPubkey,
2365
+ errorClass: blocked ? `memory-blocked:${verdict}` : void 0,
2366
+ outcome: `${verdict}/${committed ? "committed" : "uncommitted"}`
2367
+ });
2368
+ }
2369
+
2177
2370
  // src-ts/memory/transport.ts
2178
2371
  var DEFAULT_TIMEOUT_MS = 12e4;
2179
2372
  function transport(opts) {
@@ -2186,24 +2379,39 @@ async function failure(action, resp) {
2186
2379
  );
2187
2380
  }
2188
2381
  async function memoryQuery(action, auth, blockchainRid, opts, filePath, extra = {}) {
2382
+ const start = performance.now();
2189
2383
  const params = new URLSearchParams({ action, agent: auth.pubkey });
2190
2384
  if (filePath !== null) params.set("file_path", filePath);
2191
2385
  for (const [key, value] of Object.entries(extra)) params.set(key, String(value));
2192
- const resp = await transport(opts).get(
2193
- `/api/risk-engine?${params.toString()}`,
2194
- void 0,
2195
- agentAuthHeaders(auth, blockchainRid)
2196
- );
2197
- if (resp.status !== 200) throw await failure(action, resp);
2198
- return resp.json();
2386
+ try {
2387
+ const resp = await transport(opts).get(
2388
+ `/api/risk-engine?${params.toString()}`,
2389
+ void 0,
2390
+ agentAuthHeaders(auth, blockchainRid)
2391
+ );
2392
+ if (resp.status !== 200) throw await failure(action, resp);
2393
+ const body = await resp.json();
2394
+ recordMemoryCall(action, start, auth.pubkey, "success");
2395
+ return body;
2396
+ } catch (err) {
2397
+ recordMemoryCall(action, start, auth.pubkey, "error", err);
2398
+ throw err;
2399
+ }
2199
2400
  }
2200
2401
  async function memorySubmit(action, auth, opts, signedField, signedHex) {
2201
- const resp = await transport(opts).post("/api/risk-engine", {
2202
- action,
2203
- agentPubkey: auth.pubkey,
2204
- [signedField]: signedHex
2205
- });
2206
- if (resp.status !== 200) throw await failure(action, resp);
2402
+ const start = performance.now();
2403
+ try {
2404
+ const resp = await transport(opts).post("/api/risk-engine", {
2405
+ action,
2406
+ agentPubkey: auth.pubkey,
2407
+ [signedField]: signedHex
2408
+ });
2409
+ if (resp.status !== 200) throw await failure(action, resp);
2410
+ recordMemoryCall(action, start, auth.pubkey, "success");
2411
+ } catch (err) {
2412
+ recordMemoryCall(action, start, auth.pubkey, "error", err);
2413
+ throw err;
2414
+ }
2207
2415
  }
2208
2416
 
2209
2417
  // src-ts/memory/chain.ts
@@ -2457,8 +2665,8 @@ function classifyMemoryWrite(event, ctx, opts = {}) {
2457
2665
  import { promises as fs } from "fs";
2458
2666
  import os from "os";
2459
2667
  import path from "path";
2460
- function emitDebugProbe(event, ctx, memEntry, logger) {
2461
- if (!logger?.info) return;
2668
+ function emitDebugProbe(event, ctx, memEntry, logger2) {
2669
+ if (!logger2?.info) return;
2462
2670
  const ev = event ?? {};
2463
2671
  const c = ctx ?? {};
2464
2672
  const argsCandidate = ev.params ?? c.params ?? ev.args ?? c.args ?? ev.arguments ?? c.arguments;
@@ -2468,7 +2676,7 @@ function emitDebugProbe(event, ctx, memEntry, logger) {
2468
2676
  const editsArr = Array.isArray(argsObj.edits) ? argsObj.edits : null;
2469
2677
  const firstEditKeys = editsArr && editsArr[0] && typeof editsArr[0] === "object" ? Object.keys(editsArr[0]).slice(0, 10) : null;
2470
2678
  const tool = ev.toolName ?? c.tool?.name ?? c.toolName ?? c.name ?? "(none)";
2471
- logger.info("[atbash][probe] before_tool_call", {
2679
+ logger2.info("[atbash][probe] before_tool_call", {
2472
2680
  toolName: tool,
2473
2681
  eventKeys: Object.keys(ev).slice(0, 20),
2474
2682
  ctxKeys: Object.keys(c).slice(0, 20),
@@ -2496,6 +2704,20 @@ async function resultingDocument(memEntry, event, ctx) {
2496
2704
  return native.applyMemoryEdits(current, edits);
2497
2705
  }
2498
2706
  async function guardMemoryWrite(input) {
2707
+ const start = performance.now();
2708
+ const result = await guardMemoryWriteInner(input);
2709
+ if (result.handled) {
2710
+ recordMemoryGuard(
2711
+ result.scanResult?.verdict ?? "unscanned",
2712
+ result.committed === true,
2713
+ result.decision?.block === true,
2714
+ start,
2715
+ input.auth.pubkey
2716
+ );
2717
+ }
2718
+ return result;
2719
+ }
2720
+ async function guardMemoryWriteInner(input) {
2499
2721
  const {
2500
2722
  event,
2501
2723
  ctx,
@@ -2509,7 +2731,7 @@ async function guardMemoryWrite(input) {
2509
2731
  toolNames,
2510
2732
  enforce = true,
2511
2733
  debug = false,
2512
- logger,
2734
+ logger: logger2,
2513
2735
  agentHomeDirs,
2514
2736
  memoryRoot
2515
2737
  } = input;
@@ -2518,7 +2740,7 @@ async function guardMemoryWrite(input) {
2518
2740
  memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
2519
2741
  } catch (err) {
2520
2742
  const why = err instanceof Error ? err.message : String(err);
2521
- logger?.warn?.("[atbash] memory classifier refused this tool call's input", { reason: why });
2743
+ logger2?.warn?.("[atbash] memory classifier refused this tool call's input", { reason: why });
2522
2744
  if (!enforce) return { handled: false };
2523
2745
  return {
2524
2746
  handled: true,
@@ -2529,14 +2751,14 @@ async function guardMemoryWrite(input) {
2529
2751
  }
2530
2752
  };
2531
2753
  }
2532
- if (debug) emitDebugProbe(event, ctx, memEntry, logger);
2754
+ if (debug) emitDebugProbe(event, ctx, memEntry, logger2);
2533
2755
  if (!memEntry) return { handled: false };
2534
2756
  let document;
2535
2757
  try {
2536
2758
  document = await resultingDocument(memEntry, event, ctx);
2537
2759
  } catch (err) {
2538
2760
  const reason = err instanceof Error ? err.message : String(err);
2539
- logger?.warn?.("[atbash] memory edit could not be applied", { path: memEntry.key, reason });
2761
+ logger2?.warn?.("[atbash] memory edit could not be applied", { path: memEntry.key, reason });
2540
2762
  return {
2541
2763
  handled: true,
2542
2764
  decision: enforce ? { allow: false, block: true, reason: `Memory edit refused: ${reason}` } : { allow: true, reason: `Memory edit refused: ${reason}` },
@@ -2554,7 +2776,7 @@ async function guardMemoryWrite(input) {
2554
2776
  });
2555
2777
  } catch (err) {
2556
2778
  const reason = err instanceof Error ? err.message : String(err);
2557
- logger?.warn?.("[atbash] memory scan failed", {
2779
+ logger2?.warn?.("[atbash] memory scan failed", {
2558
2780
  path: memEntry.key,
2559
2781
  reason
2560
2782
  });
@@ -2564,7 +2786,7 @@ async function guardMemoryWrite(input) {
2564
2786
  };
2565
2787
  }
2566
2788
  if (scanResult.verdict === "red") {
2567
- logger?.warn?.("[atbash] memory BLOCK", {
2789
+ logger2?.warn?.("[atbash] memory BLOCK", {
2568
2790
  path: memEntry.key,
2569
2791
  reason: scanResult.reason
2570
2792
  });
@@ -2586,7 +2808,7 @@ async function guardMemoryWrite(input) {
2586
2808
  }
2587
2809
  const filePath = memorySlotFor(memEntry.key, memoryRoot, agentHomeDirs);
2588
2810
  if (filePath === null) {
2589
- logger?.info?.(
2811
+ logger2?.info?.(
2590
2812
  `[atbash] memory scanned but not recorded \u2014 ${memEntry.key} is not inside an agent home (${(agentHomeDirs ?? native.defaultAgentHomeDirs()).join(", ")}) in your home directory, so it has no chain slot of its own.`,
2591
2813
  { path: memEntry.key, verdict: scanResult.verdict, score: scanResult.score }
2592
2814
  );
@@ -2608,7 +2830,7 @@ async function guardMemoryWrite(input) {
2608
2830
  });
2609
2831
  } catch (err) {
2610
2832
  const reason = err instanceof Error ? err.message : String(err);
2611
- logger?.warn?.("[atbash] memory commit to chain failed", {
2833
+ logger2?.warn?.("[atbash] memory commit to chain failed", {
2612
2834
  path: memEntry.key,
2613
2835
  filePath,
2614
2836
  reason
@@ -2620,7 +2842,7 @@ async function guardMemoryWrite(input) {
2620
2842
  committed: false
2621
2843
  };
2622
2844
  }
2623
- logger?.info?.(
2845
+ logger2?.info?.(
2624
2846
  scanResult.verdict === "yellow" ? "[atbash] memory ALLOW (yellow, low score)" : "[atbash] memory ALLOW",
2625
2847
  {
2626
2848
  path: memEntry.key,
@@ -3532,6 +3754,7 @@ export {
3532
3754
  classifyMemoryWrite,
3533
3755
  columnAad,
3534
3756
  commitMemoryVersion,
3757
+ configureEvents,
3535
3758
  containsEvasionCharacters,
3536
3759
  containsSecret,
3537
3760
  createFileLogger,
@@ -3547,6 +3770,7 @@ export {
3547
3770
  encryptForOrg,
3548
3771
  encryptMemoryContent,
3549
3772
  encryptedLength,
3773
+ eventsAreOn,
3550
3774
  flushTelemetry,
3551
3775
  generateKeypair,
3552
3776
  getActiveAgentMemory,
@@ -3575,6 +3799,7 @@ export {
3575
3799
  pubkeyToHex,
3576
3800
  recordCall,
3577
3801
  recordDuration,
3802
+ recordEvent,
3578
3803
  redactJsonStrings,
3579
3804
  redactSecrets,
3580
3805
  resolve,