@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/README.md +75 -12
- package/dist/browser.d.mts +145 -1
- package/dist/browser.mjs +131 -36
- package/dist/index.d.mts +145 -1
- package/dist/index.d.ts +145 -1
- package/dist/index.js +279 -51
- package/dist/index.mjs +276 -51
- package/index.js +54 -54
- package/package.json +9 -6
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 =
|
|
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 =
|
|
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
|
-
|
|
528
|
-
if (
|
|
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
|
|
533
|
-
|
|
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 () =>
|
|
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
|
-
|
|
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
|
-
|
|
588
|
-
await meterProvider.forceFlush();
|
|
711
|
+
await Promise.all([meterProvider?.forceFlush(), loggerProvider?.forceFlush()]);
|
|
589
712
|
}
|
|
590
713
|
async function shutdownTelemetry() {
|
|
591
|
-
|
|
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(
|
|
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
|
-
|
|
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:
|
|
1165
|
-
confidence:
|
|
1166
|
-
provider:
|
|
1167
|
-
latencyMs:
|
|
1168
|
-
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:
|
|
1172
|
-
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:
|
|
1397
|
-
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
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
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
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,
|
|
2461
|
-
if (!
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|