@atbash/sdk 0.10.9-dev.0 → 0.10.11-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 +12 -2
- package/dist/browser.d.mts +116 -24
- package/dist/browser.mjs +164 -83
- package/dist/browser.mjs.map +1 -1
- package/dist/index.d.mts +116 -24
- package/dist/index.d.ts +116 -24
- package/dist/index.js +288 -124
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +288 -124
- package/dist/index.mjs.map +1 -1
- package/index.d.ts +0 -2
- package/index.js +0 -1
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -3021,8 +3021,8 @@ var HttpClient = class {
|
|
|
3021
3021
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
3022
3022
|
this.timeoutMs = timeoutMs;
|
|
3023
3023
|
}
|
|
3024
|
-
buildUrl(
|
|
3025
|
-
const url = new URL(this.baseUrl +
|
|
3024
|
+
buildUrl(path7, query) {
|
|
3025
|
+
const url = new URL(this.baseUrl + path7);
|
|
3026
3026
|
if (query) {
|
|
3027
3027
|
for (const [k, v] of Object.entries(query)) {
|
|
3028
3028
|
if (v !== void 0 && v !== null && v !== "") {
|
|
@@ -3032,14 +3032,14 @@ var HttpClient = class {
|
|
|
3032
3032
|
}
|
|
3033
3033
|
return url.toString();
|
|
3034
3034
|
}
|
|
3035
|
-
async get(
|
|
3036
|
-
return this.fetch(this.buildUrl(
|
|
3035
|
+
async get(path7, query, headers) {
|
|
3036
|
+
return this.fetch(this.buildUrl(path7, query), {
|
|
3037
3037
|
method: "GET",
|
|
3038
3038
|
...headers && { headers }
|
|
3039
3039
|
});
|
|
3040
3040
|
}
|
|
3041
|
-
async post(
|
|
3042
|
-
return this.fetch(this.buildUrl(
|
|
3041
|
+
async post(path7, body, headers) {
|
|
3042
|
+
return this.fetch(this.buildUrl(path7), {
|
|
3043
3043
|
method: "POST",
|
|
3044
3044
|
headers: { "Content-Type": "application/json", ...headers },
|
|
3045
3045
|
body: JSON.stringify(body)
|
|
@@ -3216,6 +3216,10 @@ var callCounter = null;
|
|
|
3216
3216
|
var durationHistogram = null;
|
|
3217
3217
|
var defaultSource = "sdk";
|
|
3218
3218
|
function isTelemetryOptedOut() {
|
|
3219
|
+
const disabled = process.env.ATBASH_TELEMETRY_DISABLED?.trim().toLowerCase();
|
|
3220
|
+
if (disabled && ["1", "true", "yes", "on"].includes(disabled)) {
|
|
3221
|
+
return true;
|
|
3222
|
+
}
|
|
3219
3223
|
try {
|
|
3220
3224
|
const home2 = process.env.HOME || homedir2() || "";
|
|
3221
3225
|
const filePath = join2(home2, ".config", "atbash", "telemetry.json");
|
|
@@ -3236,14 +3240,14 @@ function setupTelemetry(config2) {
|
|
|
3236
3240
|
if (!config2.enabled) return;
|
|
3237
3241
|
if (meterProvider) return;
|
|
3238
3242
|
if (isTelemetryOptedOut()) return;
|
|
3243
|
+
if (!config2.endpoint || !config2.getAuthHeaders) return;
|
|
3244
|
+
if (/^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(config2.endpoint)) return;
|
|
3239
3245
|
defaultSource = config2.source ?? "sdk";
|
|
3240
|
-
const
|
|
3241
|
-
|
|
3246
|
+
const proxyUrl = `${config2.endpoint.replace(/\/+$/, "")}/api/telemetry`;
|
|
3247
|
+
const getAuthHeaders = config2.getAuthHeaders;
|
|
3242
3248
|
const exporter = new OTLPMetricExporter({
|
|
3243
|
-
url:
|
|
3244
|
-
headers:
|
|
3245
|
-
"x-honeycomb-team": apiKey
|
|
3246
|
-
}
|
|
3249
|
+
url: proxyUrl,
|
|
3250
|
+
headers: async () => getAuthHeaders()
|
|
3247
3251
|
});
|
|
3248
3252
|
const reader = new PeriodicExportingMetricReader({
|
|
3249
3253
|
exporter,
|
|
@@ -3381,6 +3385,16 @@ var Atbash = class _Atbash {
|
|
|
3381
3385
|
* calls don't re-hit the dashboard. Cleared by `clearChainCache()`.
|
|
3382
3386
|
*/
|
|
3383
3387
|
_chainCache = /* @__PURE__ */ new Map();
|
|
3388
|
+
/**
|
|
3389
|
+
* Short-TTL cache for `/api/ai/exists`. The `registered` field is
|
|
3390
|
+
* monotonic (once true, stays true), so most calls in a burst re-fetch
|
|
3391
|
+
* data that hasn't changed. The `org_encryption_pubkey` field CAN change
|
|
3392
|
+
* — an org toggling encryption mid-session — so the TTL is deliberately
|
|
3393
|
+
* short (see `AGENT_EXISTS_TTL_MS`). Keyed by (pubkey, network) so
|
|
3394
|
+
* cross-agent / cross-network calls don't collide.
|
|
3395
|
+
*/
|
|
3396
|
+
_agentExistsCache = null;
|
|
3397
|
+
static AGENT_EXISTS_TTL_MS = 5e3;
|
|
3384
3398
|
/**
|
|
3385
3399
|
* Cached bearer token for risk-engine / insurance read calls. Built
|
|
3386
3400
|
* lazily as a signed `log_tool_call` tx and refreshed every 4 min so
|
|
@@ -3408,6 +3422,16 @@ var Atbash = class _Atbash {
|
|
|
3408
3422
|
verifying: this.verifyPubKey ? "with response-signature pubkey configured" : "without signature verification"
|
|
3409
3423
|
});
|
|
3410
3424
|
}
|
|
3425
|
+
try {
|
|
3426
|
+
setupTelemetry({
|
|
3427
|
+
enabled: true,
|
|
3428
|
+
source: "sdk",
|
|
3429
|
+
endpoint: this.endpoint,
|
|
3430
|
+
getAuthHeaders: () => this.authHeaders()
|
|
3431
|
+
});
|
|
3432
|
+
} catch (err) {
|
|
3433
|
+
this.logger.warn?.("[atbash] telemetry setup failed \u2014 continuing without metrics", { error: String(err) });
|
|
3434
|
+
}
|
|
3411
3435
|
}
|
|
3412
3436
|
/**
|
|
3413
3437
|
* Say which environment this build talks to, once per process.
|
|
@@ -3480,9 +3504,18 @@ var Atbash = class _Atbash {
|
|
|
3480
3504
|
*/
|
|
3481
3505
|
async checkAgentExists(pubkey, opts) {
|
|
3482
3506
|
const pk = pubkey ?? this.auth.pubkey;
|
|
3507
|
+
const network = opts?.network;
|
|
3508
|
+
const now = Date.now();
|
|
3509
|
+
const cached = this._agentExistsCache;
|
|
3510
|
+
if (cached && cached.pubkey === pk && cached.network === network && cached.expiresAt > now) {
|
|
3511
|
+
if (pk === this.auth.pubkey) {
|
|
3512
|
+
this._orgKeyFromChain = cached.orgKey;
|
|
3513
|
+
}
|
|
3514
|
+
return cached.registered;
|
|
3515
|
+
}
|
|
3483
3516
|
return this.track("checkAgentExists", pk, async () => {
|
|
3484
3517
|
const query = { pubkey: pk };
|
|
3485
|
-
if (
|
|
3518
|
+
if (network) query.network = network;
|
|
3486
3519
|
const resp = await this.http.get(
|
|
3487
3520
|
"/api/ai/exists",
|
|
3488
3521
|
query,
|
|
@@ -3490,11 +3523,21 @@ var Atbash = class _Atbash {
|
|
|
3490
3523
|
);
|
|
3491
3524
|
await this.raiseIfError(resp);
|
|
3492
3525
|
const data = await this.json(resp);
|
|
3526
|
+
const registered = Boolean(data?.registered);
|
|
3527
|
+
const orgKey = typeof data?.org_encryption_pubkey === "string" && data.org_encryption_pubkey ? data.org_encryption_pubkey : null;
|
|
3528
|
+
if (registered) {
|
|
3529
|
+
this._agentExistsCache = {
|
|
3530
|
+
pubkey: pk,
|
|
3531
|
+
network,
|
|
3532
|
+
expiresAt: Date.now() + _Atbash.AGENT_EXISTS_TTL_MS,
|
|
3533
|
+
registered,
|
|
3534
|
+
orgKey
|
|
3535
|
+
};
|
|
3536
|
+
}
|
|
3493
3537
|
if (pk === this.auth.pubkey) {
|
|
3494
|
-
|
|
3495
|
-
this._orgKeyFromChain = typeof key3 === "string" && key3 ? key3 : null;
|
|
3538
|
+
this._orgKeyFromChain = orgKey;
|
|
3496
3539
|
}
|
|
3497
|
-
return
|
|
3540
|
+
return registered;
|
|
3498
3541
|
});
|
|
3499
3542
|
}
|
|
3500
3543
|
/* ── log_tool_call (sign-only) ─────────────────────────────────────────── */
|
|
@@ -3575,12 +3618,19 @@ var Atbash = class _Atbash {
|
|
|
3575
3618
|
}
|
|
3576
3619
|
let chainOpts = options.chainOpts;
|
|
3577
3620
|
if (options.orgName) {
|
|
3578
|
-
const
|
|
3579
|
-
if (
|
|
3580
|
-
chainOpts = { network:
|
|
3581
|
-
} else
|
|
3582
|
-
const
|
|
3583
|
-
|
|
3621
|
+
const cached = this._chainCache.get(options.orgName);
|
|
3622
|
+
if (cached) {
|
|
3623
|
+
chainOpts = { network: cached.network };
|
|
3624
|
+
} else {
|
|
3625
|
+
const mapNetwork = await this.getActiveNetworkForOrg(options.orgName);
|
|
3626
|
+
if (mapNetwork) {
|
|
3627
|
+
const chain = mapNetwork === "private" ? PRIVATE_CHAIN : PUBLIC_CHAIN;
|
|
3628
|
+
this._chainCache.set(options.orgName, chain);
|
|
3629
|
+
chainOpts = { network: mapNetwork };
|
|
3630
|
+
} else if (!chainOpts?.blockchainRid) {
|
|
3631
|
+
const resolved = await this.resolveChainFromMap(options.orgName, null);
|
|
3632
|
+
chainOpts = { ...chainOpts, network: resolved.network };
|
|
3633
|
+
}
|
|
3584
3634
|
}
|
|
3585
3635
|
}
|
|
3586
3636
|
const brid = this.bridFromChainOpts(chainOpts);
|
|
@@ -3895,19 +3945,25 @@ var Atbash = class _Atbash {
|
|
|
3895
3945
|
});
|
|
3896
3946
|
}
|
|
3897
3947
|
/* ── risk-engine batched (action-dispatched POST) ──────────────────────── */
|
|
3898
|
-
getAgentDetail(agentPubkey) {
|
|
3899
|
-
return this.track(
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3948
|
+
async getAgentDetail(agentPubkey, options = {}) {
|
|
3949
|
+
return this.track("getAgentDetail", agentPubkey, async () => {
|
|
3950
|
+
const network = await this.resolveAgentLookupNetwork(options);
|
|
3951
|
+
return this.riskEnginePost(
|
|
3952
|
+
{ action: "agent-detail-batch", agent: agentPubkey },
|
|
3953
|
+
network
|
|
3954
|
+
);
|
|
3955
|
+
});
|
|
3904
3956
|
}
|
|
3905
|
-
async getAgentPolicy(agentPubkey) {
|
|
3957
|
+
async getAgentPolicy(agentPubkey, options = {}) {
|
|
3906
3958
|
return this.track("getAgentPolicy", agentPubkey, async () => {
|
|
3907
|
-
const
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3959
|
+
const network = await this.resolveAgentLookupNetwork(options);
|
|
3960
|
+
const raw2 = await this.riskEnginePost(
|
|
3961
|
+
{
|
|
3962
|
+
action: "agent-policy-batch",
|
|
3963
|
+
agent: agentPubkey
|
|
3964
|
+
},
|
|
3965
|
+
network
|
|
3966
|
+
);
|
|
3911
3967
|
return {
|
|
3912
3968
|
policy: String(raw2.policy ?? ""),
|
|
3913
3969
|
isJailed: Boolean(raw2.is_jailed),
|
|
@@ -4029,6 +4085,10 @@ var Atbash = class _Atbash {
|
|
|
4029
4085
|
clearChainCache() {
|
|
4030
4086
|
this._chainCache.clear();
|
|
4031
4087
|
}
|
|
4088
|
+
/** Drop the short-TTL `/api/ai/exists` cache. Useful in tests. */
|
|
4089
|
+
clearAgentExistsCache() {
|
|
4090
|
+
this._agentExistsCache = null;
|
|
4091
|
+
}
|
|
4032
4092
|
/* ── internals ─────────────────────────────────────────────────────────── */
|
|
4033
4093
|
/**
|
|
4034
4094
|
* Wrap an SDK method body in telemetry — records the call at start
|
|
@@ -4060,6 +4120,20 @@ var Atbash = class _Atbash {
|
|
|
4060
4120
|
if (chainOpts?.network === "public") return PUBLIC_CHAIN.blockchainRid;
|
|
4061
4121
|
return this.blockchainRid;
|
|
4062
4122
|
}
|
|
4123
|
+
/**
|
|
4124
|
+
* Resolve the dashboard chain used by agent metadata/policy reads.
|
|
4125
|
+
* Explicit per-call network overrides win; otherwise use the supplied org
|
|
4126
|
+
* or the client's configured default org. A custom BRID is intentionally
|
|
4127
|
+
* left untouched because it cannot be represented by the dashboard's
|
|
4128
|
+
* public/private query selector.
|
|
4129
|
+
*/
|
|
4130
|
+
async resolveAgentLookupNetwork(options) {
|
|
4131
|
+
if (options.chainOpts?.network) return options.chainOpts.network;
|
|
4132
|
+
if (options.chainOpts?.blockchainRid) return void 0;
|
|
4133
|
+
const orgName = options.orgName ?? this.orgName;
|
|
4134
|
+
if (!orgName) return void 0;
|
|
4135
|
+
return (await this.resolveChainForOrg(orgName)).network;
|
|
4136
|
+
}
|
|
4063
4137
|
/**
|
|
4064
4138
|
* Get-or-create a Bearer token for dashboard reads. The token is a
|
|
4065
4139
|
* signed `log_tool_call` op (locally signed, never submitted) — the
|
|
@@ -4102,10 +4176,11 @@ var Atbash = class _Atbash {
|
|
|
4102
4176
|
if (resp.status !== 200) throw await this.httpError(resp);
|
|
4103
4177
|
return this.json(resp);
|
|
4104
4178
|
}
|
|
4105
|
-
async riskEnginePost(body) {
|
|
4179
|
+
async riskEnginePost(body, network) {
|
|
4106
4180
|
let resp;
|
|
4107
4181
|
try {
|
|
4108
|
-
|
|
4182
|
+
const path7 = network ? `/api/risk-engine?network=${encodeURIComponent(network)}` : "/api/risk-engine";
|
|
4183
|
+
resp = await this.http.post(path7, body, this.authHeaders());
|
|
4109
4184
|
} catch (err) {
|
|
4110
4185
|
throw this.transportError(err);
|
|
4111
4186
|
}
|
|
@@ -4378,24 +4453,29 @@ async function scanMemory(entry, auth, opts) {
|
|
|
4378
4453
|
toolArgsJson: opts?.toolArgsJson ?? JSON.stringify({ key: entry.key, source: entry.source }),
|
|
4379
4454
|
mode: "memory-scan"
|
|
4380
4455
|
});
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
return {
|
|
4386
|
-
safe: false,
|
|
4387
|
-
verdict: "red",
|
|
4388
|
-
reason: unknownAction ? `judge returned unrecognised action_type "${result.actionType}"` : "judge returned no verdict",
|
|
4389
|
-
confidence: result.confidence,
|
|
4390
|
-
score: native.defaultScoreForVerdict("red"),
|
|
4391
|
-
toolCallId: result.toolCallId
|
|
4392
|
-
};
|
|
4456
|
+
if (result.verdict === "No verdict" && result.status !== "logged") {
|
|
4457
|
+
throw new Error(
|
|
4458
|
+
`memory scan: judge returned no verdict without an audit-tier marker (status: ${result.status || "absent"})`
|
|
4459
|
+
);
|
|
4393
4460
|
}
|
|
4394
|
-
const
|
|
4395
|
-
|
|
4461
|
+
const KNOWN_ACTIONS = ["allow", "block", "hold_for_user_confirm"];
|
|
4462
|
+
const action = result.actionType.trim().toLowerCase();
|
|
4463
|
+
if (result.verdict !== "No verdict" && !KNOWN_ACTIONS.includes(action)) {
|
|
4464
|
+
throw new Error(
|
|
4465
|
+
`memory scan: unrecognized action_type from judge (${result.actionType || "absent"})`
|
|
4466
|
+
);
|
|
4467
|
+
}
|
|
4468
|
+
const mapped = native.mapVerdict(
|
|
4469
|
+
action,
|
|
4396
4470
|
result.confidence,
|
|
4397
4471
|
threshold
|
|
4398
4472
|
);
|
|
4473
|
+
let verdict = mapped;
|
|
4474
|
+
if (result.verdict === "BLOCK") {
|
|
4475
|
+
verdict = "red";
|
|
4476
|
+
} else if (result.verdict === "HOLD" && mapped === "green") {
|
|
4477
|
+
verdict = "yellow";
|
|
4478
|
+
}
|
|
4399
4479
|
const parsed = native.parseScoreFromReason(result.reason);
|
|
4400
4480
|
const score = result.score ?? parsed.score ?? native.defaultScoreForVerdict(verdict);
|
|
4401
4481
|
return {
|
|
@@ -9301,8 +9381,8 @@ Reporter$3.prototype.wrapResult = function wrapResult(result) {
|
|
|
9301
9381
|
errors: state2.errors
|
|
9302
9382
|
};
|
|
9303
9383
|
};
|
|
9304
|
-
function ReporterError$1(
|
|
9305
|
-
this.path =
|
|
9384
|
+
function ReporterError$1(path7, msg) {
|
|
9385
|
+
this.path = path7;
|
|
9306
9386
|
this.rethrow(msg);
|
|
9307
9387
|
}
|
|
9308
9388
|
inherits$v(ReporterError$1, Error);
|
|
@@ -29523,8 +29603,8 @@ Reporter.prototype.wrapResult = function wrapResult2(result) {
|
|
|
29523
29603
|
errors: state2.errors
|
|
29524
29604
|
};
|
|
29525
29605
|
};
|
|
29526
|
-
function ReporterError(
|
|
29527
|
-
this.path =
|
|
29606
|
+
function ReporterError(path7, msg) {
|
|
29607
|
+
this.path = path7;
|
|
29528
29608
|
this.rethrow(msg);
|
|
29529
29609
|
}
|
|
29530
29610
|
inherits(ReporterError, Error);
|
|
@@ -32554,8 +32634,8 @@ var parseUtil = {};
|
|
|
32554
32634
|
const errors_js_12 = errors$3;
|
|
32555
32635
|
const en_js_12 = __importDefault2(en);
|
|
32556
32636
|
const makeIssue = (params) => {
|
|
32557
|
-
const { data, path:
|
|
32558
|
-
const fullPath = [...
|
|
32637
|
+
const { data, path: path7, errorMaps, issueData } = params;
|
|
32638
|
+
const fullPath = [...path7, ...issueData.path || []];
|
|
32559
32639
|
const fullIssue = {
|
|
32560
32640
|
...issueData,
|
|
32561
32641
|
path: fullPath
|
|
@@ -32692,11 +32772,11 @@ var errorUtil_js_1 = errorUtil$1;
|
|
|
32692
32772
|
var parseUtil_js_1 = parseUtil;
|
|
32693
32773
|
var util_js_1 = util;
|
|
32694
32774
|
var ParseInputLazyPath = class {
|
|
32695
|
-
constructor(parent, value,
|
|
32775
|
+
constructor(parent, value, path7, key3) {
|
|
32696
32776
|
this._cachedPath = [];
|
|
32697
32777
|
this.parent = parent;
|
|
32698
32778
|
this.data = value;
|
|
32699
|
-
this._path =
|
|
32779
|
+
this._path = path7;
|
|
32700
32780
|
this._key = key3;
|
|
32701
32781
|
}
|
|
32702
32782
|
get path() {
|
|
@@ -39602,21 +39682,21 @@ function createTimeoutController(timeout) {
|
|
|
39602
39682
|
const timeoutId = setTimeout(() => controller.abort(timeoutError), timeout);
|
|
39603
39683
|
return { controller, timeoutId };
|
|
39604
39684
|
}
|
|
39605
|
-
function handleRequest(method,
|
|
39685
|
+
function handleRequest(method, path7, endpoint, timeout, postObject) {
|
|
39606
39686
|
return __awaiter$2(this, void 0, void 0, function* () {
|
|
39607
39687
|
if (method == enums_1$2.Method.GET) {
|
|
39608
|
-
return yield get(
|
|
39688
|
+
return yield get(path7, endpoint, timeout);
|
|
39609
39689
|
} else {
|
|
39610
|
-
return yield post(
|
|
39690
|
+
return yield post(path7, endpoint, timeout, postObject);
|
|
39611
39691
|
}
|
|
39612
39692
|
});
|
|
39613
39693
|
}
|
|
39614
|
-
function get(
|
|
39694
|
+
function get(path7, endpoint, timeout) {
|
|
39615
39695
|
return __awaiter$2(this, void 0, void 0, function* () {
|
|
39616
|
-
logger.debug(`GET URL ${new URL(
|
|
39696
|
+
logger.debug(`GET URL ${new URL(path7, endpoint).href}`);
|
|
39617
39697
|
try {
|
|
39618
39698
|
const { controller, timeoutId } = createTimeoutController(timeout);
|
|
39619
|
-
const response = yield fetch(new URL(
|
|
39699
|
+
const response = yield fetch(new URL(path7, endpoint).href, {
|
|
39620
39700
|
signal: controller === null || controller === void 0 ? void 0 : controller.signal
|
|
39621
39701
|
});
|
|
39622
39702
|
if (timeoutId)
|
|
@@ -39654,9 +39734,9 @@ function constructBufferResponseBody(response) {
|
|
|
39654
39734
|
return responseText ? responseText : response.statusText;
|
|
39655
39735
|
});
|
|
39656
39736
|
}
|
|
39657
|
-
function post(
|
|
39737
|
+
function post(path7, endpoint, timeout, requestBody) {
|
|
39658
39738
|
return __awaiter$2(this, void 0, void 0, function* () {
|
|
39659
|
-
logger.debug(`POST URL ${new URL(
|
|
39739
|
+
logger.debug(`POST URL ${new URL(path7, endpoint).href}`);
|
|
39660
39740
|
logger.debug(`POST body ${JSON.stringify(requestBody)}`);
|
|
39661
39741
|
if (buffer_1.Buffer.isBuffer(requestBody)) {
|
|
39662
39742
|
try {
|
|
@@ -39670,7 +39750,7 @@ function post(path6, endpoint, timeout, requestBody) {
|
|
|
39670
39750
|
},
|
|
39671
39751
|
signal: controller === null || controller === void 0 ? void 0 : controller.signal
|
|
39672
39752
|
};
|
|
39673
|
-
const response = yield fetch(new URL(
|
|
39753
|
+
const response = yield fetch(new URL(path7, endpoint).href, requestOptions);
|
|
39674
39754
|
if (timeoutId)
|
|
39675
39755
|
clearTimeout(timeoutId);
|
|
39676
39756
|
const transactionTimestamp = response.headers.get("X-Transaction-Timestamp");
|
|
@@ -39681,7 +39761,7 @@ function post(path6, endpoint, timeout, requestBody) {
|
|
|
39681
39761
|
} else {
|
|
39682
39762
|
try {
|
|
39683
39763
|
const { controller, timeoutId } = createTimeoutController(timeout);
|
|
39684
|
-
const response = yield fetch(new URL(
|
|
39764
|
+
const response = yield fetch(new URL(path7, endpoint).href, {
|
|
39685
39765
|
method: "post",
|
|
39686
39766
|
body: JSON.stringify(requestBody),
|
|
39687
39767
|
headers: {
|
|
@@ -39861,10 +39941,10 @@ function requireFailoverStrategies() {
|
|
|
39861
39941
|
}
|
|
39862
39942
|
}
|
|
39863
39943
|
function abortOnError(_a2) {
|
|
39864
|
-
return __awaiter2(this, arguments, void 0, function* ({ method, path:
|
|
39944
|
+
return __awaiter2(this, arguments, void 0, function* ({ method, path: path7, config: config2, postObject, timeoutOverride }) {
|
|
39865
39945
|
return yield retryRequest({
|
|
39866
39946
|
method,
|
|
39867
|
-
path:
|
|
39947
|
+
path: path7,
|
|
39868
39948
|
config: config2,
|
|
39869
39949
|
postObject,
|
|
39870
39950
|
timeoutOverride,
|
|
@@ -39875,10 +39955,10 @@ function requireFailoverStrategies() {
|
|
|
39875
39955
|
});
|
|
39876
39956
|
}
|
|
39877
39957
|
function tryNextOnError(_a2) {
|
|
39878
|
-
return __awaiter2(this, arguments, void 0, function* ({ method, path:
|
|
39958
|
+
return __awaiter2(this, arguments, void 0, function* ({ method, path: path7, config: config2, postObject, timeoutOverride }) {
|
|
39879
39959
|
return yield retryRequest({
|
|
39880
39960
|
method,
|
|
39881
|
-
path:
|
|
39961
|
+
path: path7,
|
|
39882
39962
|
config: config2,
|
|
39883
39963
|
postObject,
|
|
39884
39964
|
timeoutOverride,
|
|
@@ -39894,7 +39974,7 @@ function requireFailoverStrategies() {
|
|
|
39894
39974
|
return endpointPoolLength - (endpointPoolLength - 1) / 3;
|
|
39895
39975
|
}
|
|
39896
39976
|
function queryMajority(_a2) {
|
|
39897
|
-
return __awaiter2(this, arguments, void 0, function* ({ method, path:
|
|
39977
|
+
return __awaiter2(this, arguments, void 0, function* ({ method, path: path7, config: config2, postObject, timeoutOverride }) {
|
|
39898
39978
|
var _b;
|
|
39899
39979
|
const bftMajorityThreshold = calculateBftMajorityThreshold(config2.endpointPool.length);
|
|
39900
39980
|
const failureThreshold = config2.endpointPool.length - bftMajorityThreshold + 1;
|
|
@@ -39905,7 +39985,7 @@ function requireFailoverStrategies() {
|
|
|
39905
39985
|
const promises = availableNodes.map((node2) => __awaiter2(this, void 0, void 0, function* () {
|
|
39906
39986
|
try {
|
|
39907
39987
|
const requestTimeout = timeoutOverride !== null && timeoutOverride !== void 0 ? timeoutOverride : config2.responseTimeout;
|
|
39908
|
-
const response = yield (0, httpUtil_1.handleRequest)(method,
|
|
39988
|
+
const response = yield (0, httpUtil_1.handleRequest)(method, path7, node2.url, requestTimeout, postObject);
|
|
39909
39989
|
const { statusCode } = response;
|
|
39910
39990
|
if (statusCode && (0, http_utils_1.isSuccessfulStatusCode)(statusCode)) {
|
|
39911
39991
|
outcomes.push({ type: "SUCCESS", result: response });
|
|
@@ -39952,7 +40032,7 @@ function requireFailoverStrategies() {
|
|
|
39952
40032
|
});
|
|
39953
40033
|
}
|
|
39954
40034
|
function singleEndpoint(_a2) {
|
|
39955
|
-
return __awaiter2(this, arguments, void 0, function* ({ method, path:
|
|
40035
|
+
return __awaiter2(this, arguments, void 0, function* ({ method, path: path7, config: config2, postObject, timeoutOverride }) {
|
|
39956
40036
|
let statusCode = null;
|
|
39957
40037
|
let rspBody = null;
|
|
39958
40038
|
let error4 = null;
|
|
@@ -39963,7 +40043,7 @@ function requireFailoverStrategies() {
|
|
|
39963
40043
|
}
|
|
39964
40044
|
for (let attempt = 0; attempt < config2.attemptsPerEndpoint; attempt++) {
|
|
39965
40045
|
const requestTimeout = timeoutOverride !== null && timeoutOverride !== void 0 ? timeoutOverride : config2.responseTimeout;
|
|
39966
|
-
const response = yield (0, httpUtil_1.handleRequest)(method,
|
|
40046
|
+
const response = yield (0, httpUtil_1.handleRequest)(method, path7, endpoint.url, requestTimeout, postObject);
|
|
39967
40047
|
if (response) {
|
|
39968
40048
|
({ error: error4, statusCode, rspBody, transactionTimestamp } = response);
|
|
39969
40049
|
}
|
|
@@ -39978,7 +40058,7 @@ function requireFailoverStrategies() {
|
|
|
39978
40058
|
});
|
|
39979
40059
|
}
|
|
39980
40060
|
function retryRequest(_a2) {
|
|
39981
|
-
return __awaiter2(this, arguments, void 0, function* ({ method, path:
|
|
40061
|
+
return __awaiter2(this, arguments, void 0, function* ({ method, path: path7, config: config2, postObject, useNextNodeOnError, recoveryThreshold, timeoutOverride }) {
|
|
39982
40062
|
var _b, _c, _d;
|
|
39983
40063
|
let statusCode = null;
|
|
39984
40064
|
let rspBody = null;
|
|
@@ -39989,7 +40069,7 @@ function requireFailoverStrategies() {
|
|
|
39989
40069
|
for (const node2 of availableNodes) {
|
|
39990
40070
|
for (let attempt = 0; attempt < config2.attemptsPerEndpoint; attempt++) {
|
|
39991
40071
|
const requestTimeout = timeoutOverride !== null && timeoutOverride !== void 0 ? timeoutOverride : config2.responseTimeout;
|
|
39992
|
-
const response = yield (0, httpUtil_1.handleRequest)(method,
|
|
40072
|
+
const response = yield (0, httpUtil_1.handleRequest)(method, path7, node2.url, requestTimeout, postObject);
|
|
39993
40073
|
error4 = (_b = response === null || response === void 0 ? void 0 : response.error) !== null && _b !== void 0 ? _b : null;
|
|
39994
40074
|
statusCode = (_c = response === null || response === void 0 ? void 0 : response.statusCode) !== null && _c !== void 0 ? _c : null;
|
|
39995
40075
|
rspBody = (_d = response === null || response === void 0 ? void 0 : response.rspBody) !== null && _d !== void 0 ? _d : null;
|
|
@@ -40132,19 +40212,19 @@ function requireRequestWithFailoverStrategy() {
|
|
|
40132
40212
|
const enums_12 = enums;
|
|
40133
40213
|
const failoverStrategies_1 = requireFailoverStrategies();
|
|
40134
40214
|
function requestWithFailoverStrategy$1(method_1, path_1, config_1, postObject_1) {
|
|
40135
|
-
return __awaiter2(this, arguments, void 0, function* (method,
|
|
40215
|
+
return __awaiter2(this, arguments, void 0, function* (method, path7, config2, postObject, forceSingleEndpoint = false, timeoutOverride) {
|
|
40136
40216
|
switch (config2.failoverStrategy) {
|
|
40137
40217
|
case enums_12.FailoverStrategy.AbortOnError:
|
|
40138
|
-
return yield (0, failoverStrategies_1.abortOnError)({ method, path:
|
|
40218
|
+
return yield (0, failoverStrategies_1.abortOnError)({ method, path: path7, config: config2, postObject, timeoutOverride });
|
|
40139
40219
|
case enums_12.FailoverStrategy.TryNextOnError:
|
|
40140
|
-
return yield (0, failoverStrategies_1.tryNextOnError)({ method, path:
|
|
40220
|
+
return yield (0, failoverStrategies_1.tryNextOnError)({ method, path: path7, config: config2, postObject, timeoutOverride });
|
|
40141
40221
|
case enums_12.FailoverStrategy.SingleEndpoint:
|
|
40142
|
-
return yield (0, failoverStrategies_1.singleEndpoint)({ method, path:
|
|
40222
|
+
return yield (0, failoverStrategies_1.singleEndpoint)({ method, path: path7, config: config2, postObject, timeoutOverride });
|
|
40143
40223
|
case enums_12.FailoverStrategy.QueryMajority:
|
|
40144
40224
|
if (forceSingleEndpoint) {
|
|
40145
|
-
return yield (0, failoverStrategies_1.singleEndpoint)({ method, path:
|
|
40225
|
+
return yield (0, failoverStrategies_1.singleEndpoint)({ method, path: path7, config: config2, postObject, timeoutOverride });
|
|
40146
40226
|
}
|
|
40147
|
-
return yield (0, failoverStrategies_1.queryMajority)({ method, path:
|
|
40227
|
+
return yield (0, failoverStrategies_1.queryMajority)({ method, path: path7, config: config2, postObject, timeoutOverride });
|
|
40148
40228
|
default:
|
|
40149
40229
|
throw new Error(`Unsupported failover strategy: ${config2.failoverStrategy}`);
|
|
40150
40230
|
}
|
|
@@ -41343,7 +41423,7 @@ var networkSettings = {};
|
|
|
41343
41423
|
const restNetworkSettingsValidationContext = RestNetworkSettingsSchema.safeParse(networkSettings2);
|
|
41344
41424
|
if ("error" in restNetworkSettingsValidationContext) {
|
|
41345
41425
|
const { error: { issues } = {} } = restNetworkSettingsValidationContext;
|
|
41346
|
-
const errorMessage2 = issues === null || issues === void 0 ? void 0 : issues.map(({ message, path:
|
|
41426
|
+
const errorMessage2 = issues === null || issues === void 0 ? void 0 : issues.map(({ message, path: path7 }) => `${path7[0]}: ${message}`).join(", ");
|
|
41347
41427
|
if (throwOnError) {
|
|
41348
41428
|
throw new Error(errorMessage2);
|
|
41349
41429
|
}
|
|
@@ -42725,6 +42805,7 @@ function classifyMemoryWrite(event, ctx, opts = {}) {
|
|
|
42725
42805
|
}
|
|
42726
42806
|
|
|
42727
42807
|
// src-ts/memory/guard.ts
|
|
42808
|
+
import path3 from "path";
|
|
42728
42809
|
function emitDebugProbe(event, ctx, memEntry, logger2) {
|
|
42729
42810
|
if (!logger2?.info) return;
|
|
42730
42811
|
const ev = event ?? {};
|
|
@@ -42761,7 +42842,8 @@ async function guardMemoryWrite(input) {
|
|
|
42761
42842
|
toolNames,
|
|
42762
42843
|
enforce = true,
|
|
42763
42844
|
debug: debug2 = false,
|
|
42764
|
-
logger: logger2
|
|
42845
|
+
logger: logger2,
|
|
42846
|
+
memoryFilePath
|
|
42765
42847
|
} = input;
|
|
42766
42848
|
const memEntry = classifyMemoryWrite(event, ctx, { patterns, toolNames });
|
|
42767
42849
|
if (debug2) emitDebugProbe(event, ctx, memEntry, logger2);
|
|
@@ -42797,17 +42879,28 @@ async function guardMemoryWrite(input) {
|
|
|
42797
42879
|
committed: false
|
|
42798
42880
|
};
|
|
42799
42881
|
}
|
|
42800
|
-
|
|
42801
|
-
|
|
42802
|
-
|
|
42803
|
-
|
|
42804
|
-
|
|
42805
|
-
|
|
42806
|
-
|
|
42807
|
-
|
|
42808
|
-
|
|
42882
|
+
const isManagedMemoryFile = memoryFilePath !== void 0 && path3.resolve(memEntry.key) === path3.resolve(memoryFilePath);
|
|
42883
|
+
if (isManagedMemoryFile) {
|
|
42884
|
+
commitMemoryVersion(memEntry.value, auth, {
|
|
42885
|
+
score: scanResult.score,
|
|
42886
|
+
orgName,
|
|
42887
|
+
endpoint
|
|
42888
|
+
}).catch((err) => {
|
|
42889
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
42890
|
+
logger2?.warn?.("[atbash] memory commit to chain failed", {
|
|
42891
|
+
path: memEntry.key,
|
|
42892
|
+
reason
|
|
42893
|
+
});
|
|
42809
42894
|
});
|
|
42810
|
-
}
|
|
42895
|
+
} else {
|
|
42896
|
+
logger2?.info?.(
|
|
42897
|
+
"[atbash] scanned but not committed \u2014 not the managed memory file",
|
|
42898
|
+
{
|
|
42899
|
+
path: memEntry.key,
|
|
42900
|
+
memoryFilePath: memoryFilePath ?? "(not configured)"
|
|
42901
|
+
}
|
|
42902
|
+
);
|
|
42903
|
+
}
|
|
42811
42904
|
logger2?.info?.(
|
|
42812
42905
|
scanResult.verdict === "yellow" ? "[atbash] memory HOLD" : "[atbash] memory ALLOW",
|
|
42813
42906
|
{ path: memEntry.key, score: scanResult.score, reason: scanResult.reason }
|
|
@@ -42816,7 +42909,7 @@ async function guardMemoryWrite(input) {
|
|
|
42816
42909
|
handled: true,
|
|
42817
42910
|
decision: { allow: true },
|
|
42818
42911
|
scanResult,
|
|
42819
|
-
committed:
|
|
42912
|
+
committed: isManagedMemoryFile
|
|
42820
42913
|
};
|
|
42821
42914
|
}
|
|
42822
42915
|
|
|
@@ -42835,26 +42928,26 @@ async function syncLocalMemory(auth, pointer, opts = {}) {
|
|
|
42835
42928
|
const now = Date.now();
|
|
42836
42929
|
const withinTtl = !opts.force && now - pointer.checkedAt < ttl;
|
|
42837
42930
|
if (withinTtl) {
|
|
42838
|
-
return { drifted: false, pointer };
|
|
42931
|
+
return { drifted: false, checked: false, pointer };
|
|
42839
42932
|
}
|
|
42840
42933
|
const currentId = await getActiveMemoryId(auth, opts.chainOpts);
|
|
42841
42934
|
const nextPointer = { activeId: currentId, checkedAt: now };
|
|
42842
42935
|
if (currentId === pointer.activeId) {
|
|
42843
|
-
return { drifted: false, pointer: nextPointer };
|
|
42936
|
+
return { drifted: false, checked: true, pointer: nextPointer };
|
|
42844
42937
|
}
|
|
42845
42938
|
if (currentId === null) {
|
|
42846
|
-
return { drifted: true, current: null, pointer: nextPointer };
|
|
42939
|
+
return { drifted: true, checked: true, current: null, pointer: nextPointer };
|
|
42847
42940
|
}
|
|
42848
42941
|
const row = await getMemoryById(currentId, auth, opts.chainOpts);
|
|
42849
42942
|
if (row.decryptError) {
|
|
42850
42943
|
throw new MemoryIntegrityError(currentId, row.decryptError);
|
|
42851
42944
|
}
|
|
42852
|
-
return { drifted: true, current: row, pointer: nextPointer };
|
|
42945
|
+
return { drifted: true, checked: true, current: row, pointer: nextPointer };
|
|
42853
42946
|
}
|
|
42854
42947
|
|
|
42855
42948
|
// src-ts/memory/pointer-store.ts
|
|
42856
42949
|
import { promises as fs } from "fs";
|
|
42857
|
-
import
|
|
42950
|
+
import path4 from "path";
|
|
42858
42951
|
var EMPTY = { version: 1, agents: {} };
|
|
42859
42952
|
var PointerStore = class {
|
|
42860
42953
|
constructor(filePath) {
|
|
@@ -42896,19 +42989,19 @@ var PointerStore = class {
|
|
|
42896
42989
|
this.cache = { ...EMPTY, agents: {} };
|
|
42897
42990
|
}
|
|
42898
42991
|
async persist(file) {
|
|
42899
|
-
await fs.mkdir(
|
|
42992
|
+
await fs.mkdir(path4.dirname(this.filePath), { recursive: true });
|
|
42900
42993
|
const tmp = `${this.filePath}.${process.pid}.tmp`;
|
|
42901
42994
|
await fs.writeFile(tmp, JSON.stringify(file, null, 2), "utf8");
|
|
42902
42995
|
await fs.rename(tmp, this.filePath);
|
|
42903
42996
|
}
|
|
42904
42997
|
};
|
|
42905
42998
|
function defaultPointerPath(workspaceDir = process.cwd()) {
|
|
42906
|
-
return
|
|
42999
|
+
return path4.join(workspaceDir, ".atbash", "memory-pointer.json");
|
|
42907
43000
|
}
|
|
42908
43001
|
|
|
42909
43002
|
// src-ts/memory/file-logger.ts
|
|
42910
43003
|
import { promises as fs2 } from "fs";
|
|
42911
|
-
import
|
|
43004
|
+
import path5 from "path";
|
|
42912
43005
|
function formatMeta(meta) {
|
|
42913
43006
|
if (!meta || Object.keys(meta).length === 0) return "";
|
|
42914
43007
|
try {
|
|
@@ -42920,7 +43013,7 @@ function formatMeta(meta) {
|
|
|
42920
43013
|
function createFileLogger(filePath, upstream) {
|
|
42921
43014
|
let queue = Promise.resolve();
|
|
42922
43015
|
async function ensureDir() {
|
|
42923
|
-
await fs2.mkdir(
|
|
43016
|
+
await fs2.mkdir(path5.dirname(filePath), { recursive: true });
|
|
42924
43017
|
}
|
|
42925
43018
|
function append(level, message, meta) {
|
|
42926
43019
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${message}${formatMeta(meta)}
|
|
@@ -42940,7 +43033,7 @@ function createFileLogger(filePath, upstream) {
|
|
|
42940
43033
|
};
|
|
42941
43034
|
}
|
|
42942
43035
|
function defaultPluginLogPath(workspaceDir = process.cwd()) {
|
|
42943
|
-
return
|
|
43036
|
+
return path5.join(workspaceDir, ".atbash", "plugin.log");
|
|
42944
43037
|
}
|
|
42945
43038
|
|
|
42946
43039
|
// src-ts/memory/read-classifier.ts
|
|
@@ -42955,13 +43048,13 @@ function classifyMemoryRead(event, ctx, opts = {}) {
|
|
|
42955
43048
|
|
|
42956
43049
|
// src-ts/memory/guard-manager.ts
|
|
42957
43050
|
import { promises as fs3 } from "fs";
|
|
42958
|
-
import
|
|
43051
|
+
import path6 from "path";
|
|
42959
43052
|
var DEFAULT_SYNC_TTL_MS = 3e4;
|
|
42960
43053
|
var MemoryGuardManager = class {
|
|
42961
43054
|
constructor(opts) {
|
|
42962
43055
|
this.opts = opts;
|
|
42963
43056
|
const workspaceDir = opts.workspaceDir;
|
|
42964
|
-
this.memoryFilePath = opts.memoryFilePath ??
|
|
43057
|
+
this.memoryFilePath = opts.memoryFilePath ?? path6.join(workspaceDir, "MEMORY.md");
|
|
42965
43058
|
this.pointerStore = new PointerStore(opts.pointerFilePath ?? defaultPointerPath(workspaceDir));
|
|
42966
43059
|
this.logger = createFileLogger(
|
|
42967
43060
|
opts.logFilePath ?? defaultPluginLogPath(workspaceDir),
|
|
@@ -42991,7 +43084,11 @@ var MemoryGuardManager = class {
|
|
|
42991
43084
|
async runBootProbe() {
|
|
42992
43085
|
try {
|
|
42993
43086
|
const seed = { activeId: null, checkedAt: 0 };
|
|
42994
|
-
const result = await syncLocalMemory(this.opts.auth, seed, {
|
|
43087
|
+
const result = await syncLocalMemory(this.opts.auth, seed, {
|
|
43088
|
+
ttlMs: 0,
|
|
43089
|
+
force: true,
|
|
43090
|
+
chainOpts: this.opts.chainOpts
|
|
43091
|
+
});
|
|
42995
43092
|
if (!result.drifted && result.pointer.activeId == null) {
|
|
42996
43093
|
this.logger.info(
|
|
42997
43094
|
`[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.`
|
|
@@ -43023,15 +43120,19 @@ var MemoryGuardManager = class {
|
|
|
43023
43120
|
}
|
|
43024
43121
|
}
|
|
43025
43122
|
/**
|
|
43026
|
-
* Returns a `HookDecision` when the
|
|
43027
|
-
*
|
|
43028
|
-
*
|
|
43123
|
+
* Returns a `HookDecision` when the guard reached a decision about this event.
|
|
43124
|
+
* Returns `null` when it did not — either the event isn't memory-related, or it
|
|
43125
|
+
* is but the guard could not check it. In both cases the host falls through to
|
|
43126
|
+
* its own audit.
|
|
43127
|
+
*
|
|
43128
|
+
* A returned decision carries `audited` (see `HookDecision`). Only
|
|
43129
|
+
* `{ allow: true, audited: true }` means "checked and cleared"; anything else
|
|
43130
|
+
* that allows is a call the host still needs to judge.
|
|
43029
43131
|
*/
|
|
43030
43132
|
async handleBeforeToolCall(event, ctx) {
|
|
43031
43133
|
if (classifyMemoryRead(event, ctx, this.opts.memoryReadClassifier)) {
|
|
43032
43134
|
this.logger.info("[atbash] memory read intercepted \u2014 running sync check");
|
|
43033
|
-
|
|
43034
|
-
return readDecision ?? { allow: true };
|
|
43135
|
+
return await this.handleMemoryRead(event, ctx);
|
|
43035
43136
|
}
|
|
43036
43137
|
const guardLogger = {
|
|
43037
43138
|
info: (msg, meta) => this.logger.info(msg, meta && typeof meta === "object" ? meta : void 0),
|
|
@@ -43048,7 +43149,9 @@ var MemoryGuardManager = class {
|
|
|
43048
43149
|
toolNames: this.opts.memoryWriteToolNames,
|
|
43049
43150
|
enforce: this.enforce,
|
|
43050
43151
|
debug: this.opts.debug,
|
|
43051
|
-
logger: guardLogger
|
|
43152
|
+
logger: guardLogger,
|
|
43153
|
+
// Only this file may reach the single, path-less chain memory slot.
|
|
43154
|
+
memoryFilePath: this.memoryFilePath
|
|
43052
43155
|
});
|
|
43053
43156
|
return this.mapGuardResult(guard);
|
|
43054
43157
|
}
|
|
@@ -43066,30 +43169,81 @@ var MemoryGuardManager = class {
|
|
|
43066
43169
|
block: true,
|
|
43067
43170
|
blockReason: d.reason ?? "",
|
|
43068
43171
|
allow: false,
|
|
43069
|
-
reason: d.reason
|
|
43172
|
+
reason: d.reason,
|
|
43173
|
+
// A block IS a decision — the most thoroughly checked one the guard
|
|
43174
|
+
// makes. Without this a host following the documented `!audited ->
|
|
43175
|
+
// judge it yourself` rule would re-judge its way past a red scan.
|
|
43176
|
+
audited: true,
|
|
43177
|
+
...sr2 ? { verdict: sr2.verdict } : {}
|
|
43070
43178
|
};
|
|
43071
43179
|
}
|
|
43072
43180
|
this.logger.info(
|
|
43073
43181
|
`[atbash] guardMemoryWrite ALLOWED \u2014 verdict=${verdict} score=${score} committed=${guard.committed === true}`
|
|
43074
43182
|
);
|
|
43075
|
-
|
|
43183
|
+
if (sr2 === void 0) {
|
|
43184
|
+
return { allow: true, audited: false, reason: "memory scan did not run (observe mode)" };
|
|
43185
|
+
}
|
|
43186
|
+
if (sr2.verdict !== "green") {
|
|
43187
|
+
return {
|
|
43188
|
+
allow: true,
|
|
43189
|
+
audited: false,
|
|
43190
|
+
verdict: sr2.verdict,
|
|
43191
|
+
reason: `memory scan returned ${sr2.verdict} but this guard is not enforcing it`
|
|
43192
|
+
};
|
|
43193
|
+
}
|
|
43194
|
+
return { allow: true, audited: true, verdict: sr2.verdict };
|
|
43076
43195
|
}
|
|
43077
|
-
|
|
43196
|
+
/**
|
|
43197
|
+
* Whether the pointer state this manager tracks actually describes the file
|
|
43198
|
+
* this call is about to read.
|
|
43199
|
+
*
|
|
43200
|
+
* The classifier fires on nine patterns — including the bare tokens
|
|
43201
|
+
* `"memory/"`, `"CLAUDE.md"` and `"AGENTS.md"` — but the sync path only ever
|
|
43202
|
+
* reads, refreshes, or vouches for `this.memoryFilePath`. Without this check a
|
|
43203
|
+
* read of `/repo/CLAUDE.md` (or any path merely containing `memory/`) would
|
|
43204
|
+
* receive an `audited: true` for a file the guard never opened.
|
|
43205
|
+
*
|
|
43206
|
+
* Conservative on purpose: every path-shaped value found must resolve to the
|
|
43207
|
+
* managed file. If none is found, or any one differs, the answer is no. That
|
|
43208
|
+
* also covers events carrying two different path keys, where the classifier
|
|
43209
|
+
* and the host could otherwise disagree about which one is authoritative.
|
|
43210
|
+
*/
|
|
43211
|
+
vouchesForTarget(event, ctx) {
|
|
43212
|
+
const KEYS = ["path", "file_path", "filePath", "notebook_path", "notebookPath", "target"];
|
|
43213
|
+
const found = [];
|
|
43214
|
+
for (const src of [event, ctx]) {
|
|
43215
|
+
for (const bag of [src, src?.params]) {
|
|
43216
|
+
if (!bag || typeof bag !== "object") continue;
|
|
43217
|
+
const rec = bag;
|
|
43218
|
+
for (const k of KEYS) {
|
|
43219
|
+
if (typeof rec[k] === "string" && rec[k]) found.push(rec[k]);
|
|
43220
|
+
}
|
|
43221
|
+
}
|
|
43222
|
+
}
|
|
43223
|
+
if (found.length === 0) return false;
|
|
43224
|
+
const managed = path6.resolve(this.memoryFilePath);
|
|
43225
|
+
return found.every((p) => path6.resolve(p) === managed);
|
|
43226
|
+
}
|
|
43227
|
+
async handleMemoryRead(event, ctx) {
|
|
43078
43228
|
const pointer = await this.pointerStore.get(this.agentPubkeyHex);
|
|
43079
43229
|
let result;
|
|
43080
43230
|
try {
|
|
43081
|
-
result = await syncLocalMemory(this.opts.auth, pointer, {
|
|
43231
|
+
result = await syncLocalMemory(this.opts.auth, pointer, {
|
|
43232
|
+
ttlMs: this.ttlMs,
|
|
43233
|
+
chainOpts: this.opts.chainOpts
|
|
43234
|
+
});
|
|
43082
43235
|
} catch (err) {
|
|
43083
43236
|
if (err instanceof MemoryIntegrityError) {
|
|
43084
43237
|
const reason = `Memory integrity check failed on id ${err.id} \u2014 read blocked.`;
|
|
43085
43238
|
this.logger.warn("[atbash] MEMORY INTEGRITY FAILURE", { id: err.id, error: err.message });
|
|
43086
43239
|
if (!this.enforce) return null;
|
|
43087
|
-
return { block: true, blockReason: reason, allow: false, reason };
|
|
43240
|
+
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43088
43241
|
}
|
|
43089
43242
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43090
43243
|
this.logger.warn("[atbash] memory sync failed (serving local copy)", { error: msg });
|
|
43091
43244
|
return null;
|
|
43092
43245
|
}
|
|
43246
|
+
let onDiskIsCurrent = result.checked;
|
|
43093
43247
|
if (result.drifted) {
|
|
43094
43248
|
const fresh = result.current;
|
|
43095
43249
|
if (fresh) {
|
|
@@ -43097,7 +43251,7 @@ var MemoryGuardManager = class {
|
|
|
43097
43251
|
const reason = `Rolled-back memory version #${fresh.id} scored ${fresh.score} (below threshold ${this.rollbackMinScore}) \u2014 read blocked.`;
|
|
43098
43252
|
this.logger.warn("[atbash] blocking read on low-score rollback", { id: fresh.id, score: fresh.score });
|
|
43099
43253
|
if (!this.enforce) return null;
|
|
43100
|
-
return { block: true, blockReason: reason, allow: false, reason };
|
|
43254
|
+
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43101
43255
|
}
|
|
43102
43256
|
this.logger.info("[atbash] memory drift detected \u2014 refreshing local file", {
|
|
43103
43257
|
id: fresh.id,
|
|
@@ -43108,16 +43262,26 @@ var MemoryGuardManager = class {
|
|
|
43108
43262
|
} catch (err) {
|
|
43109
43263
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43110
43264
|
this.logger.warn("[atbash] failed to write refreshed memory (serving old)", { error: msg });
|
|
43265
|
+
onDiskIsCurrent = false;
|
|
43111
43266
|
}
|
|
43112
43267
|
} else {
|
|
43113
43268
|
this.logger.info("[atbash] active memory removed on chain", { pubkey: this.agentPubkeyHex });
|
|
43269
|
+
onDiskIsCurrent = false;
|
|
43114
43270
|
}
|
|
43115
43271
|
}
|
|
43116
|
-
|
|
43117
|
-
|
|
43272
|
+
if (onDiskIsCurrent) {
|
|
43273
|
+
await this.pointerStore.set(this.agentPubkeyHex, result.pointer);
|
|
43274
|
+
} else {
|
|
43275
|
+
this.logger.warn(
|
|
43276
|
+
"[atbash] not advancing memory pointer \u2014 local file is stale or revoked; reads stay unaudited until it is refreshed"
|
|
43277
|
+
);
|
|
43278
|
+
}
|
|
43279
|
+
if (!onDiskIsCurrent) return null;
|
|
43280
|
+
if (!this.vouchesForTarget(event, ctx)) return null;
|
|
43281
|
+
return { allow: true, audited: true };
|
|
43118
43282
|
}
|
|
43119
43283
|
async writeMemoryAtomic(content) {
|
|
43120
|
-
await fs3.mkdir(
|
|
43284
|
+
await fs3.mkdir(path6.dirname(this.memoryFilePath), { recursive: true });
|
|
43121
43285
|
const tmp = `${this.memoryFilePath}.${process.pid}.tmp`;
|
|
43122
43286
|
await fs3.writeFile(tmp, content, "utf8");
|
|
43123
43287
|
await fs3.rename(tmp, this.memoryFilePath);
|