@atbash/sdk 0.9.0 → 0.9.2
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/browser.d.mts +49 -14
- package/dist/browser.mjs +124 -192
- package/dist/index.d.mts +49 -14
- package/dist/index.d.ts +49 -14
- package/dist/index.js +338 -92
- package/dist/index.mjs +342 -93
- package/package.json +6 -6
package/dist/index.mjs
CHANGED
|
@@ -2930,6 +2930,13 @@ function buildAllowedJudgeHosts() {
|
|
|
2930
2930
|
]);
|
|
2931
2931
|
}
|
|
2932
2932
|
var ALLOWED_JUDGE_HOSTS = buildAllowedJudgeHosts();
|
|
2933
|
+
function isAllowlistedJudgeHost(endpoint) {
|
|
2934
|
+
try {
|
|
2935
|
+
return ALLOWED_JUDGE_HOSTS.has(new URL(endpoint).hostname.toLowerCase());
|
|
2936
|
+
} catch {
|
|
2937
|
+
return false;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2933
2940
|
function validateJudgeEndpoint(judge) {
|
|
2934
2941
|
const policy = judge?.policy === "self-hosted" ? "self-hosted" : "default";
|
|
2935
2942
|
const candidate = judge?.endpoint?.trim() || DEFAULT_ENDPOINT;
|
|
@@ -2977,11 +2984,23 @@ var AtbashAPIError = class extends Error {
|
|
|
2977
2984
|
status;
|
|
2978
2985
|
/** Raw response body text (may be empty). */
|
|
2979
2986
|
body;
|
|
2980
|
-
|
|
2981
|
-
|
|
2987
|
+
/**
|
|
2988
|
+
* Request path this status came from, e.g. `/api/v1/judge` — absent when the
|
|
2989
|
+
* request never reached a response (transport failures carry status 0).
|
|
2990
|
+
*
|
|
2991
|
+
* A status alone does not say what failed. Several routes answer one call, and
|
|
2992
|
+
* more than one of them can 404: a caller reading only `404` cannot tell "this
|
|
2993
|
+
* agent is not registered" from "the chain-routing lookup did not resolve", and
|
|
2994
|
+
* has to guess from the body's prose — which a 404 raised before any handler
|
|
2995
|
+
* ran does not carry. The path is the fact that settles it.
|
|
2996
|
+
*/
|
|
2997
|
+
path;
|
|
2998
|
+
constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT, path7) {
|
|
2999
|
+
super(enrich(status, body, statusText, endpoint, path7));
|
|
2982
3000
|
this.name = "AtbashAPIError";
|
|
2983
3001
|
this.status = status;
|
|
2984
3002
|
this.body = body;
|
|
3003
|
+
this.path = path7;
|
|
2985
3004
|
}
|
|
2986
3005
|
};
|
|
2987
3006
|
var SignatureVerificationError = class extends Error {
|
|
@@ -2990,9 +3009,9 @@ var SignatureVerificationError = class extends Error {
|
|
|
2990
3009
|
this.name = "SignatureVerificationError";
|
|
2991
3010
|
}
|
|
2992
3011
|
};
|
|
2993
|
-
function enrich(status, body, statusText, endpoint) {
|
|
3012
|
+
function enrich(status, body, statusText, endpoint, path7) {
|
|
2994
3013
|
const dashboard = endpoint.replace(/\/+$/, "") || DEFAULT_ENDPOINT;
|
|
2995
|
-
let msg = `API error ${status}: ${body || statusText}`;
|
|
3014
|
+
let msg = `API error ${status}${path7 ? ` from ${path7}` : ""}: ${body || statusText}`;
|
|
2996
3015
|
const lowered = body.toLowerCase();
|
|
2997
3016
|
if (lowered.includes("agent not registered")) {
|
|
2998
3017
|
msg += `
|
|
@@ -3082,6 +3101,13 @@ function classifyTransportError(err, url, method, timeoutMs) {
|
|
|
3082
3101
|
{ cause: err }
|
|
3083
3102
|
);
|
|
3084
3103
|
}
|
|
3104
|
+
if (code2 === "UND_ERR_CONNECT_TIMEOUT") {
|
|
3105
|
+
return new HttpTransportError(
|
|
3106
|
+
"timeout",
|
|
3107
|
+
`${method} ${url} could not establish a connection before the transport deadline \u2014 check the endpoint and network`,
|
|
3108
|
+
{ cause: err }
|
|
3109
|
+
);
|
|
3110
|
+
}
|
|
3085
3111
|
if (code2 === "ENOTFOUND" || code2 === "EAI_AGAIN") {
|
|
3086
3112
|
return new HttpTransportError(
|
|
3087
3113
|
"dns",
|
|
@@ -3239,7 +3265,10 @@ function canonicalAllow(data) {
|
|
|
3239
3265
|
import { readFileSync as readFileSync2 } from "fs";
|
|
3240
3266
|
import { homedir as homedir2 } from "os";
|
|
3241
3267
|
import { join as join2 } from "path";
|
|
3242
|
-
import {
|
|
3268
|
+
import {
|
|
3269
|
+
AggregationTemporalityPreference,
|
|
3270
|
+
OTLPMetricExporter
|
|
3271
|
+
} from "@opentelemetry/exporter-metrics-otlp-http";
|
|
3243
3272
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
3244
3273
|
import {
|
|
3245
3274
|
MeterProvider,
|
|
@@ -3249,6 +3278,8 @@ var meterProvider = null;
|
|
|
3249
3278
|
var callCounter = null;
|
|
3250
3279
|
var durationHistogram = null;
|
|
3251
3280
|
var defaultSource = "sdk";
|
|
3281
|
+
var sourceExplicitlySet = false;
|
|
3282
|
+
var requestedExportIntervalMs;
|
|
3252
3283
|
function isTelemetryOptedOut() {
|
|
3253
3284
|
const disabled = process.env.ATBASH_TELEMETRY_DISABLED?.trim().toLowerCase();
|
|
3254
3285
|
if (disabled && ["1", "true", "yes", "on"].includes(disabled)) {
|
|
@@ -3272,20 +3303,41 @@ function autoInit() {
|
|
|
3272
3303
|
}
|
|
3273
3304
|
function setupTelemetry(config2) {
|
|
3274
3305
|
if (!config2.enabled) return;
|
|
3275
|
-
if (meterProvider) return;
|
|
3276
3306
|
if (isTelemetryOptedOut()) return;
|
|
3307
|
+
if (config2.source && !sourceExplicitlySet) {
|
|
3308
|
+
defaultSource = config2.source;
|
|
3309
|
+
sourceExplicitlySet = true;
|
|
3310
|
+
}
|
|
3311
|
+
if (config2.exportIntervalMs !== void 0 && requestedExportIntervalMs === void 0) {
|
|
3312
|
+
requestedExportIntervalMs = config2.exportIntervalMs;
|
|
3313
|
+
}
|
|
3314
|
+
if (meterProvider) return;
|
|
3277
3315
|
if (!config2.endpoint || !config2.getAuthHeaders) return;
|
|
3278
|
-
if (/^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(
|
|
3279
|
-
|
|
3316
|
+
if (/^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(
|
|
3317
|
+
config2.endpoint
|
|
3318
|
+
))
|
|
3319
|
+
return;
|
|
3280
3320
|
const proxyUrl = `${config2.endpoint.replace(/\/+$/, "")}/api/telemetry`;
|
|
3281
3321
|
const getAuthHeaders = config2.getAuthHeaders;
|
|
3282
3322
|
const exporter = new OTLPMetricExporter({
|
|
3283
3323
|
url: proxyUrl,
|
|
3284
|
-
headers: async () => getAuthHeaders()
|
|
3324
|
+
headers: async () => getAuthHeaders(),
|
|
3325
|
+
// DELTA, not the OTel default of CUMULATIVE.
|
|
3326
|
+
//
|
|
3327
|
+
// A cumulative counter reports a running total, and the backend recovers
|
|
3328
|
+
// the increment by differencing consecutive points of a series. That needs
|
|
3329
|
+
// at least two points. Most Atbash callers are short-lived — a CLI run
|
|
3330
|
+
// exports once and exits — so their series has exactly one point, nothing
|
|
3331
|
+
// to difference against, and the usage board reads them as zero: the calls
|
|
3332
|
+
// arrive, carry the right `source`, and still count for nothing.
|
|
3333
|
+
//
|
|
3334
|
+
// A delta counter reports the increment itself, so a single export is a
|
|
3335
|
+
// whole truthful datapoint and SUM over any window is the real call count.
|
|
3336
|
+
temporalityPreference: AggregationTemporalityPreference.DELTA
|
|
3285
3337
|
});
|
|
3286
3338
|
const reader = new PeriodicExportingMetricReader({
|
|
3287
3339
|
exporter,
|
|
3288
|
-
exportIntervalMillis:
|
|
3340
|
+
exportIntervalMillis: requestedExportIntervalMs ?? 6e4
|
|
3289
3341
|
});
|
|
3290
3342
|
meterProvider = new MeterProvider({
|
|
3291
3343
|
resource: resourceFromAttributes({
|
|
@@ -3329,6 +3381,9 @@ async function shutdownTelemetry() {
|
|
|
3329
3381
|
meterProvider = null;
|
|
3330
3382
|
callCounter = null;
|
|
3331
3383
|
durationHistogram = null;
|
|
3384
|
+
defaultSource = "sdk";
|
|
3385
|
+
sourceExplicitlySet = false;
|
|
3386
|
+
requestedExportIntervalMs = void 0;
|
|
3332
3387
|
}
|
|
3333
3388
|
|
|
3334
3389
|
// src-ts/userConfig.ts
|
|
@@ -3522,17 +3577,23 @@ var Atbash = class _Atbash {
|
|
|
3522
3577
|
verifying: this.verifyPubKey ? "with response-signature pubkey configured" : "without signature verification"
|
|
3523
3578
|
});
|
|
3524
3579
|
}
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3580
|
+
if (isAllowlistedJudgeHost(this.endpoint)) {
|
|
3581
|
+
try {
|
|
3582
|
+
setupTelemetry({
|
|
3583
|
+
enabled: true,
|
|
3584
|
+
endpoint: this.endpoint,
|
|
3585
|
+
getAuthHeaders: () => this.authHeaders()
|
|
3586
|
+
});
|
|
3587
|
+
} catch (err) {
|
|
3588
|
+
this.logger.warn?.(
|
|
3589
|
+
"[atbash] telemetry setup failed \u2014 continuing without metrics",
|
|
3590
|
+
{ error: String(err) }
|
|
3591
|
+
);
|
|
3592
|
+
}
|
|
3593
|
+
} else {
|
|
3594
|
+
this.logger.info?.(
|
|
3595
|
+
"[atbash] telemetry disabled \u2014 the agent bearer is only sent to hosts that already receive it, and this judge endpoint is not one",
|
|
3596
|
+
{ endpoint: this.endpoint }
|
|
3536
3597
|
);
|
|
3537
3598
|
}
|
|
3538
3599
|
}
|
|
@@ -4413,7 +4474,8 @@ var Atbash = class _Atbash {
|
|
|
4413
4474
|
resp.status,
|
|
4414
4475
|
await safeText(resp),
|
|
4415
4476
|
resp.statusText,
|
|
4416
|
-
this.endpoint
|
|
4477
|
+
this.endpoint,
|
|
4478
|
+
requestPath(resp.url)
|
|
4417
4479
|
);
|
|
4418
4480
|
}
|
|
4419
4481
|
/**
|
|
@@ -4555,6 +4617,14 @@ function parseJson(bytes) {
|
|
|
4555
4617
|
const parsed = tryParseJson(bytes.toString("utf-8"));
|
|
4556
4618
|
return parsed === void 0 ? {} : parsed;
|
|
4557
4619
|
}
|
|
4620
|
+
function requestPath(url) {
|
|
4621
|
+
if (!url) return void 0;
|
|
4622
|
+
try {
|
|
4623
|
+
return new URL(url).pathname || void 0;
|
|
4624
|
+
} catch {
|
|
4625
|
+
return void 0;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4558
4628
|
async function safeText(resp) {
|
|
4559
4629
|
try {
|
|
4560
4630
|
return await resp.text();
|
|
@@ -35212,9 +35282,9 @@ var ZodUnion = class extends ZodType {
|
|
|
35212
35282
|
}
|
|
35213
35283
|
};
|
|
35214
35284
|
types.ZodUnion = ZodUnion;
|
|
35215
|
-
ZodUnion.create = (
|
|
35285
|
+
ZodUnion.create = (types3, params) => {
|
|
35216
35286
|
return new ZodUnion({
|
|
35217
|
-
options:
|
|
35287
|
+
options: types3,
|
|
35218
35288
|
typeName: ZodFirstPartyTypeKind.ZodUnion,
|
|
35219
35289
|
...processCreateParams(params)
|
|
35220
35290
|
});
|
|
@@ -38671,9 +38741,9 @@ _nodeUtil.exports;
|
|
|
38671
38741
|
var freeProcess = moduleExports && freeGlobal2.process;
|
|
38672
38742
|
var nodeUtil2 = (function() {
|
|
38673
38743
|
try {
|
|
38674
|
-
var
|
|
38675
|
-
if (
|
|
38676
|
-
return
|
|
38744
|
+
var types3 = freeModule && freeModule.require && freeModule.require("util").types;
|
|
38745
|
+
if (types3) {
|
|
38746
|
+
return types3;
|
|
38677
38747
|
}
|
|
38678
38748
|
return freeProcess && freeProcess.binding && freeProcess.binding("util");
|
|
38679
38749
|
} catch (e) {
|
|
@@ -42921,7 +42991,7 @@ async function commitMemoryVersion(plaintext, auth, opts) {
|
|
|
42921
42991
|
const chainOpts = await resolveChainOptsForOrg(opts, auth);
|
|
42922
42992
|
const client = await buildChainClient(chainOpts);
|
|
42923
42993
|
const { sigProvider } = buildSigner(auth);
|
|
42924
|
-
await client.signAndSendUniqueTransaction(
|
|
42994
|
+
const receipt = await client.signAndSendUniqueTransaction(
|
|
42925
42995
|
{
|
|
42926
42996
|
name: native.OP_ADD_AGENT_MEMORY,
|
|
42927
42997
|
args: [
|
|
@@ -42934,6 +43004,9 @@ async function commitMemoryVersion(plaintext, auth, opts) {
|
|
|
42934
43004
|
},
|
|
42935
43005
|
sigProvider
|
|
42936
43006
|
);
|
|
43007
|
+
if (receipt.status !== index.ResponseStatus.Confirmed) {
|
|
43008
|
+
throw new Error(`Memory transaction was not confirmed (status=${receipt.status})`);
|
|
43009
|
+
}
|
|
42937
43010
|
}
|
|
42938
43011
|
async function decryptRow(row, key3) {
|
|
42939
43012
|
const parsed = native.parseMemoryRow(gtvToFfiSafe(row));
|
|
@@ -43043,22 +43116,109 @@ async function rollbackMemory(toId, reason, auth, opts) {
|
|
|
43043
43116
|
const chainOpts = await resolveChainOptsForOrg(opts, auth);
|
|
43044
43117
|
const client = await buildChainClient(chainOpts);
|
|
43045
43118
|
const { sigProvider } = buildSigner(auth);
|
|
43046
|
-
await client.signAndSendUniqueTransaction(
|
|
43119
|
+
const receipt = await client.signAndSendUniqueTransaction(
|
|
43047
43120
|
{
|
|
43048
43121
|
name: native.OP_ROLLBACK_AGENT_MEMORY,
|
|
43049
43122
|
args: [toGtxBytes(args.agent_pubkey), args.to_id, args.reason]
|
|
43050
43123
|
},
|
|
43051
43124
|
sigProvider
|
|
43052
43125
|
);
|
|
43126
|
+
if (receipt.status !== index.ResponseStatus.Confirmed) {
|
|
43127
|
+
throw new Error(`Memory rollback was not confirmed (status=${receipt.status})`);
|
|
43128
|
+
}
|
|
43053
43129
|
}
|
|
43054
43130
|
|
|
43055
43131
|
// src-ts/memory/_json_safe.ts
|
|
43132
|
+
import { types as types2 } from "util";
|
|
43133
|
+
var replacementKeys = [
|
|
43134
|
+
"newText",
|
|
43135
|
+
"new_string",
|
|
43136
|
+
"new_str",
|
|
43137
|
+
"newStr",
|
|
43138
|
+
"replacement"
|
|
43139
|
+
];
|
|
43140
|
+
var argumentKeys = [
|
|
43141
|
+
"file_path",
|
|
43142
|
+
"path",
|
|
43143
|
+
"filename",
|
|
43144
|
+
"target",
|
|
43145
|
+
"file",
|
|
43146
|
+
"filePath",
|
|
43147
|
+
"content",
|
|
43148
|
+
...replacementKeys,
|
|
43149
|
+
"value",
|
|
43150
|
+
"text"
|
|
43151
|
+
];
|
|
43152
|
+
var maxPrototypeDepth = 32;
|
|
43153
|
+
var maxEditSlots = 65536;
|
|
43154
|
+
var maxTextUnits = 16 * 1024 * 1024;
|
|
43155
|
+
function invalid() {
|
|
43156
|
+
throw new TypeError("Unreadable or unsupported memory classifier input");
|
|
43157
|
+
}
|
|
43158
|
+
function dataProperty(value, key3) {
|
|
43159
|
+
let current = value;
|
|
43160
|
+
for (let depth = 0; current !== null; depth++) {
|
|
43161
|
+
if (depth >= maxPrototypeDepth || types2.isProxy(current)) invalid();
|
|
43162
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key3);
|
|
43163
|
+
if (descriptor) {
|
|
43164
|
+
if (!("value" in descriptor)) invalid();
|
|
43165
|
+
return descriptor.value;
|
|
43166
|
+
}
|
|
43167
|
+
current = Object.getPrototypeOf(current);
|
|
43168
|
+
}
|
|
43169
|
+
return void 0;
|
|
43170
|
+
}
|
|
43056
43171
|
function toJsonSafe(value) {
|
|
43057
|
-
|
|
43058
|
-
|
|
43059
|
-
|
|
43060
|
-
|
|
43172
|
+
let textUnits = 0;
|
|
43173
|
+
function scalar(input) {
|
|
43174
|
+
if (input == null) return null;
|
|
43175
|
+
if (typeof input === "string") {
|
|
43176
|
+
textUnits += input.length;
|
|
43177
|
+
if (textUnits > maxTextUnits) invalid();
|
|
43178
|
+
return input;
|
|
43179
|
+
}
|
|
43180
|
+
if (typeof input === "boolean") return input;
|
|
43181
|
+
if (typeof input === "number" && Number.isFinite(input)) return input;
|
|
43182
|
+
return invalid();
|
|
43183
|
+
}
|
|
43184
|
+
function project(input, shape) {
|
|
43185
|
+
if (input == null || typeof input !== "object") return scalar(input);
|
|
43186
|
+
if (types2.isProxy(input) || Array.isArray(input)) invalid();
|
|
43187
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
43188
|
+
const keys2 = shape === "envelope" ? ["toolName", "name"] : shape === "tool" ? ["name"] : shape === "args" ? argumentKeys : replacementKeys;
|
|
43189
|
+
for (const key3 of keys2) {
|
|
43190
|
+
const field = dataProperty(input, key3);
|
|
43191
|
+
if (field !== void 0) output[key3] = scalar(field);
|
|
43192
|
+
}
|
|
43193
|
+
if (shape === "envelope") {
|
|
43194
|
+
for (const key3 of ["params", "args", "arguments", "tool"]) {
|
|
43195
|
+
const field = dataProperty(input, key3);
|
|
43196
|
+
if (field !== void 0)
|
|
43197
|
+
output[key3] = project(field, key3 === "tool" ? "tool" : "args");
|
|
43198
|
+
}
|
|
43199
|
+
}
|
|
43200
|
+
if (shape === "args") {
|
|
43201
|
+
const edits = dataProperty(input, "edits");
|
|
43202
|
+
if (edits !== void 0) {
|
|
43203
|
+
if (edits !== null && typeof edits === "object" && types2.isProxy(edits))
|
|
43204
|
+
invalid();
|
|
43205
|
+
if (Array.isArray(edits)) {
|
|
43206
|
+
const length = dataProperty(edits, "length");
|
|
43207
|
+
if (typeof length !== "number" || !Number.isInteger(length) || length < 0 || length > maxEditSlots)
|
|
43208
|
+
invalid();
|
|
43209
|
+
const entries = [];
|
|
43210
|
+
for (let index2 = 0; index2 < length; index2++) {
|
|
43211
|
+
entries.push(project(dataProperty(edits, String(index2)), "edit"));
|
|
43212
|
+
}
|
|
43213
|
+
output.edits = entries;
|
|
43214
|
+
} else {
|
|
43215
|
+
output.edits = scalar(edits);
|
|
43216
|
+
}
|
|
43217
|
+
}
|
|
43218
|
+
}
|
|
43219
|
+
return output;
|
|
43061
43220
|
}
|
|
43221
|
+
return project(value, "envelope");
|
|
43062
43222
|
}
|
|
43063
43223
|
|
|
43064
43224
|
// src-ts/memory/classifier.ts
|
|
@@ -43104,6 +43264,7 @@ async function guardMemoryWrite(input) {
|
|
|
43104
43264
|
endpoint,
|
|
43105
43265
|
verifyPubKey,
|
|
43106
43266
|
orgName,
|
|
43267
|
+
chainOpts,
|
|
43107
43268
|
threshold,
|
|
43108
43269
|
patterns,
|
|
43109
43270
|
toolNames,
|
|
@@ -43111,7 +43272,22 @@ async function guardMemoryWrite(input) {
|
|
|
43111
43272
|
debug: debug2 = false,
|
|
43112
43273
|
logger: logger2
|
|
43113
43274
|
} = input;
|
|
43114
|
-
|
|
43275
|
+
let memEntry;
|
|
43276
|
+
try {
|
|
43277
|
+
memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
|
|
43278
|
+
} catch (err) {
|
|
43279
|
+
const why = err instanceof Error ? err.message : String(err);
|
|
43280
|
+
logger2?.warn?.("[atbash] memory classifier refused this tool call's input", { reason: why });
|
|
43281
|
+
if (!enforce) return { handled: false };
|
|
43282
|
+
return {
|
|
43283
|
+
handled: true,
|
|
43284
|
+
decision: {
|
|
43285
|
+
allow: false,
|
|
43286
|
+
block: true,
|
|
43287
|
+
reason: `Memory protection could not read this tool call (${why}); blocked because it could not be checked.`
|
|
43288
|
+
}
|
|
43289
|
+
};
|
|
43290
|
+
}
|
|
43115
43291
|
if (debug2) emitDebugProbe(event, ctx, memEntry, logger2);
|
|
43116
43292
|
if (!memEntry) return { handled: false };
|
|
43117
43293
|
let scanResult;
|
|
@@ -43145,23 +43321,41 @@ async function guardMemoryWrite(input) {
|
|
|
43145
43321
|
committed: false
|
|
43146
43322
|
};
|
|
43147
43323
|
}
|
|
43324
|
+
if (scanResult.verdict !== "green" && scanResult.verdict !== "yellow") {
|
|
43325
|
+
const reason = "Memory scan returned an unrecognized verdict.";
|
|
43326
|
+
return {
|
|
43327
|
+
handled: true,
|
|
43328
|
+
decision: enforce ? { allow: false, block: true, reason } : { allow: true, reason },
|
|
43329
|
+
scanResult,
|
|
43330
|
+
committed: false
|
|
43331
|
+
};
|
|
43332
|
+
}
|
|
43148
43333
|
const filePath = path3.basename(memEntry.key);
|
|
43149
|
-
|
|
43150
|
-
|
|
43151
|
-
|
|
43152
|
-
|
|
43153
|
-
|
|
43154
|
-
|
|
43155
|
-
|
|
43334
|
+
try {
|
|
43335
|
+
await commitMemoryVersion(memEntry.value, auth, {
|
|
43336
|
+
score: scanResult.score,
|
|
43337
|
+
filePath,
|
|
43338
|
+
orgName,
|
|
43339
|
+
endpoint,
|
|
43340
|
+
verifyPubKey,
|
|
43341
|
+
chainOpts
|
|
43342
|
+
});
|
|
43343
|
+
} catch (err) {
|
|
43156
43344
|
const reason = err instanceof Error ? err.message : String(err);
|
|
43157
43345
|
logger2?.warn?.("[atbash] memory commit to chain failed", {
|
|
43158
43346
|
path: memEntry.key,
|
|
43159
43347
|
filePath,
|
|
43160
43348
|
reason
|
|
43161
43349
|
});
|
|
43162
|
-
|
|
43350
|
+
return {
|
|
43351
|
+
handled: true,
|
|
43352
|
+
decision: enforce ? { allow: false, block: true, reason: `Memory commit failed: ${reason}` } : { allow: true, reason: `Memory commit failed: ${reason}` },
|
|
43353
|
+
scanResult,
|
|
43354
|
+
committed: false
|
|
43355
|
+
};
|
|
43356
|
+
}
|
|
43163
43357
|
logger2?.info?.(
|
|
43164
|
-
scanResult.verdict === "yellow" ? "[atbash] memory
|
|
43358
|
+
scanResult.verdict === "yellow" ? "[atbash] memory ALLOW (yellow, low score)" : "[atbash] memory ALLOW",
|
|
43165
43359
|
{
|
|
43166
43360
|
path: memEntry.key,
|
|
43167
43361
|
filePath,
|
|
@@ -43194,7 +43388,7 @@ async function syncLocalMemory(auth, pointer, opts = {}) {
|
|
|
43194
43388
|
if (withinTtl) {
|
|
43195
43389
|
return { drifted: false, checked: false, pointer };
|
|
43196
43390
|
}
|
|
43197
|
-
const currentId = await getActiveMemoryId(auth, opts.chainOpts);
|
|
43391
|
+
const currentId = await getActiveMemoryId(auth, opts.chainOpts, opts.filePath);
|
|
43198
43392
|
const nextPointer = { activeId: currentId, checkedAt: now };
|
|
43199
43393
|
if (currentId === pointer.activeId) {
|
|
43200
43394
|
return { drifted: false, checked: true, pointer: nextPointer };
|
|
@@ -43276,13 +43470,16 @@ function formatMeta(meta) {
|
|
|
43276
43470
|
}
|
|
43277
43471
|
function createFileLogger(filePath, upstream) {
|
|
43278
43472
|
let queue = Promise.resolve();
|
|
43473
|
+
let writeError;
|
|
43279
43474
|
async function ensureDir() {
|
|
43280
43475
|
await fs2.mkdir(path5.dirname(filePath), { recursive: true });
|
|
43281
43476
|
}
|
|
43282
43477
|
function append(level, message, meta) {
|
|
43283
43478
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${message}${formatMeta(meta)}
|
|
43284
43479
|
`;
|
|
43285
|
-
queue = queue.then(ensureDir).then(() => fs2.appendFile(filePath, line, "utf8")).catch(() => {
|
|
43480
|
+
queue = queue.then(ensureDir).then(() => fs2.appendFile(filePath, line, "utf8")).catch((error4) => {
|
|
43481
|
+
writeError = error4 instanceof Error ? error4 : new Error(String(error4));
|
|
43482
|
+
upstream?.warn("[atbash] plugin log write failed", { error: writeError.message });
|
|
43286
43483
|
});
|
|
43287
43484
|
}
|
|
43288
43485
|
return {
|
|
@@ -43293,6 +43490,10 @@ function createFileLogger(filePath, upstream) {
|
|
|
43293
43490
|
warn(message, meta) {
|
|
43294
43491
|
upstream?.warn(message, meta ?? {});
|
|
43295
43492
|
append("warn", message, meta);
|
|
43493
|
+
},
|
|
43494
|
+
async flush() {
|
|
43495
|
+
await queue;
|
|
43496
|
+
if (writeError) throw writeError;
|
|
43296
43497
|
}
|
|
43297
43498
|
};
|
|
43298
43499
|
}
|
|
@@ -43382,7 +43583,15 @@ var MemoryGuardManager = class {
|
|
|
43382
43583
|
orgName: this.opts.orgName
|
|
43383
43584
|
});
|
|
43384
43585
|
const resolved = await atbash.resolveChainForOrg(this.opts.orgName);
|
|
43385
|
-
|
|
43586
|
+
if (!await atbash.checkAgentExists(this.agentPubkeyHex, { network: resolved.network })) {
|
|
43587
|
+
throw new Error(`agent is not registered on the resolved ${resolved.network} chain`);
|
|
43588
|
+
}
|
|
43589
|
+
const chainOpts = {
|
|
43590
|
+
...this.opts.chainOpts,
|
|
43591
|
+
network: resolved.network,
|
|
43592
|
+
blockchainRid: resolved.blockchainRid,
|
|
43593
|
+
nodeUrls: this.opts.chainOpts?.nodeUrls ?? resolved.nodeUrls
|
|
43594
|
+
};
|
|
43386
43595
|
this._resolvedChainOpts = chainOpts;
|
|
43387
43596
|
this.logger.info(
|
|
43388
43597
|
`[atbash] resolved org \u2192 chain \u2014 org=${this.opts.orgName} network=${resolved.network}`
|
|
@@ -43391,9 +43600,9 @@ var MemoryGuardManager = class {
|
|
|
43391
43600
|
} catch (err) {
|
|
43392
43601
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43393
43602
|
this.logger.warn(
|
|
43394
|
-
`[atbash] org \u2192 chain resolution failed (
|
|
43603
|
+
`[atbash] org \u2192 chain resolution failed (memory read will be blocked): ${msg}`
|
|
43395
43604
|
);
|
|
43396
|
-
|
|
43605
|
+
throw err;
|
|
43397
43606
|
} finally {
|
|
43398
43607
|
this._resolveChainInflight = void 0;
|
|
43399
43608
|
}
|
|
@@ -43412,13 +43621,18 @@ var MemoryGuardManager = class {
|
|
|
43412
43621
|
const result = await syncLocalMemory(this.opts.auth, seed, {
|
|
43413
43622
|
ttlMs: 0,
|
|
43414
43623
|
force: true,
|
|
43415
|
-
chainOpts
|
|
43624
|
+
chainOpts,
|
|
43625
|
+
filePath: path6.basename(this.memoryFilePath)
|
|
43416
43626
|
});
|
|
43417
43627
|
if (!result.drifted && result.pointer.activeId == null) {
|
|
43418
43628
|
this.logger.info(
|
|
43419
43629
|
`[atbash] no active memory on chain for agent=${this.agentPubkeyHex.slice(0, 16)}\u2026 org=${this.opts.orgName ?? "(none)"} \u2014 either the agent isn't registered on this chain or hasn't written any memory. Sync will remain a no-op until a write lands.`
|
|
43420
43630
|
);
|
|
43421
43631
|
} else if (result.drifted && result.current) {
|
|
43632
|
+
if (result.current.filePath !== path6.basename(this.memoryFilePath) || result.current.isActive !== true || result.current.decryptError) {
|
|
43633
|
+
this.logger.warn("[atbash] boot sync REFUSED mismatched memory version");
|
|
43634
|
+
return;
|
|
43635
|
+
}
|
|
43422
43636
|
if (result.current.score < this.rollbackMinScore) {
|
|
43423
43637
|
this.logger.warn(
|
|
43424
43638
|
`[atbash] boot sync REFUSED refresh \u2014 id=${result.current.id} score=${result.current.score} below threshold ${this.rollbackMinScore}. Leaving MEMORY.md and pointer untouched; next memory read will be blocked.`
|
|
@@ -43432,7 +43646,8 @@ var MemoryGuardManager = class {
|
|
|
43432
43646
|
await this.writeMemoryAtomic(result.current.content);
|
|
43433
43647
|
} catch (err) {
|
|
43434
43648
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43435
|
-
this.logger.warn("[atbash] boot sync write failed
|
|
43649
|
+
this.logger.warn("[atbash] boot sync write failed; pointer remains unchanged", { error: msg });
|
|
43650
|
+
return;
|
|
43436
43651
|
}
|
|
43437
43652
|
}
|
|
43438
43653
|
await this.pointerStore.set(this.agentPubkeyHex, result.pointer);
|
|
@@ -43446,16 +43661,30 @@ var MemoryGuardManager = class {
|
|
|
43446
43661
|
}
|
|
43447
43662
|
/**
|
|
43448
43663
|
* Returns a `HookDecision` when the guard reached a decision about this event.
|
|
43449
|
-
* Returns `null`
|
|
43450
|
-
*
|
|
43451
|
-
*
|
|
43664
|
+
* Returns `null` for non-memory events, other files this manager does not
|
|
43665
|
+
* own, or unverified observe-mode reads. An enforced read of the managed
|
|
43666
|
+
* file blocks when verification cannot complete.
|
|
43452
43667
|
*
|
|
43453
43668
|
* A returned decision carries `audited` (see `HookDecision`). Only
|
|
43454
43669
|
* `{ allow: true, audited: true }` means "checked and cleared"; anything else
|
|
43455
43670
|
* that allows is a call the host still needs to judge.
|
|
43456
43671
|
*/
|
|
43457
43672
|
async handleBeforeToolCall(event, ctx) {
|
|
43458
|
-
|
|
43673
|
+
let isMemoryRead;
|
|
43674
|
+
try {
|
|
43675
|
+
isMemoryRead = classifyMemoryRead(event, ctx, this.opts.memoryReadClassifier);
|
|
43676
|
+
} catch (err) {
|
|
43677
|
+
const why = err instanceof Error ? err.message : String(err);
|
|
43678
|
+
this.logger.warn("[atbash] memory classifier refused this tool call's input", { reason: why });
|
|
43679
|
+
if (!this.enforce) return null;
|
|
43680
|
+
return {
|
|
43681
|
+
allow: false,
|
|
43682
|
+
block: true,
|
|
43683
|
+
blockReason: `Memory protection could not read this tool call (${why}); blocked because it could not be checked.`,
|
|
43684
|
+
audited: true
|
|
43685
|
+
};
|
|
43686
|
+
}
|
|
43687
|
+
if (isMemoryRead) {
|
|
43459
43688
|
this.logger.info("[atbash] memory read intercepted \u2014 running sync check");
|
|
43460
43689
|
return await this.handleMemoryRead(event, ctx);
|
|
43461
43690
|
}
|
|
@@ -43470,6 +43699,7 @@ var MemoryGuardManager = class {
|
|
|
43470
43699
|
endpoint: this.opts.judgeEndpoint,
|
|
43471
43700
|
verifyPubKey: this.opts.judgeVerifyPubKey,
|
|
43472
43701
|
orgName: this.opts.orgName,
|
|
43702
|
+
chainOpts: this.opts.chainOpts,
|
|
43473
43703
|
patterns: this.opts.memoryPathPatterns,
|
|
43474
43704
|
toolNames: this.opts.memoryWriteToolNames,
|
|
43475
43705
|
enforce: this.enforce,
|
|
@@ -43514,6 +43744,14 @@ var MemoryGuardManager = class {
|
|
|
43514
43744
|
reason: `memory scan returned ${sr2.verdict} but this guard is not enforcing it`
|
|
43515
43745
|
};
|
|
43516
43746
|
}
|
|
43747
|
+
if (guard.committed !== true) {
|
|
43748
|
+
return {
|
|
43749
|
+
allow: true,
|
|
43750
|
+
audited: false,
|
|
43751
|
+
verdict: sr2.verdict,
|
|
43752
|
+
reason: "memory scan passed but chain commit did not confirm"
|
|
43753
|
+
};
|
|
43754
|
+
}
|
|
43517
43755
|
return { allow: true, audited: true, verdict: sr2.verdict };
|
|
43518
43756
|
}
|
|
43519
43757
|
/**
|
|
@@ -43548,62 +43786,73 @@ var MemoryGuardManager = class {
|
|
|
43548
43786
|
return found.every((p) => path6.resolve(p) === managed);
|
|
43549
43787
|
}
|
|
43550
43788
|
async handleMemoryRead(event, ctx) {
|
|
43789
|
+
if (!this.vouchesForTarget(event, ctx)) return null;
|
|
43551
43790
|
const pointer = await this.pointerStore.get(this.agentPubkeyHex);
|
|
43552
43791
|
let result;
|
|
43792
|
+
let chainOpts;
|
|
43553
43793
|
try {
|
|
43554
|
-
|
|
43794
|
+
chainOpts = await this.resolveChainOpts();
|
|
43555
43795
|
result = await syncLocalMemory(this.opts.auth, pointer, {
|
|
43556
43796
|
ttlMs: this.ttlMs,
|
|
43557
|
-
|
|
43797
|
+
force: this.enforce,
|
|
43798
|
+
chainOpts,
|
|
43799
|
+
filePath: path6.basename(this.memoryFilePath)
|
|
43558
43800
|
});
|
|
43559
43801
|
} catch (err) {
|
|
43560
43802
|
if (err instanceof MemoryIntegrityError) {
|
|
43561
43803
|
const reason = `Memory integrity check failed on id ${err.id} \u2014 read blocked.`;
|
|
43562
43804
|
this.logger.warn("[atbash] MEMORY INTEGRITY FAILURE", { id: err.id, error: err.message });
|
|
43563
|
-
|
|
43564
|
-
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43805
|
+
return this.unverifiedRead(reason);
|
|
43565
43806
|
}
|
|
43566
43807
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43567
|
-
this.logger.warn("[atbash] memory sync failed
|
|
43568
|
-
return
|
|
43808
|
+
this.logger.warn("[atbash] memory sync failed", { error: msg });
|
|
43809
|
+
return this.unverifiedRead(`Memory chain sync failed: ${msg}`);
|
|
43569
43810
|
}
|
|
43570
|
-
|
|
43571
|
-
|
|
43572
|
-
|
|
43573
|
-
|
|
43574
|
-
if (fresh.score < this.rollbackMinScore) {
|
|
43575
|
-
const reason = `Rolled-back memory version #${fresh.id} scored ${fresh.score} (below threshold ${this.rollbackMinScore}) \u2014 read blocked.`;
|
|
43576
|
-
this.logger.warn("[atbash] blocking read on low-score rollback", { id: fresh.id, score: fresh.score });
|
|
43577
|
-
if (!this.enforce) return null;
|
|
43578
|
-
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43579
|
-
}
|
|
43580
|
-
this.logger.info("[atbash] memory drift detected \u2014 refreshing local file", {
|
|
43581
|
-
id: fresh.id,
|
|
43582
|
-
score: fresh.score
|
|
43583
|
-
});
|
|
43584
|
-
try {
|
|
43585
|
-
await this.writeMemoryAtomic(fresh.content);
|
|
43586
|
-
} catch (err) {
|
|
43587
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
43588
|
-
this.logger.warn("[atbash] failed to write refreshed memory (serving old)", { error: msg });
|
|
43589
|
-
onDiskIsCurrent = false;
|
|
43590
|
-
}
|
|
43591
|
-
} else {
|
|
43592
|
-
this.logger.info("[atbash] active memory removed on chain", { pubkey: this.agentPubkeyHex });
|
|
43593
|
-
onDiskIsCurrent = false;
|
|
43594
|
-
}
|
|
43811
|
+
if (!result.checked) return this.unverifiedRead("Memory chain state was not checked.");
|
|
43812
|
+
const activeId = result.pointer.activeId;
|
|
43813
|
+
if (activeId === null) {
|
|
43814
|
+
return this.unverifiedRead("No active chain version exists for the managed memory file.");
|
|
43595
43815
|
}
|
|
43596
|
-
|
|
43597
|
-
|
|
43598
|
-
|
|
43599
|
-
|
|
43600
|
-
|
|
43601
|
-
);
|
|
43816
|
+
let active;
|
|
43817
|
+
try {
|
|
43818
|
+
active = result.drifted ? result.current : await getMemoryById(activeId, this.opts.auth, chainOpts);
|
|
43819
|
+
} catch (err) {
|
|
43820
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43821
|
+
this.logger.warn("[atbash] active memory row could not be verified", { error: msg });
|
|
43822
|
+
return this.unverifiedRead(`Memory version could not be verified: ${msg}`);
|
|
43602
43823
|
}
|
|
43603
|
-
|
|
43604
|
-
if (!
|
|
43824
|
+
const filePath = path6.basename(this.memoryFilePath);
|
|
43825
|
+
if (!active || active.id !== activeId || active.filePath !== filePath || active.isActive !== true || active.decryptError) {
|
|
43826
|
+
return this.unverifiedRead("Active memory version does not match the managed file.");
|
|
43827
|
+
}
|
|
43828
|
+
if (active.score < this.rollbackMinScore) {
|
|
43829
|
+
return this.unverifiedRead(`Memory version #${active.id} is below the minimum safety score.`);
|
|
43830
|
+
}
|
|
43831
|
+
try {
|
|
43832
|
+
if (result.drifted) {
|
|
43833
|
+
this.logger.info("[atbash] memory drift detected \u2014 refreshing local file", { id: active.id });
|
|
43834
|
+
await this.writeMemoryAtomic(active.content);
|
|
43835
|
+
}
|
|
43836
|
+
const onDisk = await fs3.readFile(this.memoryFilePath, "utf8");
|
|
43837
|
+
if (onDisk !== active.content) {
|
|
43838
|
+
return this.unverifiedRead("Local memory bytes do not match the active chain version.");
|
|
43839
|
+
}
|
|
43840
|
+
} catch (err) {
|
|
43841
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43842
|
+
this.logger.warn("[atbash] managed memory file could not be verified", { error: msg });
|
|
43843
|
+
return this.unverifiedRead(`Managed memory file could not be verified: ${msg}`);
|
|
43844
|
+
}
|
|
43845
|
+
await this.pointerStore.set(this.agentPubkeyHex, result.pointer);
|
|
43605
43846
|
return { allow: true, audited: true };
|
|
43606
43847
|
}
|
|
43848
|
+
/** Complete queued audit-log writes before a host shuts down or removes its workspace. */
|
|
43849
|
+
async flushLogs() {
|
|
43850
|
+
await this.logger.flush();
|
|
43851
|
+
}
|
|
43852
|
+
unverifiedRead(reason) {
|
|
43853
|
+
if (!this.enforce) return null;
|
|
43854
|
+
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43855
|
+
}
|
|
43607
43856
|
async writeMemoryAtomic(content) {
|
|
43608
43857
|
await fs3.mkdir(path6.dirname(this.memoryFilePath), { recursive: true });
|
|
43609
43858
|
const tmp = `${this.memoryFilePath}.${process.pid}.tmp`;
|