@atbash/sdk 0.18.0-dev.0 → 0.19.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/README.md +75 -12
- package/dist/browser.d.mts +145 -1
- package/dist/browser.mjs +108 -25
- package/dist/index.d.mts +145 -1
- package/dist/index.d.ts +145 -1
- package/dist/index.js +256 -40
- package/dist/index.mjs +253 -40
- package/package.json +9 -6
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ __export(src_ts_exports, {
|
|
|
59
59
|
classifyMemoryWrite: () => classifyMemoryWrite,
|
|
60
60
|
columnAad: () => columnAad,
|
|
61
61
|
commitMemoryVersion: () => commitMemoryVersion,
|
|
62
|
+
configureEvents: () => configureEvents,
|
|
62
63
|
containsEvasionCharacters: () => containsEvasionCharacters,
|
|
63
64
|
containsSecret: () => containsSecret,
|
|
64
65
|
createFileLogger: () => createFileLogger,
|
|
@@ -74,6 +75,7 @@ __export(src_ts_exports, {
|
|
|
74
75
|
encryptForOrg: () => encryptForOrg,
|
|
75
76
|
encryptMemoryContent: () => encryptMemoryContent,
|
|
76
77
|
encryptedLength: () => encryptedLength,
|
|
78
|
+
eventsAreOn: () => eventsAreOn,
|
|
77
79
|
flushTelemetry: () => flushTelemetry,
|
|
78
80
|
generateKeypair: () => generateKeypair,
|
|
79
81
|
getActiveAgentMemory: () => getActiveAgentMemory,
|
|
@@ -102,6 +104,7 @@ __export(src_ts_exports, {
|
|
|
102
104
|
pubkeyToHex: () => pubkeyToHex,
|
|
103
105
|
recordCall: () => recordCall,
|
|
104
106
|
recordDuration: () => recordDuration,
|
|
107
|
+
recordEvent: () => recordEvent,
|
|
105
108
|
redactJsonStrings: () => redactJsonStrings,
|
|
106
109
|
redactSecrets: () => redactSecrets,
|
|
107
110
|
resolve: () => resolve,
|
|
@@ -597,6 +600,59 @@ function canonicalAllow(data) {
|
|
|
597
600
|
return verdict === "ALLOW" || verdict === "GREEN";
|
|
598
601
|
}
|
|
599
602
|
|
|
603
|
+
// src-ts/opentel/events.ts
|
|
604
|
+
var SLOW_MS = 1500;
|
|
605
|
+
var SUCCESS_SAMPLE_RATE = 10;
|
|
606
|
+
var logger = null;
|
|
607
|
+
var eventsEnabled = false;
|
|
608
|
+
var sdkVersion = "";
|
|
609
|
+
var eventSource = "sdk";
|
|
610
|
+
var hashAgentKey = null;
|
|
611
|
+
function eventsOptedOut() {
|
|
612
|
+
const flag = process.env.ATBASH_TELEMETRY_EVENTS_DISABLED?.trim().toLowerCase();
|
|
613
|
+
return Boolean(flag && ["1", "true", "yes", "on"].includes(flag));
|
|
614
|
+
}
|
|
615
|
+
function configureEvents(next, opts = {}) {
|
|
616
|
+
logger = next;
|
|
617
|
+
eventsEnabled = next !== null && !eventsOptedOut();
|
|
618
|
+
if (opts.version) sdkVersion = opts.version;
|
|
619
|
+
if (opts.source) eventSource = opts.source;
|
|
620
|
+
hashAgentKey = opts.hashAgentKey ?? null;
|
|
621
|
+
}
|
|
622
|
+
function eventsAreOn() {
|
|
623
|
+
return eventsEnabled && logger !== null;
|
|
624
|
+
}
|
|
625
|
+
function sampleRateFor(e) {
|
|
626
|
+
if (e.status === "error") return 1;
|
|
627
|
+
if (e.durationMs >= SLOW_MS) return 1;
|
|
628
|
+
return Math.random() < 1 / SUCCESS_SAMPLE_RATE ? SUCCESS_SAMPLE_RATE : 0;
|
|
629
|
+
}
|
|
630
|
+
function recordEvent(e) {
|
|
631
|
+
if (!eventsAreOn()) return;
|
|
632
|
+
const sampleRate = sampleRateFor(e);
|
|
633
|
+
if (sampleRate === 0) return;
|
|
634
|
+
try {
|
|
635
|
+
const attributes = {
|
|
636
|
+
"function.name": e.functionName,
|
|
637
|
+
"duration_ms": Math.round(e.durationMs),
|
|
638
|
+
status: e.status,
|
|
639
|
+
source: eventSource,
|
|
640
|
+
// Honeycomb reweights counts and percentiles by this. It is per-event
|
|
641
|
+
// because the sampling is biased by outcome and latency.
|
|
642
|
+
sampleRate
|
|
643
|
+
};
|
|
644
|
+
if (sdkVersion) attributes["sdk.version"] = sdkVersion;
|
|
645
|
+
if (e.agentPubkey && hashAgentKey) attributes["agent.hash"] = hashAgentKey(e.agentPubkey);
|
|
646
|
+
if (e.chain) attributes["chain"] = e.chain;
|
|
647
|
+
if (e.errorClass) attributes["error.class"] = e.errorClass;
|
|
648
|
+
if (e.errorStatus !== void 0) attributes["error.status"] = e.errorStatus;
|
|
649
|
+
if (e.errorPath) attributes["error.path"] = e.errorPath;
|
|
650
|
+
if (e.outcome) attributes["outcome"] = e.outcome;
|
|
651
|
+
logger.emit({ body: e.functionName, attributes });
|
|
652
|
+
} catch {
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
600
656
|
// src-ts/opentel/telemetry.ts
|
|
601
657
|
var import_node_fs2 = require("fs");
|
|
602
658
|
var import_node_os2 = require("os");
|
|
@@ -604,7 +660,11 @@ var import_node_path2 = require("path");
|
|
|
604
660
|
var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
605
661
|
var import_resources = require("@opentelemetry/resources");
|
|
606
662
|
var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
663
|
+
var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
|
|
664
|
+
var import_sdk_logs = require("@opentelemetry/sdk-logs");
|
|
665
|
+
var import_node_crypto = require("crypto");
|
|
607
666
|
var meterProvider = null;
|
|
667
|
+
var loggerProvider = null;
|
|
608
668
|
var callCounter = null;
|
|
609
669
|
var durationHistogram = null;
|
|
610
670
|
var defaultSource = "sdk";
|
|
@@ -631,6 +691,41 @@ function autoInit() {
|
|
|
631
691
|
if (isTelemetryOptedOut()) return;
|
|
632
692
|
setupTelemetry({ enabled: true });
|
|
633
693
|
}
|
|
694
|
+
function defaultAgentKeyHasher(pubkeyHex) {
|
|
695
|
+
return (0, import_node_crypto.createHash)("sha256").update(pubkeyHex).digest("hex");
|
|
696
|
+
}
|
|
697
|
+
var counterHasher = null;
|
|
698
|
+
function hashAgentKeyOrDrop(pubkeyHex) {
|
|
699
|
+
return counterHasher ? counterHasher(pubkeyHex) : void 0;
|
|
700
|
+
}
|
|
701
|
+
function customCollector() {
|
|
702
|
+
const url = process.env.ATBASH_TELEMETRY_COLLECTOR_URL?.trim();
|
|
703
|
+
if (!url) return null;
|
|
704
|
+
const loopback = /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(:|\/|$)/i.test(url);
|
|
705
|
+
if (loopback && process.env.ATBASH_TELEMETRY_ALLOW_LOOPBACK !== "1") {
|
|
706
|
+
throw new Error(
|
|
707
|
+
"ATBASH_TELEMETRY_COLLECTOR_URL is a loopback address; set ATBASH_TELEMETRY_ALLOW_LOOPBACK=1 to allow it."
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
if (!loopback && !/^https:\/\//i.test(url)) {
|
|
711
|
+
throw new Error("ATBASH_TELEMETRY_COLLECTOR_URL must be https (loopback excepted).");
|
|
712
|
+
}
|
|
713
|
+
let headers = {};
|
|
714
|
+
const raw = process.env.ATBASH_TELEMETRY_COLLECTOR_HEADERS?.trim();
|
|
715
|
+
if (raw) {
|
|
716
|
+
try {
|
|
717
|
+
const parsed = JSON.parse(raw);
|
|
718
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
719
|
+
headers = Object.fromEntries(
|
|
720
|
+
Object.entries(parsed).map(([k, v]) => [k, String(v)])
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
} catch {
|
|
724
|
+
throw new Error("ATBASH_TELEMETRY_COLLECTOR_HEADERS must be a JSON object of header names to values.");
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return { url: url.replace(/\/+$/, ""), headers: () => headers };
|
|
728
|
+
}
|
|
634
729
|
function setupTelemetry(config) {
|
|
635
730
|
if (!config.enabled) return;
|
|
636
731
|
if (isTelemetryOptedOut()) return;
|
|
@@ -642,16 +737,20 @@ function setupTelemetry(config) {
|
|
|
642
737
|
requestedExportIntervalMs = config.exportIntervalMs;
|
|
643
738
|
}
|
|
644
739
|
if (meterProvider) return;
|
|
645
|
-
|
|
646
|
-
if (
|
|
740
|
+
const collector = customCollector();
|
|
741
|
+
if (!collector && (!config.endpoint || !config.getAuthHeaders)) return;
|
|
742
|
+
if (!collector && /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(
|
|
647
743
|
config.endpoint
|
|
648
744
|
))
|
|
649
745
|
return;
|
|
650
|
-
const
|
|
651
|
-
|
|
746
|
+
const destination = collector ?? {
|
|
747
|
+
url: `${config.endpoint.replace(/\/+$/, "")}/api/telemetry`,
|
|
748
|
+
headers: () => config.getAuthHeaders()
|
|
749
|
+
};
|
|
750
|
+
const proxyUrl = destination.url;
|
|
652
751
|
const exporter = new import_exporter_metrics_otlp_http.OTLPMetricExporter({
|
|
653
|
-
url: proxyUrl,
|
|
654
|
-
headers: async () =>
|
|
752
|
+
url: collector ? `${proxyUrl}/v1/metrics` : proxyUrl,
|
|
753
|
+
headers: async () => destination.headers(),
|
|
655
754
|
// DELTA, not the OTel default of CUMULATIVE.
|
|
656
755
|
//
|
|
657
756
|
// A cumulative counter reports a running total, and the backend recovers
|
|
@@ -683,6 +782,21 @@ function setupTelemetry(config) {
|
|
|
683
782
|
description: "SDK function execution duration",
|
|
684
783
|
unit: "ms"
|
|
685
784
|
});
|
|
785
|
+
counterHasher = config.hashAgentKey ?? defaultAgentKeyHasher;
|
|
786
|
+
const logExporter = new import_exporter_logs_otlp_http.OTLPLogExporter({
|
|
787
|
+
// A real collector takes OTLP at its own signal paths; the Atbash proxy
|
|
788
|
+
// takes a query parameter, because it is one authenticated route.
|
|
789
|
+
url: collector ? `${proxyUrl}/v1/logs` : `${proxyUrl}?signal=logs`,
|
|
790
|
+
headers: async () => destination.headers()
|
|
791
|
+
});
|
|
792
|
+
loggerProvider = new import_sdk_logs.LoggerProvider({
|
|
793
|
+
resource: (0, import_resources.resourceFromAttributes)({ "service.name": "atbash-sdk" }),
|
|
794
|
+
processors: [new import_sdk_logs.BatchLogRecordProcessor({ exporter: logExporter })]
|
|
795
|
+
});
|
|
796
|
+
configureEvents(loggerProvider.getLogger("atbash-sdk"), {
|
|
797
|
+
source: config.source ?? defaultSource,
|
|
798
|
+
hashAgentKey: config.hashAgentKey ?? defaultAgentKeyHasher
|
|
799
|
+
});
|
|
686
800
|
}
|
|
687
801
|
function recordCall(functionName, source, agentPubkey) {
|
|
688
802
|
autoInit();
|
|
@@ -690,7 +804,10 @@ function recordCall(functionName, source, agentPubkey) {
|
|
|
690
804
|
callCounter.add(1, {
|
|
691
805
|
"function.name": functionName,
|
|
692
806
|
source: source ?? defaultSource,
|
|
693
|
-
|
|
807
|
+
// Hashed on this path too. The counters and the events export to the same
|
|
808
|
+
// place in the same request, so a raw key here would make hashing it on
|
|
809
|
+
// the event beside it pointless.
|
|
810
|
+
...agentPubkey && { "agent.hash": hashAgentKeyOrDrop(agentPubkey) }
|
|
694
811
|
});
|
|
695
812
|
}
|
|
696
813
|
function recordDuration(functionName, durationMs, status, source) {
|
|
@@ -702,15 +819,16 @@ function recordDuration(functionName, durationMs, status, source) {
|
|
|
702
819
|
});
|
|
703
820
|
}
|
|
704
821
|
async function flushTelemetry() {
|
|
705
|
-
|
|
706
|
-
await meterProvider.forceFlush();
|
|
822
|
+
await Promise.all([meterProvider?.forceFlush(), loggerProvider?.forceFlush()]);
|
|
707
823
|
}
|
|
708
824
|
async function shutdownTelemetry() {
|
|
709
|
-
|
|
710
|
-
await meterProvider.shutdown();
|
|
825
|
+
await Promise.all([meterProvider?.shutdown(), loggerProvider?.shutdown()]);
|
|
711
826
|
meterProvider = null;
|
|
827
|
+
loggerProvider = null;
|
|
712
828
|
callCounter = null;
|
|
713
829
|
durationHistogram = null;
|
|
830
|
+
counterHasher = null;
|
|
831
|
+
configureEvents(null);
|
|
714
832
|
defaultSource = "sdk";
|
|
715
833
|
sourceExplicitlySet = false;
|
|
716
834
|
requestedExportIntervalMs = void 0;
|
|
@@ -812,6 +930,13 @@ function resolveConstructorChain(options) {
|
|
|
812
930
|
}
|
|
813
931
|
return chainForNetwork(options.network ?? forcedChainNetwork() ?? "private");
|
|
814
932
|
}
|
|
933
|
+
function errorDetail(err) {
|
|
934
|
+
if (!(err instanceof AtbashAPIError)) return {};
|
|
935
|
+
return {
|
|
936
|
+
errorStatus: err.status,
|
|
937
|
+
...err.path ? { errorPath: err.path } : {}
|
|
938
|
+
};
|
|
939
|
+
}
|
|
815
940
|
var Atbash = class _Atbash {
|
|
816
941
|
auth;
|
|
817
942
|
endpoint;
|
|
@@ -1022,7 +1147,7 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1022
1147
|
*
|
|
1023
1148
|
* @param logger - The host logger, when one was supplied.
|
|
1024
1149
|
*/
|
|
1025
|
-
static warnOnStrandedKey(
|
|
1150
|
+
static warnOnStrandedKey(logger2) {
|
|
1026
1151
|
const stranded = strandedAgentKey();
|
|
1027
1152
|
if (!stranded) return;
|
|
1028
1153
|
let identity;
|
|
@@ -1031,7 +1156,7 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1031
1156
|
} catch {
|
|
1032
1157
|
identity = "(unreadable)";
|
|
1033
1158
|
}
|
|
1034
|
-
|
|
1159
|
+
logger2?.warn?.(
|
|
1035
1160
|
`[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.`
|
|
1036
1161
|
);
|
|
1037
1162
|
}
|
|
@@ -1790,10 +1915,28 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1790
1915
|
recordCall(name, void 0, agentPubkey);
|
|
1791
1916
|
try {
|
|
1792
1917
|
const result = await fn();
|
|
1793
|
-
|
|
1918
|
+
const durationMs = performance.now() - start;
|
|
1919
|
+
recordDuration(name, durationMs, "success");
|
|
1920
|
+
recordEvent({
|
|
1921
|
+
functionName: name,
|
|
1922
|
+
durationMs,
|
|
1923
|
+
status: "success",
|
|
1924
|
+
agentPubkey,
|
|
1925
|
+
chain: this.blockchainRid
|
|
1926
|
+
});
|
|
1794
1927
|
return result;
|
|
1795
1928
|
} catch (err) {
|
|
1796
|
-
|
|
1929
|
+
const durationMs = performance.now() - start;
|
|
1930
|
+
recordDuration(name, durationMs, "error");
|
|
1931
|
+
recordEvent({
|
|
1932
|
+
functionName: name,
|
|
1933
|
+
durationMs,
|
|
1934
|
+
status: "error",
|
|
1935
|
+
agentPubkey,
|
|
1936
|
+
chain: this.blockchainRid,
|
|
1937
|
+
errorClass: err instanceof Error ? err.constructor.name : typeof err,
|
|
1938
|
+
...errorDetail(err)
|
|
1939
|
+
});
|
|
1797
1940
|
throw err;
|
|
1798
1941
|
}
|
|
1799
1942
|
}
|
|
@@ -2286,6 +2429,47 @@ async function scanMemoryBatch(entries, auth, opts) {
|
|
|
2286
2429
|
return results;
|
|
2287
2430
|
}
|
|
2288
2431
|
|
|
2432
|
+
// src-ts/memory/telemetry.ts
|
|
2433
|
+
function metricName(action) {
|
|
2434
|
+
return `memory.${action}`;
|
|
2435
|
+
}
|
|
2436
|
+
function recordMemoryCall(action, startedAt, agentPubkey, status, err) {
|
|
2437
|
+
const name = metricName(action);
|
|
2438
|
+
const durationMs = performance.now() - startedAt;
|
|
2439
|
+
recordCall(name, void 0, agentPubkey);
|
|
2440
|
+
recordDuration(name, durationMs, status);
|
|
2441
|
+
recordEvent({
|
|
2442
|
+
functionName: name,
|
|
2443
|
+
durationMs,
|
|
2444
|
+
status,
|
|
2445
|
+
agentPubkey,
|
|
2446
|
+
...err !== void 0 && {
|
|
2447
|
+
errorClass: err instanceof Error ? err.constructor.name : typeof err,
|
|
2448
|
+
// The chain transport is where the failures are, so the status and the
|
|
2449
|
+
// route are the whole of what makes one diagnosable.
|
|
2450
|
+
...err instanceof AtbashAPIError && {
|
|
2451
|
+
errorStatus: err.status,
|
|
2452
|
+
...err.path ? { errorPath: err.path } : {}
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
function recordMemoryGuard(verdict, committed, blocked, startedAt, agentPubkey) {
|
|
2458
|
+
const durationMs = performance.now() - startedAt;
|
|
2459
|
+
recordCall("memory.guard", void 0, agentPubkey);
|
|
2460
|
+
recordDuration("memory.guard", durationMs, blocked ? "error" : "success");
|
|
2461
|
+
recordEvent({
|
|
2462
|
+
functionName: "memory.guard",
|
|
2463
|
+
durationMs,
|
|
2464
|
+
// A block is the outcome worth keeping every one of; the sampler keeps
|
|
2465
|
+
// errors whole, and a refused write is an error from the agent's side.
|
|
2466
|
+
status: blocked ? "error" : "success",
|
|
2467
|
+
agentPubkey,
|
|
2468
|
+
errorClass: blocked ? `memory-blocked:${verdict}` : void 0,
|
|
2469
|
+
outcome: `${verdict}/${committed ? "committed" : "uncommitted"}`
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2289
2473
|
// src-ts/memory/transport.ts
|
|
2290
2474
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
2291
2475
|
function transport(opts) {
|
|
@@ -2298,24 +2482,39 @@ async function failure(action, resp) {
|
|
|
2298
2482
|
);
|
|
2299
2483
|
}
|
|
2300
2484
|
async function memoryQuery(action, auth, blockchainRid, opts, filePath, extra = {}) {
|
|
2485
|
+
const start = performance.now();
|
|
2301
2486
|
const params = new URLSearchParams({ action, agent: auth.pubkey });
|
|
2302
2487
|
if (filePath !== null) params.set("file_path", filePath);
|
|
2303
2488
|
for (const [key, value] of Object.entries(extra)) params.set(key, String(value));
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2489
|
+
try {
|
|
2490
|
+
const resp = await transport(opts).get(
|
|
2491
|
+
`/api/risk-engine?${params.toString()}`,
|
|
2492
|
+
void 0,
|
|
2493
|
+
agentAuthHeaders(auth, blockchainRid)
|
|
2494
|
+
);
|
|
2495
|
+
if (resp.status !== 200) throw await failure(action, resp);
|
|
2496
|
+
const body = await resp.json();
|
|
2497
|
+
recordMemoryCall(action, start, auth.pubkey, "success");
|
|
2498
|
+
return body;
|
|
2499
|
+
} catch (err) {
|
|
2500
|
+
recordMemoryCall(action, start, auth.pubkey, "error", err);
|
|
2501
|
+
throw err;
|
|
2502
|
+
}
|
|
2311
2503
|
}
|
|
2312
2504
|
async function memorySubmit(action, auth, opts, signedField, signedHex) {
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2505
|
+
const start = performance.now();
|
|
2506
|
+
try {
|
|
2507
|
+
const resp = await transport(opts).post("/api/risk-engine", {
|
|
2508
|
+
action,
|
|
2509
|
+
agentPubkey: auth.pubkey,
|
|
2510
|
+
[signedField]: signedHex
|
|
2511
|
+
});
|
|
2512
|
+
if (resp.status !== 200) throw await failure(action, resp);
|
|
2513
|
+
recordMemoryCall(action, start, auth.pubkey, "success");
|
|
2514
|
+
} catch (err) {
|
|
2515
|
+
recordMemoryCall(action, start, auth.pubkey, "error", err);
|
|
2516
|
+
throw err;
|
|
2517
|
+
}
|
|
2319
2518
|
}
|
|
2320
2519
|
|
|
2321
2520
|
// src-ts/memory/chain.ts
|
|
@@ -2569,8 +2768,8 @@ function classifyMemoryWrite(event, ctx, opts = {}) {
|
|
|
2569
2768
|
var import_node_fs4 = require("fs");
|
|
2570
2769
|
var import_node_os4 = __toESM(require("os"));
|
|
2571
2770
|
var import_node_path4 = __toESM(require("path"));
|
|
2572
|
-
function emitDebugProbe(event, ctx, memEntry,
|
|
2573
|
-
if (!
|
|
2771
|
+
function emitDebugProbe(event, ctx, memEntry, logger2) {
|
|
2772
|
+
if (!logger2?.info) return;
|
|
2574
2773
|
const ev = event ?? {};
|
|
2575
2774
|
const c = ctx ?? {};
|
|
2576
2775
|
const argsCandidate = ev.params ?? c.params ?? ev.args ?? c.args ?? ev.arguments ?? c.arguments;
|
|
@@ -2580,7 +2779,7 @@ function emitDebugProbe(event, ctx, memEntry, logger) {
|
|
|
2580
2779
|
const editsArr = Array.isArray(argsObj.edits) ? argsObj.edits : null;
|
|
2581
2780
|
const firstEditKeys = editsArr && editsArr[0] && typeof editsArr[0] === "object" ? Object.keys(editsArr[0]).slice(0, 10) : null;
|
|
2582
2781
|
const tool = ev.toolName ?? c.tool?.name ?? c.toolName ?? c.name ?? "(none)";
|
|
2583
|
-
|
|
2782
|
+
logger2.info("[atbash][probe] before_tool_call", {
|
|
2584
2783
|
toolName: tool,
|
|
2585
2784
|
eventKeys: Object.keys(ev).slice(0, 20),
|
|
2586
2785
|
ctxKeys: Object.keys(c).slice(0, 20),
|
|
@@ -2608,6 +2807,20 @@ async function resultingDocument(memEntry, event, ctx) {
|
|
|
2608
2807
|
return native.applyMemoryEdits(current, edits);
|
|
2609
2808
|
}
|
|
2610
2809
|
async function guardMemoryWrite(input) {
|
|
2810
|
+
const start = performance.now();
|
|
2811
|
+
const result = await guardMemoryWriteInner(input);
|
|
2812
|
+
if (result.handled) {
|
|
2813
|
+
recordMemoryGuard(
|
|
2814
|
+
result.scanResult?.verdict ?? "unscanned",
|
|
2815
|
+
result.committed === true,
|
|
2816
|
+
result.decision?.block === true,
|
|
2817
|
+
start,
|
|
2818
|
+
input.auth.pubkey
|
|
2819
|
+
);
|
|
2820
|
+
}
|
|
2821
|
+
return result;
|
|
2822
|
+
}
|
|
2823
|
+
async function guardMemoryWriteInner(input) {
|
|
2611
2824
|
const {
|
|
2612
2825
|
event,
|
|
2613
2826
|
ctx,
|
|
@@ -2621,7 +2834,7 @@ async function guardMemoryWrite(input) {
|
|
|
2621
2834
|
toolNames,
|
|
2622
2835
|
enforce = true,
|
|
2623
2836
|
debug = false,
|
|
2624
|
-
logger,
|
|
2837
|
+
logger: logger2,
|
|
2625
2838
|
agentHomeDirs,
|
|
2626
2839
|
memoryRoot
|
|
2627
2840
|
} = input;
|
|
@@ -2630,7 +2843,7 @@ async function guardMemoryWrite(input) {
|
|
|
2630
2843
|
memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
|
|
2631
2844
|
} catch (err) {
|
|
2632
2845
|
const why = err instanceof Error ? err.message : String(err);
|
|
2633
|
-
|
|
2846
|
+
logger2?.warn?.("[atbash] memory classifier refused this tool call's input", { reason: why });
|
|
2634
2847
|
if (!enforce) return { handled: false };
|
|
2635
2848
|
return {
|
|
2636
2849
|
handled: true,
|
|
@@ -2641,14 +2854,14 @@ async function guardMemoryWrite(input) {
|
|
|
2641
2854
|
}
|
|
2642
2855
|
};
|
|
2643
2856
|
}
|
|
2644
|
-
if (debug) emitDebugProbe(event, ctx, memEntry,
|
|
2857
|
+
if (debug) emitDebugProbe(event, ctx, memEntry, logger2);
|
|
2645
2858
|
if (!memEntry) return { handled: false };
|
|
2646
2859
|
let document;
|
|
2647
2860
|
try {
|
|
2648
2861
|
document = await resultingDocument(memEntry, event, ctx);
|
|
2649
2862
|
} catch (err) {
|
|
2650
2863
|
const reason = err instanceof Error ? err.message : String(err);
|
|
2651
|
-
|
|
2864
|
+
logger2?.warn?.("[atbash] memory edit could not be applied", { path: memEntry.key, reason });
|
|
2652
2865
|
return {
|
|
2653
2866
|
handled: true,
|
|
2654
2867
|
decision: enforce ? { allow: false, block: true, reason: `Memory edit refused: ${reason}` } : { allow: true, reason: `Memory edit refused: ${reason}` },
|
|
@@ -2666,7 +2879,7 @@ async function guardMemoryWrite(input) {
|
|
|
2666
2879
|
});
|
|
2667
2880
|
} catch (err) {
|
|
2668
2881
|
const reason = err instanceof Error ? err.message : String(err);
|
|
2669
|
-
|
|
2882
|
+
logger2?.warn?.("[atbash] memory scan failed", {
|
|
2670
2883
|
path: memEntry.key,
|
|
2671
2884
|
reason
|
|
2672
2885
|
});
|
|
@@ -2676,7 +2889,7 @@ async function guardMemoryWrite(input) {
|
|
|
2676
2889
|
};
|
|
2677
2890
|
}
|
|
2678
2891
|
if (scanResult.verdict === "red") {
|
|
2679
|
-
|
|
2892
|
+
logger2?.warn?.("[atbash] memory BLOCK", {
|
|
2680
2893
|
path: memEntry.key,
|
|
2681
2894
|
reason: scanResult.reason
|
|
2682
2895
|
});
|
|
@@ -2698,7 +2911,7 @@ async function guardMemoryWrite(input) {
|
|
|
2698
2911
|
}
|
|
2699
2912
|
const filePath = memorySlotFor(memEntry.key, memoryRoot, agentHomeDirs);
|
|
2700
2913
|
if (filePath === null) {
|
|
2701
|
-
|
|
2914
|
+
logger2?.info?.(
|
|
2702
2915
|
`[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.`,
|
|
2703
2916
|
{ path: memEntry.key, verdict: scanResult.verdict, score: scanResult.score }
|
|
2704
2917
|
);
|
|
@@ -2720,7 +2933,7 @@ async function guardMemoryWrite(input) {
|
|
|
2720
2933
|
});
|
|
2721
2934
|
} catch (err) {
|
|
2722
2935
|
const reason = err instanceof Error ? err.message : String(err);
|
|
2723
|
-
|
|
2936
|
+
logger2?.warn?.("[atbash] memory commit to chain failed", {
|
|
2724
2937
|
path: memEntry.key,
|
|
2725
2938
|
filePath,
|
|
2726
2939
|
reason
|
|
@@ -2732,7 +2945,7 @@ async function guardMemoryWrite(input) {
|
|
|
2732
2945
|
committed: false
|
|
2733
2946
|
};
|
|
2734
2947
|
}
|
|
2735
|
-
|
|
2948
|
+
logger2?.info?.(
|
|
2736
2949
|
scanResult.verdict === "yellow" ? "[atbash] memory ALLOW (yellow, low score)" : "[atbash] memory ALLOW",
|
|
2737
2950
|
{
|
|
2738
2951
|
path: memEntry.key,
|
|
@@ -3645,6 +3858,7 @@ function diffMemorySnapshots(before, after) {
|
|
|
3645
3858
|
classifyMemoryWrite,
|
|
3646
3859
|
columnAad,
|
|
3647
3860
|
commitMemoryVersion,
|
|
3861
|
+
configureEvents,
|
|
3648
3862
|
containsEvasionCharacters,
|
|
3649
3863
|
containsSecret,
|
|
3650
3864
|
createFileLogger,
|
|
@@ -3660,6 +3874,7 @@ function diffMemorySnapshots(before, after) {
|
|
|
3660
3874
|
encryptForOrg,
|
|
3661
3875
|
encryptMemoryContent,
|
|
3662
3876
|
encryptedLength,
|
|
3877
|
+
eventsAreOn,
|
|
3663
3878
|
flushTelemetry,
|
|
3664
3879
|
generateKeypair,
|
|
3665
3880
|
getActiveAgentMemory,
|
|
@@ -3688,6 +3903,7 @@ function diffMemorySnapshots(before, after) {
|
|
|
3688
3903
|
pubkeyToHex,
|
|
3689
3904
|
recordCall,
|
|
3690
3905
|
recordDuration,
|
|
3906
|
+
recordEvent,
|
|
3691
3907
|
redactJsonStrings,
|
|
3692
3908
|
redactSecrets,
|
|
3693
3909
|
resolve,
|