@atbash/sdk 0.16.5-dev.0 → 0.16.6-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.mts +23 -13
- package/dist/browser.mjs +25 -11
- package/dist/index.d.mts +23 -13
- package/dist/index.d.ts +23 -13
- package/dist/index.js +150 -70
- package/dist/index.mjs +150 -70
- package/package.json +7 -6
package/dist/browser.d.mts
CHANGED
|
@@ -1111,6 +1111,8 @@ interface GuardMemoryWriteInput extends ClassifyMemoryWriteOptions {
|
|
|
1111
1111
|
verifyPubKey?: string;
|
|
1112
1112
|
/** Org name for chain resolution (public vs org-private chain). */
|
|
1113
1113
|
orgName?: string;
|
|
1114
|
+
/** Explicit chain target when the host already resolved and verified it. */
|
|
1115
|
+
chainOpts?: ChainOpts;
|
|
1114
1116
|
/** LLM confidence threshold for yellow escalation. Default 0.6. */
|
|
1115
1117
|
threshold?: number;
|
|
1116
1118
|
/** When false, red verdicts log but don't block. Default true. */
|
|
@@ -1133,7 +1135,7 @@ interface GuardMemoryWriteResult {
|
|
|
1133
1135
|
decision?: GuardMemoryDecision;
|
|
1134
1136
|
/** Present when handled and scan ran. Absent when scan threw. */
|
|
1135
1137
|
scanResult?: MemoryScanResult;
|
|
1136
|
-
/** Present when handled. True
|
|
1138
|
+
/** Present when handled. True only after the chain transaction confirms. */
|
|
1137
1139
|
committed?: boolean;
|
|
1138
1140
|
}
|
|
1139
1141
|
/**
|
|
@@ -1143,12 +1145,13 @@ interface GuardMemoryWriteResult {
|
|
|
1143
1145
|
*
|
|
1144
1146
|
* Verdict handling:
|
|
1145
1147
|
* - `red` → not persisted to chain; decision blocks (unless `enforce: false`)
|
|
1146
|
-
* - `yellow` → persisted with LLM score; decision allows
|
|
1147
|
-
* - `green` → persisted with LLM score; decision allows
|
|
1148
|
+
* - `yellow` → persisted with its LLM score; decision allows once the chain confirms
|
|
1149
|
+
* - `green` → persisted with its LLM score; decision allows once the chain confirms
|
|
1150
|
+
* - anything else → not persisted; decision blocks (unless `enforce: false`)
|
|
1148
1151
|
*
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
+
* A write is acknowledged only after the chain transaction confirms, for
|
|
1153
|
+
* yellow exactly as for green. An unconfirmed write blocks in enforce mode
|
|
1154
|
+
* and remains unaudited in observe mode.
|
|
1152
1155
|
*/
|
|
1153
1156
|
declare function guardMemoryWrite(input: GuardMemoryWriteInput): Promise<GuardMemoryWriteResult>;
|
|
1154
1157
|
|
|
@@ -1178,6 +1181,8 @@ interface SyncMemoryOptions {
|
|
|
1178
1181
|
/** Skip the TTL gate and force a chain check this call. Default false. */
|
|
1179
1182
|
force?: boolean;
|
|
1180
1183
|
chainOpts?: ChainOpts;
|
|
1184
|
+
/** Exact chain slot for the managed file; omission keeps the cross-file API behavior. */
|
|
1185
|
+
filePath?: string;
|
|
1181
1186
|
}
|
|
1182
1187
|
/**
|
|
1183
1188
|
* `drifted: false` — pointer is still valid; caller can keep serving the local copy.
|
|
@@ -1226,6 +1231,8 @@ declare function defaultPointerPath(workspaceDir?: string): string;
|
|
|
1226
1231
|
interface WrappedLogger {
|
|
1227
1232
|
info(message: string, meta?: Record<string, unknown>): void;
|
|
1228
1233
|
warn(message: string, meta?: Record<string, unknown>): void;
|
|
1234
|
+
/** Wait for queued writes and surface any lost log entry. */
|
|
1235
|
+
flush(): Promise<void>;
|
|
1229
1236
|
}
|
|
1230
1237
|
interface UpstreamLogger {
|
|
1231
1238
|
info: (...args: unknown[]) => void;
|
|
@@ -1285,10 +1292,10 @@ interface HookDecision {
|
|
|
1285
1292
|
* failure, a rolled-back version), and a host must never re-judge its way past
|
|
1286
1293
|
* one.
|
|
1287
1294
|
*
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1295
|
+
* Absent or false means the guard reached no decision — for example, the scan
|
|
1296
|
+
* never ran or this manager does not own the requested file. In enforce mode,
|
|
1297
|
+
* failed chain verification of the managed file is a block, never an unaudited
|
|
1298
|
+
* allow. In observe mode unverified calls remain unaudited.
|
|
1292
1299
|
*
|
|
1293
1300
|
* So the host rule is:
|
|
1294
1301
|
* `if (d.block) deny; else if (d.audited) allow; else judge it yourself;`
|
|
@@ -1388,9 +1395,9 @@ declare class MemoryGuardManager {
|
|
|
1388
1395
|
runBootProbe(): Promise<void>;
|
|
1389
1396
|
/**
|
|
1390
1397
|
* Returns a `HookDecision` when the guard reached a decision about this event.
|
|
1391
|
-
* Returns `null`
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1398
|
+
* Returns `null` for non-memory events, other files this manager does not
|
|
1399
|
+
* own, or unverified observe-mode reads. An enforced read of the managed
|
|
1400
|
+
* file blocks when verification cannot complete.
|
|
1394
1401
|
*
|
|
1395
1402
|
* A returned decision carries `audited` (see `HookDecision`). Only
|
|
1396
1403
|
* `{ allow: true, audited: true }` means "checked and cleared"; anything else
|
|
@@ -1415,6 +1422,9 @@ declare class MemoryGuardManager {
|
|
|
1415
1422
|
*/
|
|
1416
1423
|
private vouchesForTarget;
|
|
1417
1424
|
private handleMemoryRead;
|
|
1425
|
+
/** Complete queued audit-log writes before a host shuts down or removes its workspace. */
|
|
1426
|
+
flushLogs(): Promise<void>;
|
|
1427
|
+
private unverifiedRead;
|
|
1418
1428
|
private writeMemoryAtomic;
|
|
1419
1429
|
}
|
|
1420
1430
|
declare function createMemoryGuardManager(opts: MemoryGuardManagerOptions): MemoryGuardManager;
|
package/dist/browser.mjs
CHANGED
|
@@ -44360,6 +44360,13 @@ function buildAllowedJudgeHosts() {
|
|
|
44360
44360
|
]);
|
|
44361
44361
|
}
|
|
44362
44362
|
var ALLOWED_JUDGE_HOSTS = buildAllowedJudgeHosts();
|
|
44363
|
+
function isAllowlistedJudgeHost(endpoint) {
|
|
44364
|
+
try {
|
|
44365
|
+
return ALLOWED_JUDGE_HOSTS.has(new URL(endpoint).hostname.toLowerCase());
|
|
44366
|
+
} catch {
|
|
44367
|
+
return false;
|
|
44368
|
+
}
|
|
44369
|
+
}
|
|
44363
44370
|
function validateJudgeEndpoint(judge) {
|
|
44364
44371
|
const policy = judge?.policy === "self-hosted" ? "self-hosted" : "default";
|
|
44365
44372
|
const candidate = judge?.endpoint?.trim() || DEFAULT_ENDPOINT;
|
|
@@ -44796,16 +44803,23 @@ var Atbash = class _Atbash {
|
|
|
44796
44803
|
verifying: this.verifyPubKey ? "with response-signature pubkey configured" : "without signature verification"
|
|
44797
44804
|
});
|
|
44798
44805
|
}
|
|
44799
|
-
|
|
44800
|
-
|
|
44801
|
-
|
|
44802
|
-
|
|
44803
|
-
|
|
44804
|
-
|
|
44805
|
-
|
|
44806
|
-
|
|
44807
|
-
|
|
44808
|
-
|
|
44806
|
+
if (isAllowlistedJudgeHost(this.endpoint)) {
|
|
44807
|
+
try {
|
|
44808
|
+
setupTelemetry({
|
|
44809
|
+
enabled: true,
|
|
44810
|
+
endpoint: this.endpoint,
|
|
44811
|
+
getAuthHeaders: () => this.authHeaders()
|
|
44812
|
+
});
|
|
44813
|
+
} catch (err) {
|
|
44814
|
+
this.logger.warn?.(
|
|
44815
|
+
"[atbash] telemetry setup failed \u2014 continuing without metrics",
|
|
44816
|
+
{ error: String(err) }
|
|
44817
|
+
);
|
|
44818
|
+
}
|
|
44819
|
+
} else {
|
|
44820
|
+
this.logger.info?.(
|
|
44821
|
+
"[atbash] telemetry disabled \u2014 the agent bearer is only sent to hosts that already receive it, and this judge endpoint is not one",
|
|
44822
|
+
{ endpoint: this.endpoint }
|
|
44809
44823
|
);
|
|
44810
44824
|
}
|
|
44811
44825
|
}
|
|
@@ -46124,7 +46138,7 @@ async function syncLocalMemory(auth, pointer, opts = {}) {
|
|
|
46124
46138
|
if (withinTtl) {
|
|
46125
46139
|
return { drifted: false, checked: false, pointer };
|
|
46126
46140
|
}
|
|
46127
|
-
const currentId = await getActiveMemoryId(auth, opts.chainOpts);
|
|
46141
|
+
const currentId = await getActiveMemoryId(auth, opts.chainOpts, opts.filePath);
|
|
46128
46142
|
const nextPointer = { activeId: currentId, checkedAt: now };
|
|
46129
46143
|
if (currentId === pointer.activeId) {
|
|
46130
46144
|
return { drifted: false, checked: true, pointer: nextPointer };
|
package/dist/index.d.mts
CHANGED
|
@@ -1111,6 +1111,8 @@ interface GuardMemoryWriteInput extends ClassifyMemoryWriteOptions {
|
|
|
1111
1111
|
verifyPubKey?: string;
|
|
1112
1112
|
/** Org name for chain resolution (public vs org-private chain). */
|
|
1113
1113
|
orgName?: string;
|
|
1114
|
+
/** Explicit chain target when the host already resolved and verified it. */
|
|
1115
|
+
chainOpts?: ChainOpts;
|
|
1114
1116
|
/** LLM confidence threshold for yellow escalation. Default 0.6. */
|
|
1115
1117
|
threshold?: number;
|
|
1116
1118
|
/** When false, red verdicts log but don't block. Default true. */
|
|
@@ -1133,7 +1135,7 @@ interface GuardMemoryWriteResult {
|
|
|
1133
1135
|
decision?: GuardMemoryDecision;
|
|
1134
1136
|
/** Present when handled and scan ran. Absent when scan threw. */
|
|
1135
1137
|
scanResult?: MemoryScanResult;
|
|
1136
|
-
/** Present when handled. True
|
|
1138
|
+
/** Present when handled. True only after the chain transaction confirms. */
|
|
1137
1139
|
committed?: boolean;
|
|
1138
1140
|
}
|
|
1139
1141
|
/**
|
|
@@ -1143,12 +1145,13 @@ interface GuardMemoryWriteResult {
|
|
|
1143
1145
|
*
|
|
1144
1146
|
* Verdict handling:
|
|
1145
1147
|
* - `red` → not persisted to chain; decision blocks (unless `enforce: false`)
|
|
1146
|
-
* - `yellow` → persisted with LLM score; decision allows
|
|
1147
|
-
* - `green` → persisted with LLM score; decision allows
|
|
1148
|
+
* - `yellow` → persisted with its LLM score; decision allows once the chain confirms
|
|
1149
|
+
* - `green` → persisted with its LLM score; decision allows once the chain confirms
|
|
1150
|
+
* - anything else → not persisted; decision blocks (unless `enforce: false`)
|
|
1148
1151
|
*
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
+
* A write is acknowledged only after the chain transaction confirms, for
|
|
1153
|
+
* yellow exactly as for green. An unconfirmed write blocks in enforce mode
|
|
1154
|
+
* and remains unaudited in observe mode.
|
|
1152
1155
|
*/
|
|
1153
1156
|
declare function guardMemoryWrite(input: GuardMemoryWriteInput): Promise<GuardMemoryWriteResult>;
|
|
1154
1157
|
|
|
@@ -1178,6 +1181,8 @@ interface SyncMemoryOptions {
|
|
|
1178
1181
|
/** Skip the TTL gate and force a chain check this call. Default false. */
|
|
1179
1182
|
force?: boolean;
|
|
1180
1183
|
chainOpts?: ChainOpts;
|
|
1184
|
+
/** Exact chain slot for the managed file; omission keeps the cross-file API behavior. */
|
|
1185
|
+
filePath?: string;
|
|
1181
1186
|
}
|
|
1182
1187
|
/**
|
|
1183
1188
|
* `drifted: false` — pointer is still valid; caller can keep serving the local copy.
|
|
@@ -1226,6 +1231,8 @@ declare function defaultPointerPath(workspaceDir?: string): string;
|
|
|
1226
1231
|
interface WrappedLogger {
|
|
1227
1232
|
info(message: string, meta?: Record<string, unknown>): void;
|
|
1228
1233
|
warn(message: string, meta?: Record<string, unknown>): void;
|
|
1234
|
+
/** Wait for queued writes and surface any lost log entry. */
|
|
1235
|
+
flush(): Promise<void>;
|
|
1229
1236
|
}
|
|
1230
1237
|
interface UpstreamLogger {
|
|
1231
1238
|
info: (...args: unknown[]) => void;
|
|
@@ -1285,10 +1292,10 @@ interface HookDecision {
|
|
|
1285
1292
|
* failure, a rolled-back version), and a host must never re-judge its way past
|
|
1286
1293
|
* one.
|
|
1287
1294
|
*
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1295
|
+
* Absent or false means the guard reached no decision — for example, the scan
|
|
1296
|
+
* never ran or this manager does not own the requested file. In enforce mode,
|
|
1297
|
+
* failed chain verification of the managed file is a block, never an unaudited
|
|
1298
|
+
* allow. In observe mode unverified calls remain unaudited.
|
|
1292
1299
|
*
|
|
1293
1300
|
* So the host rule is:
|
|
1294
1301
|
* `if (d.block) deny; else if (d.audited) allow; else judge it yourself;`
|
|
@@ -1388,9 +1395,9 @@ declare class MemoryGuardManager {
|
|
|
1388
1395
|
runBootProbe(): Promise<void>;
|
|
1389
1396
|
/**
|
|
1390
1397
|
* Returns a `HookDecision` when the guard reached a decision about this event.
|
|
1391
|
-
* Returns `null`
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1398
|
+
* Returns `null` for non-memory events, other files this manager does not
|
|
1399
|
+
* own, or unverified observe-mode reads. An enforced read of the managed
|
|
1400
|
+
* file blocks when verification cannot complete.
|
|
1394
1401
|
*
|
|
1395
1402
|
* A returned decision carries `audited` (see `HookDecision`). Only
|
|
1396
1403
|
* `{ allow: true, audited: true }` means "checked and cleared"; anything else
|
|
@@ -1415,6 +1422,9 @@ declare class MemoryGuardManager {
|
|
|
1415
1422
|
*/
|
|
1416
1423
|
private vouchesForTarget;
|
|
1417
1424
|
private handleMemoryRead;
|
|
1425
|
+
/** Complete queued audit-log writes before a host shuts down or removes its workspace. */
|
|
1426
|
+
flushLogs(): Promise<void>;
|
|
1427
|
+
private unverifiedRead;
|
|
1418
1428
|
private writeMemoryAtomic;
|
|
1419
1429
|
}
|
|
1420
1430
|
declare function createMemoryGuardManager(opts: MemoryGuardManagerOptions): MemoryGuardManager;
|
package/dist/index.d.ts
CHANGED
|
@@ -1111,6 +1111,8 @@ interface GuardMemoryWriteInput extends ClassifyMemoryWriteOptions {
|
|
|
1111
1111
|
verifyPubKey?: string;
|
|
1112
1112
|
/** Org name for chain resolution (public vs org-private chain). */
|
|
1113
1113
|
orgName?: string;
|
|
1114
|
+
/** Explicit chain target when the host already resolved and verified it. */
|
|
1115
|
+
chainOpts?: ChainOpts;
|
|
1114
1116
|
/** LLM confidence threshold for yellow escalation. Default 0.6. */
|
|
1115
1117
|
threshold?: number;
|
|
1116
1118
|
/** When false, red verdicts log but don't block. Default true. */
|
|
@@ -1133,7 +1135,7 @@ interface GuardMemoryWriteResult {
|
|
|
1133
1135
|
decision?: GuardMemoryDecision;
|
|
1134
1136
|
/** Present when handled and scan ran. Absent when scan threw. */
|
|
1135
1137
|
scanResult?: MemoryScanResult;
|
|
1136
|
-
/** Present when handled. True
|
|
1138
|
+
/** Present when handled. True only after the chain transaction confirms. */
|
|
1137
1139
|
committed?: boolean;
|
|
1138
1140
|
}
|
|
1139
1141
|
/**
|
|
@@ -1143,12 +1145,13 @@ interface GuardMemoryWriteResult {
|
|
|
1143
1145
|
*
|
|
1144
1146
|
* Verdict handling:
|
|
1145
1147
|
* - `red` → not persisted to chain; decision blocks (unless `enforce: false`)
|
|
1146
|
-
* - `yellow` → persisted with LLM score; decision allows
|
|
1147
|
-
* - `green` → persisted with LLM score; decision allows
|
|
1148
|
+
* - `yellow` → persisted with its LLM score; decision allows once the chain confirms
|
|
1149
|
+
* - `green` → persisted with its LLM score; decision allows once the chain confirms
|
|
1150
|
+
* - anything else → not persisted; decision blocks (unless `enforce: false`)
|
|
1148
1151
|
*
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
+
* A write is acknowledged only after the chain transaction confirms, for
|
|
1153
|
+
* yellow exactly as for green. An unconfirmed write blocks in enforce mode
|
|
1154
|
+
* and remains unaudited in observe mode.
|
|
1152
1155
|
*/
|
|
1153
1156
|
declare function guardMemoryWrite(input: GuardMemoryWriteInput): Promise<GuardMemoryWriteResult>;
|
|
1154
1157
|
|
|
@@ -1178,6 +1181,8 @@ interface SyncMemoryOptions {
|
|
|
1178
1181
|
/** Skip the TTL gate and force a chain check this call. Default false. */
|
|
1179
1182
|
force?: boolean;
|
|
1180
1183
|
chainOpts?: ChainOpts;
|
|
1184
|
+
/** Exact chain slot for the managed file; omission keeps the cross-file API behavior. */
|
|
1185
|
+
filePath?: string;
|
|
1181
1186
|
}
|
|
1182
1187
|
/**
|
|
1183
1188
|
* `drifted: false` — pointer is still valid; caller can keep serving the local copy.
|
|
@@ -1226,6 +1231,8 @@ declare function defaultPointerPath(workspaceDir?: string): string;
|
|
|
1226
1231
|
interface WrappedLogger {
|
|
1227
1232
|
info(message: string, meta?: Record<string, unknown>): void;
|
|
1228
1233
|
warn(message: string, meta?: Record<string, unknown>): void;
|
|
1234
|
+
/** Wait for queued writes and surface any lost log entry. */
|
|
1235
|
+
flush(): Promise<void>;
|
|
1229
1236
|
}
|
|
1230
1237
|
interface UpstreamLogger {
|
|
1231
1238
|
info: (...args: unknown[]) => void;
|
|
@@ -1285,10 +1292,10 @@ interface HookDecision {
|
|
|
1285
1292
|
* failure, a rolled-back version), and a host must never re-judge its way past
|
|
1286
1293
|
* one.
|
|
1287
1294
|
*
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1295
|
+
* Absent or false means the guard reached no decision — for example, the scan
|
|
1296
|
+
* never ran or this manager does not own the requested file. In enforce mode,
|
|
1297
|
+
* failed chain verification of the managed file is a block, never an unaudited
|
|
1298
|
+
* allow. In observe mode unverified calls remain unaudited.
|
|
1292
1299
|
*
|
|
1293
1300
|
* So the host rule is:
|
|
1294
1301
|
* `if (d.block) deny; else if (d.audited) allow; else judge it yourself;`
|
|
@@ -1388,9 +1395,9 @@ declare class MemoryGuardManager {
|
|
|
1388
1395
|
runBootProbe(): Promise<void>;
|
|
1389
1396
|
/**
|
|
1390
1397
|
* Returns a `HookDecision` when the guard reached a decision about this event.
|
|
1391
|
-
* Returns `null`
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1398
|
+
* Returns `null` for non-memory events, other files this manager does not
|
|
1399
|
+
* own, or unverified observe-mode reads. An enforced read of the managed
|
|
1400
|
+
* file blocks when verification cannot complete.
|
|
1394
1401
|
*
|
|
1395
1402
|
* A returned decision carries `audited` (see `HookDecision`). Only
|
|
1396
1403
|
* `{ allow: true, audited: true }` means "checked and cleared"; anything else
|
|
@@ -1415,6 +1422,9 @@ declare class MemoryGuardManager {
|
|
|
1415
1422
|
*/
|
|
1416
1423
|
private vouchesForTarget;
|
|
1417
1424
|
private handleMemoryRead;
|
|
1425
|
+
/** Complete queued audit-log writes before a host shuts down or removes its workspace. */
|
|
1426
|
+
flushLogs(): Promise<void>;
|
|
1427
|
+
private unverifiedRead;
|
|
1418
1428
|
private writeMemoryAtomic;
|
|
1419
1429
|
}
|
|
1420
1430
|
declare function createMemoryGuardManager(opts: MemoryGuardManagerOptions): MemoryGuardManager;
|
package/dist/index.js
CHANGED
|
@@ -3026,6 +3026,13 @@ function buildAllowedJudgeHosts() {
|
|
|
3026
3026
|
]);
|
|
3027
3027
|
}
|
|
3028
3028
|
var ALLOWED_JUDGE_HOSTS = buildAllowedJudgeHosts();
|
|
3029
|
+
function isAllowlistedJudgeHost(endpoint) {
|
|
3030
|
+
try {
|
|
3031
|
+
return ALLOWED_JUDGE_HOSTS.has(new URL(endpoint).hostname.toLowerCase());
|
|
3032
|
+
} catch {
|
|
3033
|
+
return false;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3029
3036
|
function validateJudgeEndpoint(judge) {
|
|
3030
3037
|
const policy = judge?.policy === "self-hosted" ? "self-hosted" : "default";
|
|
3031
3038
|
const candidate = judge?.endpoint?.trim() || DEFAULT_ENDPOINT;
|
|
@@ -3654,16 +3661,23 @@ var Atbash = class _Atbash {
|
|
|
3654
3661
|
verifying: this.verifyPubKey ? "with response-signature pubkey configured" : "without signature verification"
|
|
3655
3662
|
});
|
|
3656
3663
|
}
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3664
|
+
if (isAllowlistedJudgeHost(this.endpoint)) {
|
|
3665
|
+
try {
|
|
3666
|
+
setupTelemetry({
|
|
3667
|
+
enabled: true,
|
|
3668
|
+
endpoint: this.endpoint,
|
|
3669
|
+
getAuthHeaders: () => this.authHeaders()
|
|
3670
|
+
});
|
|
3671
|
+
} catch (err) {
|
|
3672
|
+
this.logger.warn?.(
|
|
3673
|
+
"[atbash] telemetry setup failed \u2014 continuing without metrics",
|
|
3674
|
+
{ error: String(err) }
|
|
3675
|
+
);
|
|
3676
|
+
}
|
|
3677
|
+
} else {
|
|
3678
|
+
this.logger.info?.(
|
|
3679
|
+
"[atbash] telemetry disabled \u2014 the agent bearer is only sent to hosts that already receive it, and this judge endpoint is not one",
|
|
3680
|
+
{ endpoint: this.endpoint }
|
|
3667
3681
|
);
|
|
3668
3682
|
}
|
|
3669
3683
|
}
|
|
@@ -43061,7 +43075,7 @@ async function commitMemoryVersion(plaintext, auth, opts) {
|
|
|
43061
43075
|
const chainOpts = await resolveChainOptsForOrg(opts, auth);
|
|
43062
43076
|
const client = await buildChainClient(chainOpts);
|
|
43063
43077
|
const { sigProvider } = buildSigner(auth);
|
|
43064
|
-
await client.signAndSendUniqueTransaction(
|
|
43078
|
+
const receipt = await client.signAndSendUniqueTransaction(
|
|
43065
43079
|
{
|
|
43066
43080
|
name: native.OP_ADD_AGENT_MEMORY,
|
|
43067
43081
|
args: [
|
|
@@ -43074,6 +43088,9 @@ async function commitMemoryVersion(plaintext, auth, opts) {
|
|
|
43074
43088
|
},
|
|
43075
43089
|
sigProvider
|
|
43076
43090
|
);
|
|
43091
|
+
if (receipt.status !== index.ResponseStatus.Confirmed) {
|
|
43092
|
+
throw new Error(`Memory transaction was not confirmed (status=${receipt.status})`);
|
|
43093
|
+
}
|
|
43077
43094
|
}
|
|
43078
43095
|
async function decryptRow(row, key3) {
|
|
43079
43096
|
const parsed = native.parseMemoryRow(gtvToFfiSafe(row));
|
|
@@ -43183,13 +43200,16 @@ async function rollbackMemory(toId, reason, auth, opts) {
|
|
|
43183
43200
|
const chainOpts = await resolveChainOptsForOrg(opts, auth);
|
|
43184
43201
|
const client = await buildChainClient(chainOpts);
|
|
43185
43202
|
const { sigProvider } = buildSigner(auth);
|
|
43186
|
-
await client.signAndSendUniqueTransaction(
|
|
43203
|
+
const receipt = await client.signAndSendUniqueTransaction(
|
|
43187
43204
|
{
|
|
43188
43205
|
name: native.OP_ROLLBACK_AGENT_MEMORY,
|
|
43189
43206
|
args: [toGtxBytes(args.agent_pubkey), args.to_id, args.reason]
|
|
43190
43207
|
},
|
|
43191
43208
|
sigProvider
|
|
43192
43209
|
);
|
|
43210
|
+
if (receipt.status !== index.ResponseStatus.Confirmed) {
|
|
43211
|
+
throw new Error(`Memory rollback was not confirmed (status=${receipt.status})`);
|
|
43212
|
+
}
|
|
43193
43213
|
}
|
|
43194
43214
|
|
|
43195
43215
|
// src-ts/memory/_json_safe.ts
|
|
@@ -43328,6 +43348,7 @@ async function guardMemoryWrite(input) {
|
|
|
43328
43348
|
endpoint,
|
|
43329
43349
|
verifyPubKey,
|
|
43330
43350
|
orgName,
|
|
43351
|
+
chainOpts,
|
|
43331
43352
|
threshold,
|
|
43332
43353
|
patterns,
|
|
43333
43354
|
toolNames,
|
|
@@ -43384,23 +43405,41 @@ async function guardMemoryWrite(input) {
|
|
|
43384
43405
|
committed: false
|
|
43385
43406
|
};
|
|
43386
43407
|
}
|
|
43408
|
+
if (scanResult.verdict !== "green" && scanResult.verdict !== "yellow") {
|
|
43409
|
+
const reason = "Memory scan returned an unrecognized verdict.";
|
|
43410
|
+
return {
|
|
43411
|
+
handled: true,
|
|
43412
|
+
decision: enforce ? { allow: false, block: true, reason } : { allow: true, reason },
|
|
43413
|
+
scanResult,
|
|
43414
|
+
committed: false
|
|
43415
|
+
};
|
|
43416
|
+
}
|
|
43387
43417
|
const filePath = import_node_path4.default.basename(memEntry.key);
|
|
43388
|
-
|
|
43389
|
-
|
|
43390
|
-
|
|
43391
|
-
|
|
43392
|
-
|
|
43393
|
-
|
|
43394
|
-
|
|
43418
|
+
try {
|
|
43419
|
+
await commitMemoryVersion(memEntry.value, auth, {
|
|
43420
|
+
score: scanResult.score,
|
|
43421
|
+
filePath,
|
|
43422
|
+
orgName,
|
|
43423
|
+
endpoint,
|
|
43424
|
+
verifyPubKey,
|
|
43425
|
+
chainOpts
|
|
43426
|
+
});
|
|
43427
|
+
} catch (err) {
|
|
43395
43428
|
const reason = err instanceof Error ? err.message : String(err);
|
|
43396
43429
|
logger2?.warn?.("[atbash] memory commit to chain failed", {
|
|
43397
43430
|
path: memEntry.key,
|
|
43398
43431
|
filePath,
|
|
43399
43432
|
reason
|
|
43400
43433
|
});
|
|
43401
|
-
|
|
43434
|
+
return {
|
|
43435
|
+
handled: true,
|
|
43436
|
+
decision: enforce ? { allow: false, block: true, reason: `Memory commit failed: ${reason}` } : { allow: true, reason: `Memory commit failed: ${reason}` },
|
|
43437
|
+
scanResult,
|
|
43438
|
+
committed: false
|
|
43439
|
+
};
|
|
43440
|
+
}
|
|
43402
43441
|
logger2?.info?.(
|
|
43403
|
-
scanResult.verdict === "yellow" ? "[atbash] memory
|
|
43442
|
+
scanResult.verdict === "yellow" ? "[atbash] memory ALLOW (yellow, low score)" : "[atbash] memory ALLOW",
|
|
43404
43443
|
{
|
|
43405
43444
|
path: memEntry.key,
|
|
43406
43445
|
filePath,
|
|
@@ -43433,7 +43472,7 @@ async function syncLocalMemory(auth, pointer, opts = {}) {
|
|
|
43433
43472
|
if (withinTtl) {
|
|
43434
43473
|
return { drifted: false, checked: false, pointer };
|
|
43435
43474
|
}
|
|
43436
|
-
const currentId = await getActiveMemoryId(auth, opts.chainOpts);
|
|
43475
|
+
const currentId = await getActiveMemoryId(auth, opts.chainOpts, opts.filePath);
|
|
43437
43476
|
const nextPointer = { activeId: currentId, checkedAt: now };
|
|
43438
43477
|
if (currentId === pointer.activeId) {
|
|
43439
43478
|
return { drifted: false, checked: true, pointer: nextPointer };
|
|
@@ -43515,13 +43554,16 @@ function formatMeta(meta) {
|
|
|
43515
43554
|
}
|
|
43516
43555
|
function createFileLogger(filePath, upstream) {
|
|
43517
43556
|
let queue = Promise.resolve();
|
|
43557
|
+
let writeError;
|
|
43518
43558
|
async function ensureDir() {
|
|
43519
43559
|
await import_node_fs5.promises.mkdir(import_node_path6.default.dirname(filePath), { recursive: true });
|
|
43520
43560
|
}
|
|
43521
43561
|
function append(level, message, meta) {
|
|
43522
43562
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${message}${formatMeta(meta)}
|
|
43523
43563
|
`;
|
|
43524
|
-
queue = queue.then(ensureDir).then(() => import_node_fs5.promises.appendFile(filePath, line, "utf8")).catch(() => {
|
|
43564
|
+
queue = queue.then(ensureDir).then(() => import_node_fs5.promises.appendFile(filePath, line, "utf8")).catch((error4) => {
|
|
43565
|
+
writeError = error4 instanceof Error ? error4 : new Error(String(error4));
|
|
43566
|
+
upstream?.warn("[atbash] plugin log write failed", { error: writeError.message });
|
|
43525
43567
|
});
|
|
43526
43568
|
}
|
|
43527
43569
|
return {
|
|
@@ -43532,6 +43574,10 @@ function createFileLogger(filePath, upstream) {
|
|
|
43532
43574
|
warn(message, meta) {
|
|
43533
43575
|
upstream?.warn(message, meta ?? {});
|
|
43534
43576
|
append("warn", message, meta);
|
|
43577
|
+
},
|
|
43578
|
+
async flush() {
|
|
43579
|
+
await queue;
|
|
43580
|
+
if (writeError) throw writeError;
|
|
43535
43581
|
}
|
|
43536
43582
|
};
|
|
43537
43583
|
}
|
|
@@ -43621,7 +43667,15 @@ var MemoryGuardManager = class {
|
|
|
43621
43667
|
orgName: this.opts.orgName
|
|
43622
43668
|
});
|
|
43623
43669
|
const resolved = await atbash.resolveChainForOrg(this.opts.orgName);
|
|
43624
|
-
|
|
43670
|
+
if (!await atbash.checkAgentExists(this.agentPubkeyHex, { network: resolved.network })) {
|
|
43671
|
+
throw new Error(`agent is not registered on the resolved ${resolved.network} chain`);
|
|
43672
|
+
}
|
|
43673
|
+
const chainOpts = {
|
|
43674
|
+
...this.opts.chainOpts,
|
|
43675
|
+
network: resolved.network,
|
|
43676
|
+
blockchainRid: resolved.blockchainRid,
|
|
43677
|
+
nodeUrls: this.opts.chainOpts?.nodeUrls ?? resolved.nodeUrls
|
|
43678
|
+
};
|
|
43625
43679
|
this._resolvedChainOpts = chainOpts;
|
|
43626
43680
|
this.logger.info(
|
|
43627
43681
|
`[atbash] resolved org \u2192 chain \u2014 org=${this.opts.orgName} network=${resolved.network}`
|
|
@@ -43630,9 +43684,9 @@ var MemoryGuardManager = class {
|
|
|
43630
43684
|
} catch (err) {
|
|
43631
43685
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43632
43686
|
this.logger.warn(
|
|
43633
|
-
`[atbash] org \u2192 chain resolution failed (
|
|
43687
|
+
`[atbash] org \u2192 chain resolution failed (memory read will be blocked): ${msg}`
|
|
43634
43688
|
);
|
|
43635
|
-
|
|
43689
|
+
throw err;
|
|
43636
43690
|
} finally {
|
|
43637
43691
|
this._resolveChainInflight = void 0;
|
|
43638
43692
|
}
|
|
@@ -43651,13 +43705,18 @@ var MemoryGuardManager = class {
|
|
|
43651
43705
|
const result = await syncLocalMemory(this.opts.auth, seed, {
|
|
43652
43706
|
ttlMs: 0,
|
|
43653
43707
|
force: true,
|
|
43654
|
-
chainOpts
|
|
43708
|
+
chainOpts,
|
|
43709
|
+
filePath: import_node_path7.default.basename(this.memoryFilePath)
|
|
43655
43710
|
});
|
|
43656
43711
|
if (!result.drifted && result.pointer.activeId == null) {
|
|
43657
43712
|
this.logger.info(
|
|
43658
43713
|
`[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.`
|
|
43659
43714
|
);
|
|
43660
43715
|
} else if (result.drifted && result.current) {
|
|
43716
|
+
if (result.current.filePath !== import_node_path7.default.basename(this.memoryFilePath) || result.current.isActive !== true || result.current.decryptError) {
|
|
43717
|
+
this.logger.warn("[atbash] boot sync REFUSED mismatched memory version");
|
|
43718
|
+
return;
|
|
43719
|
+
}
|
|
43661
43720
|
if (result.current.score < this.rollbackMinScore) {
|
|
43662
43721
|
this.logger.warn(
|
|
43663
43722
|
`[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.`
|
|
@@ -43671,7 +43730,8 @@ var MemoryGuardManager = class {
|
|
|
43671
43730
|
await this.writeMemoryAtomic(result.current.content);
|
|
43672
43731
|
} catch (err) {
|
|
43673
43732
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43674
|
-
this.logger.warn("[atbash] boot sync write failed
|
|
43733
|
+
this.logger.warn("[atbash] boot sync write failed; pointer remains unchanged", { error: msg });
|
|
43734
|
+
return;
|
|
43675
43735
|
}
|
|
43676
43736
|
}
|
|
43677
43737
|
await this.pointerStore.set(this.agentPubkeyHex, result.pointer);
|
|
@@ -43685,9 +43745,9 @@ var MemoryGuardManager = class {
|
|
|
43685
43745
|
}
|
|
43686
43746
|
/**
|
|
43687
43747
|
* Returns a `HookDecision` when the guard reached a decision about this event.
|
|
43688
|
-
* Returns `null`
|
|
43689
|
-
*
|
|
43690
|
-
*
|
|
43748
|
+
* Returns `null` for non-memory events, other files this manager does not
|
|
43749
|
+
* own, or unverified observe-mode reads. An enforced read of the managed
|
|
43750
|
+
* file blocks when verification cannot complete.
|
|
43691
43751
|
*
|
|
43692
43752
|
* A returned decision carries `audited` (see `HookDecision`). Only
|
|
43693
43753
|
* `{ allow: true, audited: true }` means "checked and cleared"; anything else
|
|
@@ -43723,6 +43783,7 @@ var MemoryGuardManager = class {
|
|
|
43723
43783
|
endpoint: this.opts.judgeEndpoint,
|
|
43724
43784
|
verifyPubKey: this.opts.judgeVerifyPubKey,
|
|
43725
43785
|
orgName: this.opts.orgName,
|
|
43786
|
+
chainOpts: this.opts.chainOpts,
|
|
43726
43787
|
patterns: this.opts.memoryPathPatterns,
|
|
43727
43788
|
toolNames: this.opts.memoryWriteToolNames,
|
|
43728
43789
|
enforce: this.enforce,
|
|
@@ -43767,6 +43828,14 @@ var MemoryGuardManager = class {
|
|
|
43767
43828
|
reason: `memory scan returned ${sr2.verdict} but this guard is not enforcing it`
|
|
43768
43829
|
};
|
|
43769
43830
|
}
|
|
43831
|
+
if (guard.committed !== true) {
|
|
43832
|
+
return {
|
|
43833
|
+
allow: true,
|
|
43834
|
+
audited: false,
|
|
43835
|
+
verdict: sr2.verdict,
|
|
43836
|
+
reason: "memory scan passed but chain commit did not confirm"
|
|
43837
|
+
};
|
|
43838
|
+
}
|
|
43770
43839
|
return { allow: true, audited: true, verdict: sr2.verdict };
|
|
43771
43840
|
}
|
|
43772
43841
|
/**
|
|
@@ -43801,62 +43870,73 @@ var MemoryGuardManager = class {
|
|
|
43801
43870
|
return found.every((p) => import_node_path7.default.resolve(p) === managed);
|
|
43802
43871
|
}
|
|
43803
43872
|
async handleMemoryRead(event, ctx) {
|
|
43873
|
+
if (!this.vouchesForTarget(event, ctx)) return null;
|
|
43804
43874
|
const pointer = await this.pointerStore.get(this.agentPubkeyHex);
|
|
43805
43875
|
let result;
|
|
43876
|
+
let chainOpts;
|
|
43806
43877
|
try {
|
|
43807
|
-
|
|
43878
|
+
chainOpts = await this.resolveChainOpts();
|
|
43808
43879
|
result = await syncLocalMemory(this.opts.auth, pointer, {
|
|
43809
43880
|
ttlMs: this.ttlMs,
|
|
43810
|
-
|
|
43881
|
+
force: this.enforce,
|
|
43882
|
+
chainOpts,
|
|
43883
|
+
filePath: import_node_path7.default.basename(this.memoryFilePath)
|
|
43811
43884
|
});
|
|
43812
43885
|
} catch (err) {
|
|
43813
43886
|
if (err instanceof MemoryIntegrityError) {
|
|
43814
43887
|
const reason = `Memory integrity check failed on id ${err.id} \u2014 read blocked.`;
|
|
43815
43888
|
this.logger.warn("[atbash] MEMORY INTEGRITY FAILURE", { id: err.id, error: err.message });
|
|
43816
|
-
|
|
43817
|
-
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43889
|
+
return this.unverifiedRead(reason);
|
|
43818
43890
|
}
|
|
43819
43891
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43820
|
-
this.logger.warn("[atbash] memory sync failed
|
|
43821
|
-
return
|
|
43892
|
+
this.logger.warn("[atbash] memory sync failed", { error: msg });
|
|
43893
|
+
return this.unverifiedRead(`Memory chain sync failed: ${msg}`);
|
|
43822
43894
|
}
|
|
43823
|
-
|
|
43824
|
-
|
|
43825
|
-
|
|
43826
|
-
|
|
43827
|
-
if (fresh.score < this.rollbackMinScore) {
|
|
43828
|
-
const reason = `Rolled-back memory version #${fresh.id} scored ${fresh.score} (below threshold ${this.rollbackMinScore}) \u2014 read blocked.`;
|
|
43829
|
-
this.logger.warn("[atbash] blocking read on low-score rollback", { id: fresh.id, score: fresh.score });
|
|
43830
|
-
if (!this.enforce) return null;
|
|
43831
|
-
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43832
|
-
}
|
|
43833
|
-
this.logger.info("[atbash] memory drift detected \u2014 refreshing local file", {
|
|
43834
|
-
id: fresh.id,
|
|
43835
|
-
score: fresh.score
|
|
43836
|
-
});
|
|
43837
|
-
try {
|
|
43838
|
-
await this.writeMemoryAtomic(fresh.content);
|
|
43839
|
-
} catch (err) {
|
|
43840
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
43841
|
-
this.logger.warn("[atbash] failed to write refreshed memory (serving old)", { error: msg });
|
|
43842
|
-
onDiskIsCurrent = false;
|
|
43843
|
-
}
|
|
43844
|
-
} else {
|
|
43845
|
-
this.logger.info("[atbash] active memory removed on chain", { pubkey: this.agentPubkeyHex });
|
|
43846
|
-
onDiskIsCurrent = false;
|
|
43847
|
-
}
|
|
43895
|
+
if (!result.checked) return this.unverifiedRead("Memory chain state was not checked.");
|
|
43896
|
+
const activeId = result.pointer.activeId;
|
|
43897
|
+
if (activeId === null) {
|
|
43898
|
+
return this.unverifiedRead("No active chain version exists for the managed memory file.");
|
|
43848
43899
|
}
|
|
43849
|
-
|
|
43850
|
-
|
|
43851
|
-
|
|
43852
|
-
|
|
43853
|
-
|
|
43854
|
-
);
|
|
43900
|
+
let active;
|
|
43901
|
+
try {
|
|
43902
|
+
active = result.drifted ? result.current : await getMemoryById(activeId, this.opts.auth, chainOpts);
|
|
43903
|
+
} catch (err) {
|
|
43904
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43905
|
+
this.logger.warn("[atbash] active memory row could not be verified", { error: msg });
|
|
43906
|
+
return this.unverifiedRead(`Memory version could not be verified: ${msg}`);
|
|
43855
43907
|
}
|
|
43856
|
-
|
|
43857
|
-
if (!
|
|
43908
|
+
const filePath = import_node_path7.default.basename(this.memoryFilePath);
|
|
43909
|
+
if (!active || active.id !== activeId || active.filePath !== filePath || active.isActive !== true || active.decryptError) {
|
|
43910
|
+
return this.unverifiedRead("Active memory version does not match the managed file.");
|
|
43911
|
+
}
|
|
43912
|
+
if (active.score < this.rollbackMinScore) {
|
|
43913
|
+
return this.unverifiedRead(`Memory version #${active.id} is below the minimum safety score.`);
|
|
43914
|
+
}
|
|
43915
|
+
try {
|
|
43916
|
+
if (result.drifted) {
|
|
43917
|
+
this.logger.info("[atbash] memory drift detected \u2014 refreshing local file", { id: active.id });
|
|
43918
|
+
await this.writeMemoryAtomic(active.content);
|
|
43919
|
+
}
|
|
43920
|
+
const onDisk = await import_node_fs6.promises.readFile(this.memoryFilePath, "utf8");
|
|
43921
|
+
if (onDisk !== active.content) {
|
|
43922
|
+
return this.unverifiedRead("Local memory bytes do not match the active chain version.");
|
|
43923
|
+
}
|
|
43924
|
+
} catch (err) {
|
|
43925
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43926
|
+
this.logger.warn("[atbash] managed memory file could not be verified", { error: msg });
|
|
43927
|
+
return this.unverifiedRead(`Managed memory file could not be verified: ${msg}`);
|
|
43928
|
+
}
|
|
43929
|
+
await this.pointerStore.set(this.agentPubkeyHex, result.pointer);
|
|
43858
43930
|
return { allow: true, audited: true };
|
|
43859
43931
|
}
|
|
43932
|
+
/** Complete queued audit-log writes before a host shuts down or removes its workspace. */
|
|
43933
|
+
async flushLogs() {
|
|
43934
|
+
await this.logger.flush();
|
|
43935
|
+
}
|
|
43936
|
+
unverifiedRead(reason) {
|
|
43937
|
+
if (!this.enforce) return null;
|
|
43938
|
+
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43939
|
+
}
|
|
43860
43940
|
async writeMemoryAtomic(content) {
|
|
43861
43941
|
await import_node_fs6.promises.mkdir(import_node_path7.default.dirname(this.memoryFilePath), { recursive: true });
|
|
43862
43942
|
const tmp = `${this.memoryFilePath}.${process.pid}.tmp`;
|
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;
|
|
@@ -3570,16 +3577,23 @@ var Atbash = class _Atbash {
|
|
|
3570
3577
|
verifying: this.verifyPubKey ? "with response-signature pubkey configured" : "without signature verification"
|
|
3571
3578
|
});
|
|
3572
3579
|
}
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
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 }
|
|
3583
3597
|
);
|
|
3584
3598
|
}
|
|
3585
3599
|
}
|
|
@@ -42977,7 +42991,7 @@ async function commitMemoryVersion(plaintext, auth, opts) {
|
|
|
42977
42991
|
const chainOpts = await resolveChainOptsForOrg(opts, auth);
|
|
42978
42992
|
const client = await buildChainClient(chainOpts);
|
|
42979
42993
|
const { sigProvider } = buildSigner(auth);
|
|
42980
|
-
await client.signAndSendUniqueTransaction(
|
|
42994
|
+
const receipt = await client.signAndSendUniqueTransaction(
|
|
42981
42995
|
{
|
|
42982
42996
|
name: native.OP_ADD_AGENT_MEMORY,
|
|
42983
42997
|
args: [
|
|
@@ -42990,6 +43004,9 @@ async function commitMemoryVersion(plaintext, auth, opts) {
|
|
|
42990
43004
|
},
|
|
42991
43005
|
sigProvider
|
|
42992
43006
|
);
|
|
43007
|
+
if (receipt.status !== index.ResponseStatus.Confirmed) {
|
|
43008
|
+
throw new Error(`Memory transaction was not confirmed (status=${receipt.status})`);
|
|
43009
|
+
}
|
|
42993
43010
|
}
|
|
42994
43011
|
async function decryptRow(row, key3) {
|
|
42995
43012
|
const parsed = native.parseMemoryRow(gtvToFfiSafe(row));
|
|
@@ -43099,13 +43116,16 @@ async function rollbackMemory(toId, reason, auth, opts) {
|
|
|
43099
43116
|
const chainOpts = await resolveChainOptsForOrg(opts, auth);
|
|
43100
43117
|
const client = await buildChainClient(chainOpts);
|
|
43101
43118
|
const { sigProvider } = buildSigner(auth);
|
|
43102
|
-
await client.signAndSendUniqueTransaction(
|
|
43119
|
+
const receipt = await client.signAndSendUniqueTransaction(
|
|
43103
43120
|
{
|
|
43104
43121
|
name: native.OP_ROLLBACK_AGENT_MEMORY,
|
|
43105
43122
|
args: [toGtxBytes(args.agent_pubkey), args.to_id, args.reason]
|
|
43106
43123
|
},
|
|
43107
43124
|
sigProvider
|
|
43108
43125
|
);
|
|
43126
|
+
if (receipt.status !== index.ResponseStatus.Confirmed) {
|
|
43127
|
+
throw new Error(`Memory rollback was not confirmed (status=${receipt.status})`);
|
|
43128
|
+
}
|
|
43109
43129
|
}
|
|
43110
43130
|
|
|
43111
43131
|
// src-ts/memory/_json_safe.ts
|
|
@@ -43244,6 +43264,7 @@ async function guardMemoryWrite(input) {
|
|
|
43244
43264
|
endpoint,
|
|
43245
43265
|
verifyPubKey,
|
|
43246
43266
|
orgName,
|
|
43267
|
+
chainOpts,
|
|
43247
43268
|
threshold,
|
|
43248
43269
|
patterns,
|
|
43249
43270
|
toolNames,
|
|
@@ -43300,23 +43321,41 @@ async function guardMemoryWrite(input) {
|
|
|
43300
43321
|
committed: false
|
|
43301
43322
|
};
|
|
43302
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
|
+
}
|
|
43303
43333
|
const filePath = path3.basename(memEntry.key);
|
|
43304
|
-
|
|
43305
|
-
|
|
43306
|
-
|
|
43307
|
-
|
|
43308
|
-
|
|
43309
|
-
|
|
43310
|
-
|
|
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) {
|
|
43311
43344
|
const reason = err instanceof Error ? err.message : String(err);
|
|
43312
43345
|
logger2?.warn?.("[atbash] memory commit to chain failed", {
|
|
43313
43346
|
path: memEntry.key,
|
|
43314
43347
|
filePath,
|
|
43315
43348
|
reason
|
|
43316
43349
|
});
|
|
43317
|
-
|
|
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
|
+
}
|
|
43318
43357
|
logger2?.info?.(
|
|
43319
|
-
scanResult.verdict === "yellow" ? "[atbash] memory
|
|
43358
|
+
scanResult.verdict === "yellow" ? "[atbash] memory ALLOW (yellow, low score)" : "[atbash] memory ALLOW",
|
|
43320
43359
|
{
|
|
43321
43360
|
path: memEntry.key,
|
|
43322
43361
|
filePath,
|
|
@@ -43349,7 +43388,7 @@ async function syncLocalMemory(auth, pointer, opts = {}) {
|
|
|
43349
43388
|
if (withinTtl) {
|
|
43350
43389
|
return { drifted: false, checked: false, pointer };
|
|
43351
43390
|
}
|
|
43352
|
-
const currentId = await getActiveMemoryId(auth, opts.chainOpts);
|
|
43391
|
+
const currentId = await getActiveMemoryId(auth, opts.chainOpts, opts.filePath);
|
|
43353
43392
|
const nextPointer = { activeId: currentId, checkedAt: now };
|
|
43354
43393
|
if (currentId === pointer.activeId) {
|
|
43355
43394
|
return { drifted: false, checked: true, pointer: nextPointer };
|
|
@@ -43431,13 +43470,16 @@ function formatMeta(meta) {
|
|
|
43431
43470
|
}
|
|
43432
43471
|
function createFileLogger(filePath, upstream) {
|
|
43433
43472
|
let queue = Promise.resolve();
|
|
43473
|
+
let writeError;
|
|
43434
43474
|
async function ensureDir() {
|
|
43435
43475
|
await fs2.mkdir(path5.dirname(filePath), { recursive: true });
|
|
43436
43476
|
}
|
|
43437
43477
|
function append(level, message, meta) {
|
|
43438
43478
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${message}${formatMeta(meta)}
|
|
43439
43479
|
`;
|
|
43440
|
-
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 });
|
|
43441
43483
|
});
|
|
43442
43484
|
}
|
|
43443
43485
|
return {
|
|
@@ -43448,6 +43490,10 @@ function createFileLogger(filePath, upstream) {
|
|
|
43448
43490
|
warn(message, meta) {
|
|
43449
43491
|
upstream?.warn(message, meta ?? {});
|
|
43450
43492
|
append("warn", message, meta);
|
|
43493
|
+
},
|
|
43494
|
+
async flush() {
|
|
43495
|
+
await queue;
|
|
43496
|
+
if (writeError) throw writeError;
|
|
43451
43497
|
}
|
|
43452
43498
|
};
|
|
43453
43499
|
}
|
|
@@ -43537,7 +43583,15 @@ var MemoryGuardManager = class {
|
|
|
43537
43583
|
orgName: this.opts.orgName
|
|
43538
43584
|
});
|
|
43539
43585
|
const resolved = await atbash.resolveChainForOrg(this.opts.orgName);
|
|
43540
|
-
|
|
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
|
+
};
|
|
43541
43595
|
this._resolvedChainOpts = chainOpts;
|
|
43542
43596
|
this.logger.info(
|
|
43543
43597
|
`[atbash] resolved org \u2192 chain \u2014 org=${this.opts.orgName} network=${resolved.network}`
|
|
@@ -43546,9 +43600,9 @@ var MemoryGuardManager = class {
|
|
|
43546
43600
|
} catch (err) {
|
|
43547
43601
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43548
43602
|
this.logger.warn(
|
|
43549
|
-
`[atbash] org \u2192 chain resolution failed (
|
|
43603
|
+
`[atbash] org \u2192 chain resolution failed (memory read will be blocked): ${msg}`
|
|
43550
43604
|
);
|
|
43551
|
-
|
|
43605
|
+
throw err;
|
|
43552
43606
|
} finally {
|
|
43553
43607
|
this._resolveChainInflight = void 0;
|
|
43554
43608
|
}
|
|
@@ -43567,13 +43621,18 @@ var MemoryGuardManager = class {
|
|
|
43567
43621
|
const result = await syncLocalMemory(this.opts.auth, seed, {
|
|
43568
43622
|
ttlMs: 0,
|
|
43569
43623
|
force: true,
|
|
43570
|
-
chainOpts
|
|
43624
|
+
chainOpts,
|
|
43625
|
+
filePath: path6.basename(this.memoryFilePath)
|
|
43571
43626
|
});
|
|
43572
43627
|
if (!result.drifted && result.pointer.activeId == null) {
|
|
43573
43628
|
this.logger.info(
|
|
43574
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.`
|
|
43575
43630
|
);
|
|
43576
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
|
+
}
|
|
43577
43636
|
if (result.current.score < this.rollbackMinScore) {
|
|
43578
43637
|
this.logger.warn(
|
|
43579
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.`
|
|
@@ -43587,7 +43646,8 @@ var MemoryGuardManager = class {
|
|
|
43587
43646
|
await this.writeMemoryAtomic(result.current.content);
|
|
43588
43647
|
} catch (err) {
|
|
43589
43648
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43590
|
-
this.logger.warn("[atbash] boot sync write failed
|
|
43649
|
+
this.logger.warn("[atbash] boot sync write failed; pointer remains unchanged", { error: msg });
|
|
43650
|
+
return;
|
|
43591
43651
|
}
|
|
43592
43652
|
}
|
|
43593
43653
|
await this.pointerStore.set(this.agentPubkeyHex, result.pointer);
|
|
@@ -43601,9 +43661,9 @@ var MemoryGuardManager = class {
|
|
|
43601
43661
|
}
|
|
43602
43662
|
/**
|
|
43603
43663
|
* Returns a `HookDecision` when the guard reached a decision about this event.
|
|
43604
|
-
* Returns `null`
|
|
43605
|
-
*
|
|
43606
|
-
*
|
|
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.
|
|
43607
43667
|
*
|
|
43608
43668
|
* A returned decision carries `audited` (see `HookDecision`). Only
|
|
43609
43669
|
* `{ allow: true, audited: true }` means "checked and cleared"; anything else
|
|
@@ -43639,6 +43699,7 @@ var MemoryGuardManager = class {
|
|
|
43639
43699
|
endpoint: this.opts.judgeEndpoint,
|
|
43640
43700
|
verifyPubKey: this.opts.judgeVerifyPubKey,
|
|
43641
43701
|
orgName: this.opts.orgName,
|
|
43702
|
+
chainOpts: this.opts.chainOpts,
|
|
43642
43703
|
patterns: this.opts.memoryPathPatterns,
|
|
43643
43704
|
toolNames: this.opts.memoryWriteToolNames,
|
|
43644
43705
|
enforce: this.enforce,
|
|
@@ -43683,6 +43744,14 @@ var MemoryGuardManager = class {
|
|
|
43683
43744
|
reason: `memory scan returned ${sr2.verdict} but this guard is not enforcing it`
|
|
43684
43745
|
};
|
|
43685
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
|
+
}
|
|
43686
43755
|
return { allow: true, audited: true, verdict: sr2.verdict };
|
|
43687
43756
|
}
|
|
43688
43757
|
/**
|
|
@@ -43717,62 +43786,73 @@ var MemoryGuardManager = class {
|
|
|
43717
43786
|
return found.every((p) => path6.resolve(p) === managed);
|
|
43718
43787
|
}
|
|
43719
43788
|
async handleMemoryRead(event, ctx) {
|
|
43789
|
+
if (!this.vouchesForTarget(event, ctx)) return null;
|
|
43720
43790
|
const pointer = await this.pointerStore.get(this.agentPubkeyHex);
|
|
43721
43791
|
let result;
|
|
43792
|
+
let chainOpts;
|
|
43722
43793
|
try {
|
|
43723
|
-
|
|
43794
|
+
chainOpts = await this.resolveChainOpts();
|
|
43724
43795
|
result = await syncLocalMemory(this.opts.auth, pointer, {
|
|
43725
43796
|
ttlMs: this.ttlMs,
|
|
43726
|
-
|
|
43797
|
+
force: this.enforce,
|
|
43798
|
+
chainOpts,
|
|
43799
|
+
filePath: path6.basename(this.memoryFilePath)
|
|
43727
43800
|
});
|
|
43728
43801
|
} catch (err) {
|
|
43729
43802
|
if (err instanceof MemoryIntegrityError) {
|
|
43730
43803
|
const reason = `Memory integrity check failed on id ${err.id} \u2014 read blocked.`;
|
|
43731
43804
|
this.logger.warn("[atbash] MEMORY INTEGRITY FAILURE", { id: err.id, error: err.message });
|
|
43732
|
-
|
|
43733
|
-
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43805
|
+
return this.unverifiedRead(reason);
|
|
43734
43806
|
}
|
|
43735
43807
|
const msg = err instanceof Error ? err.message : String(err);
|
|
43736
|
-
this.logger.warn("[atbash] memory sync failed
|
|
43737
|
-
return
|
|
43808
|
+
this.logger.warn("[atbash] memory sync failed", { error: msg });
|
|
43809
|
+
return this.unverifiedRead(`Memory chain sync failed: ${msg}`);
|
|
43738
43810
|
}
|
|
43739
|
-
|
|
43740
|
-
|
|
43741
|
-
|
|
43742
|
-
|
|
43743
|
-
if (fresh.score < this.rollbackMinScore) {
|
|
43744
|
-
const reason = `Rolled-back memory version #${fresh.id} scored ${fresh.score} (below threshold ${this.rollbackMinScore}) \u2014 read blocked.`;
|
|
43745
|
-
this.logger.warn("[atbash] blocking read on low-score rollback", { id: fresh.id, score: fresh.score });
|
|
43746
|
-
if (!this.enforce) return null;
|
|
43747
|
-
return { block: true, blockReason: reason, allow: false, reason, audited: true };
|
|
43748
|
-
}
|
|
43749
|
-
this.logger.info("[atbash] memory drift detected \u2014 refreshing local file", {
|
|
43750
|
-
id: fresh.id,
|
|
43751
|
-
score: fresh.score
|
|
43752
|
-
});
|
|
43753
|
-
try {
|
|
43754
|
-
await this.writeMemoryAtomic(fresh.content);
|
|
43755
|
-
} catch (err) {
|
|
43756
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
43757
|
-
this.logger.warn("[atbash] failed to write refreshed memory (serving old)", { error: msg });
|
|
43758
|
-
onDiskIsCurrent = false;
|
|
43759
|
-
}
|
|
43760
|
-
} else {
|
|
43761
|
-
this.logger.info("[atbash] active memory removed on chain", { pubkey: this.agentPubkeyHex });
|
|
43762
|
-
onDiskIsCurrent = false;
|
|
43763
|
-
}
|
|
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.");
|
|
43764
43815
|
}
|
|
43765
|
-
|
|
43766
|
-
|
|
43767
|
-
|
|
43768
|
-
|
|
43769
|
-
|
|
43770
|
-
);
|
|
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}`);
|
|
43771
43823
|
}
|
|
43772
|
-
|
|
43773
|
-
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);
|
|
43774
43846
|
return { allow: true, audited: true };
|
|
43775
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
|
+
}
|
|
43776
43856
|
async writeMemoryAtomic(content) {
|
|
43777
43857
|
await fs3.mkdir(path6.dirname(this.memoryFilePath), { recursive: true });
|
|
43778
43858
|
const tmp = `${this.memoryFilePath}.${process.pid}.tmp`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atbash/sdk",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.6-dev.0",
|
|
4
4
|
"description": "TypeScript SDK for Atbash — the safety layer that evaluates AI agent actions against operator-defined policies before execution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atbash",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"test:native": "node --test __test__/vector_parity.test.mjs",
|
|
60
60
|
"test:sdk": "node --test __test__/*.sdk.test.mjs",
|
|
61
61
|
"test:live": "node --test __test__/live_technical_health.test.mjs",
|
|
62
|
+
"test:memory:live": "node --test __test__/memory_chain_live.test.mjs",
|
|
62
63
|
"test": "node --test __test__/*.test.mjs",
|
|
63
64
|
"lint": "eslint .",
|
|
64
65
|
"lint:fix": "eslint . --fix",
|
|
@@ -89,10 +90,10 @@
|
|
|
89
90
|
"typescript-eslint": "^8.62.1"
|
|
90
91
|
},
|
|
91
92
|
"optionalDependencies": {
|
|
92
|
-
"@atbash/sdk-linux-x64-gnu": "0.16.
|
|
93
|
-
"@atbash/sdk-linux-arm64-gnu": "0.16.
|
|
94
|
-
"@atbash/sdk-linux-x64-musl": "0.16.
|
|
95
|
-
"@atbash/sdk-darwin-arm64": "0.16.
|
|
96
|
-
"@atbash/sdk-win32-x64-msvc": "0.16.
|
|
93
|
+
"@atbash/sdk-linux-x64-gnu": "0.16.6-dev.0",
|
|
94
|
+
"@atbash/sdk-linux-arm64-gnu": "0.16.6-dev.0",
|
|
95
|
+
"@atbash/sdk-linux-x64-musl": "0.16.6-dev.0",
|
|
96
|
+
"@atbash/sdk-darwin-arm64": "0.16.6-dev.0",
|
|
97
|
+
"@atbash/sdk-win32-x64-msvc": "0.16.6-dev.0"
|
|
97
98
|
}
|
|
98
99
|
}
|