@agentdocumentformat/cli 0.7.8 → 0.8.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 +1 -1
- package/dist/adf.mjs +59 -8
- package/dist/chunks/{APEv2Parser-2CNNHMXV.mjs → APEv2Parser-WIXRJLHB.mjs} +2 -2
- package/dist/chunks/{AiffParser-7BR4RL6G.mjs → AiffParser-ZC4QFOVJ.mjs} +5 -5
- package/dist/chunks/{App-7RIIOHJJ.mjs → App-A5GUFCFJ.mjs} +279 -293
- package/dist/chunks/{DsdiffParser-CMD4AX7A.mjs → DsdiffParser-UAPAJHFX.mjs} +5 -5
- package/dist/chunks/{DsfParser-UOGZDS2M.mjs → DsfParser-NNYZK2QX.mjs} +6 -6
- package/dist/chunks/{FlacParser-HB3T3CD4.mjs → FlacParser-MW2W4LO3.mjs} +6 -6
- package/dist/chunks/{MP4Parser-66HBRUSX.mjs → MP4Parser-Z57SYOOQ.mjs} +4 -4
- package/dist/chunks/{MpegParser-YW6LIUEW.mjs → MpegParser-XIIPAZVM.mjs} +7 -7
- package/dist/chunks/{MusepackParser-PAUSXP7D.mjs → MusepackParser-QWNDEYHJ.mjs} +6 -6
- package/dist/chunks/{OggParser-NUBV3GWC.mjs → OggParser-WRX53F77.mjs} +9 -9
- package/dist/chunks/{WavPackParser-WQAGT7TC.mjs → WavPackParser-MJ7WCSJ5.mjs} +2 -2
- package/dist/chunks/{WaveParser-EWLQVDX4.mjs → WaveParser-RAVLHZ5S.mjs} +7 -7
- package/dist/chunks/{adf-schema-US4NFHMR.mjs → adf-schema-YJSGLMA4.mjs} +2 -2
- package/dist/chunks/auth-manager-AJ32FZWS.mjs +19 -0
- package/dist/chunks/auth-manager-C5OBEQK2.mjs +18 -0
- package/dist/chunks/auth-manager-TBNKCJ45.mjs +17 -0
- package/dist/chunks/{chatgpt-subscription-E4MXTIAT.mjs → chatgpt-subscription-MMGNW5KJ.mjs} +5 -2
- package/dist/chunks/chunk-3UGZFWIF.mjs +26 -0
- package/dist/chunks/{chunk-5PO5PKRV.mjs → chunk-4OGZFUKA.mjs} +126 -67
- package/dist/chunks/{chunk-VN7UH7I5.mjs → chunk-6XQZGYMK.mjs} +246 -67
- package/dist/chunks/{chunk-XNPZBLYL.mjs → chunk-75DC5EWN.mjs} +13 -2
- package/dist/chunks/chunk-AZYOXXVL.mjs +865 -0
- package/dist/chunks/{chunk-6VQEXLE2.mjs → chunk-BHY7QDCM.mjs} +41 -3
- package/dist/chunks/chunk-C2S64Y6F.mjs +203 -0
- package/dist/chunks/chunk-C4GA5SI4.mjs +12799 -0
- package/dist/chunks/chunk-DDTTSKGG.mjs +2443 -0
- package/dist/chunks/{chunk-VAUOM3IN.mjs → chunk-DLWCKKIO.mjs} +1 -1
- package/dist/chunks/{chunk-QPLEAE6V.mjs → chunk-G6NBFYG5.mjs} +152 -824
- package/dist/chunks/{chunk-KXEF4F4E.mjs → chunk-GAZO6UMI.mjs} +1 -1
- package/dist/chunks/{chunk-XYTWGHFS.mjs → chunk-H3XLRWVE.mjs} +1 -1
- package/dist/chunks/{chunk-7T2YMTWL.mjs → chunk-H4C65MTP.mjs} +4 -4
- package/dist/chunks/chunk-HBOFZRUS.mjs +738 -0
- package/dist/chunks/chunk-JVPZIMXW.mjs +976 -0
- package/dist/chunks/{chunk-XYV3VCIX.mjs → chunk-JZFONJSL.mjs} +79 -20
- package/dist/chunks/chunk-KDDVQKCG.mjs +65 -0
- package/dist/chunks/chunk-KRPKWOMB.mjs +290 -0
- package/dist/chunks/{chunk-G6PH7RPW.mjs → chunk-LCMANRYY.mjs} +3 -3
- package/dist/chunks/{chunk-ZESQMP66.mjs → chunk-LGL6ML37.mjs} +3 -3
- package/dist/chunks/chunk-LKFHERRV.mjs +145 -0
- package/dist/chunks/chunk-LTX74NU6.mjs +29 -0
- package/dist/chunks/chunk-M2F7C2UJ.mjs +425 -0
- package/dist/chunks/{chunk-62NE4LG4.mjs → chunk-MQAMI5BF.mjs} +5 -5
- package/dist/chunks/chunk-MZRXFWPQ.mjs +278 -0
- package/dist/chunks/{chunk-PRMGZZM5.mjs → chunk-NZLEPJ5M.mjs} +2 -0
- package/dist/chunks/{chunk-CD3ATSXE.mjs → chunk-NZO5O4VN.mjs} +1 -1
- package/dist/chunks/{chunk-I3UYSITV.mjs → chunk-O7XKQ6CH.mjs} +1 -1
- package/dist/chunks/chunk-P3RG5LQA.mjs +244 -0
- package/dist/chunks/chunk-PAK5JCO2.mjs +48 -0
- package/dist/chunks/chunk-PC63YFB7.mjs +34 -0
- package/dist/chunks/{daemon-control-HKFVSQTQ.mjs → chunk-PRPTKMQW.mjs} +67 -26
- package/dist/chunks/{chunk-LFMB4PAA.mjs → chunk-TKXIH27X.mjs} +974 -113
- package/dist/chunks/{chunk-5W7KES6P.mjs → chunk-UZPDETEU.mjs} +1 -1
- package/dist/chunks/chunk-V2T6NZX2.mjs +517 -0
- package/dist/chunks/chunk-V3MM6PCH.mjs +366 -0
- package/dist/chunks/chunk-WJGPNHGX.mjs +67 -0
- package/dist/chunks/chunk-WVTPPOKK.mjs +290 -0
- package/dist/chunks/{chunk-J74STFKE.mjs → chunk-WX6QCUWI.mjs} +272 -51
- package/dist/chunks/{chunk-Y5ZLKFLE.mjs → chunk-XNNJQCZP.mjs} +196 -165
- package/dist/chunks/{chunk-HDBPBORT.mjs → chunk-XT2BJUFC.mjs} +15 -6
- package/dist/chunks/{chunk-HSBV6YFU.mjs → chunk-YRMKSHW3.mjs} +614 -92
- package/dist/chunks/{chunk-BNXUAOO4.mjs → chunk-ZGXUOAWZ.mjs} +3186 -959
- package/dist/chunks/chunk-ZPB3HYM7.mjs +45 -0
- package/dist/chunks/claude-subscription-33BMZB6I.mjs +29 -0
- package/dist/chunks/{cli-NMFIAR7Z.mjs → cli-I47IFDHJ.mjs} +797 -56
- package/dist/chunks/{commands-KPJWFRUE.mjs → commands-E4EQUHE5.mjs} +4 -3
- package/dist/chunks/{daemon-YDIK3AAM.mjs → daemon-FQ5BV3AP.mjs} +82058 -63958
- package/dist/chunks/daemon-control-L3CLG63P.mjs +59 -0
- package/dist/chunks/{daemon-token-WRSULP7G.mjs → daemon-token-L5QKTHU5.mjs} +3 -1
- package/dist/chunks/{discord-DSP4HUWI.mjs → discord-IR62565M.mjs} +3 -52
- package/dist/chunks/{email-BRG7KUO2.mjs → email-KHHTJBDW.mjs} +4 -2
- package/dist/chunks/{grok-subscription-4PYX7TBF.mjs → grok-subscription-E74FS5EG.mjs} +5 -2
- package/dist/chunks/{lib-N7FCZWKH.mjs → lib-L27L5PXM.mjs} +22 -22
- package/dist/chunks/{open-VAU7PXT6.mjs → open-TLGS2Z3Q.mjs} +4 -4
- package/dist/chunks/{parser-JVY2PWSM.mjs → parser-7GARNT5L.mjs} +1 -1
- package/dist/chunks/{pipeline-executor-O77NTGLP.mjs → pipeline-executor-FD4FBWQA.mjs} +6 -5
- package/dist/chunks/prompt-RRHZX4YT.mjs +13 -0
- package/dist/chunks/remote-access-cli-XXAPQWH6.mjs +268 -0
- package/dist/chunks/remote-hello-UFWUBDQI.mjs +37 -0
- package/dist/chunks/sdk-5FD2ZP7N.mjs +28422 -0
- package/dist/chunks/{slack-OCNNF4DX.mjs → slack-IYC5RAB5.mjs} +3 -4
- package/dist/chunks/{store-HUVDJPYN.mjs → store-ZRN2WCFB.mjs} +6 -3
- package/dist/chunks/tailscale-WLJATFMB.mjs +25 -0
- package/dist/chunks/{theme-SFE3JMA2.mjs → theme-HLBDTF6F.mjs} +1 -1
- package/dist/chunks/{themes-YLXLOIJF.mjs → themes-BQ72P446.mjs} +2 -2
- package/dist/chunks/{token-6G3HCMNX.mjs → token-6D22WECM.mjs} +20 -16
- package/dist/chunks/{token-util-UXYSDQIW.mjs → token-util-YF3ZWBZ4.mjs} +1 -2
- package/dist/chunks/{tui-UQJLK7XI.mjs → tui-SPSPDADM.mjs} +17 -14
- package/dist/chunks/{welcome-XHXFV7SH.mjs → welcome-J5JZZAM5.mjs} +1 -1
- package/dist/chunks/{whatsapp-AJMRQBT7.mjs → whatsapp-YJFFVN4Q.mjs} +9 -9
- package/package.json +1 -1
- package/dist/chunks/chunk-GKKCMXIZ.mjs +0 -94
- package/dist/chunks/chunk-IHDDCZBS.mjs +0 -112
- package/dist/chunks/chunk-KWFOS5WC.mjs +0 -41
- package/dist/chunks/chunk-UEZR2CNS.mjs +0 -65
- package/dist/chunks/chunk-VMHTYDH4.mjs +0 -172
- package/dist/chunks/chunk-Y2QA53AN.mjs +0 -259
- package/dist/chunks/{chunk-NBTUWMGP.mjs → chunk-2MTE22CR.mjs} +3 -3
- package/dist/chunks/{telegram-2EFGKIVK.mjs → telegram-JYY2VDTI.mjs} +5 -5
|
@@ -7,11 +7,19 @@ const __dirname = __adfDirname(__filename);
|
|
|
7
7
|
import {
|
|
8
8
|
DEFAULT_AGENT_CONFIG,
|
|
9
9
|
DOCS_GUIDES_URL
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-LCMANRYY.mjs";
|
|
11
11
|
import {
|
|
12
12
|
nanoid
|
|
13
13
|
} from "./chunk-3GNIDM53.mjs";
|
|
14
14
|
import {
|
|
15
|
+
applyFileRefChanges,
|
|
16
|
+
isAudioInputMime,
|
|
17
|
+
isFileRefBlock,
|
|
18
|
+
isVideoInputMime,
|
|
19
|
+
isVisionMime
|
|
20
|
+
} from "./chunk-LKFHERRV.mjs";
|
|
21
|
+
import {
|
|
22
|
+
AUDIT_DEFAULTS,
|
|
15
23
|
CODE_EXECUTION_DEFAULTS,
|
|
16
24
|
DEFAULT_MIND_CONTENT,
|
|
17
25
|
DEFAULT_MIND_LOG_CONTENT,
|
|
@@ -21,7 +29,7 @@ import {
|
|
|
21
29
|
META_PROTECTION_LEVELS,
|
|
22
30
|
SETTABLE_STATES,
|
|
23
31
|
getDefaultDocumentContent
|
|
24
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-NZO5O4VN.mjs";
|
|
25
33
|
import {
|
|
26
34
|
__esm,
|
|
27
35
|
__export
|
|
@@ -211,38 +219,6 @@ var TEXT_MIME_PREFIXES = ["text/"], TEXT_MIME_EXACT = /* @__PURE__ */ new Set([
|
|
|
211
219
|
function isTextMime(mime) {
|
|
212
220
|
return mime ? TEXT_MIME_PREFIXES.some((p) => mime.startsWith(p)) || /\+(json|xml|yaml)$/.test(mime) ? !0 : TEXT_MIME_EXACT.has(mime) : !1;
|
|
213
221
|
}
|
|
214
|
-
var VISION_MIMES = /* @__PURE__ */ new Set([
|
|
215
|
-
"image/png",
|
|
216
|
-
"image/jpeg",
|
|
217
|
-
"image/gif",
|
|
218
|
-
"image/webp"
|
|
219
|
-
]);
|
|
220
|
-
function isVisionMime(mime) {
|
|
221
|
-
return mime ? VISION_MIMES.has(mime) : !1;
|
|
222
|
-
}
|
|
223
|
-
var AUDIO_INPUT_MIMES = /* @__PURE__ */ new Set([
|
|
224
|
-
"audio/wav",
|
|
225
|
-
"audio/mpeg",
|
|
226
|
-
"audio/mp3",
|
|
227
|
-
"audio/aiff",
|
|
228
|
-
"audio/aac",
|
|
229
|
-
"audio/ogg",
|
|
230
|
-
"audio/flac",
|
|
231
|
-
"audio/x-m4a",
|
|
232
|
-
"audio/webm"
|
|
233
|
-
]);
|
|
234
|
-
function isAudioInputMime(mime) {
|
|
235
|
-
return mime ? AUDIO_INPUT_MIMES.has(mime) : !1;
|
|
236
|
-
}
|
|
237
|
-
var VIDEO_INPUT_MIMES = /* @__PURE__ */ new Set([
|
|
238
|
-
"video/mp4",
|
|
239
|
-
"video/mpeg",
|
|
240
|
-
"video/quicktime",
|
|
241
|
-
"video/webm"
|
|
242
|
-
]);
|
|
243
|
-
function isVideoInputMime(mime) {
|
|
244
|
-
return mime ? VIDEO_INPUT_MIMES.has(mime) : !1;
|
|
245
|
-
}
|
|
246
222
|
var AUDIO_FORMAT_MAP = {
|
|
247
223
|
"audio/wav": "wav",
|
|
248
224
|
"audio/mpeg": "mp3",
|
|
@@ -462,7 +438,7 @@ var catHandler = {
|
|
|
462
438
|
async execute(ctx) {
|
|
463
439
|
if (ctx.args.length < 2) return err("cp: usage: cp <src> <dst>");
|
|
464
440
|
let src = vfsPath(ctx.args[0]), dst = vfsPath(ctx.args[1]), [content, readErr] = await shellReadFile(ctx.toolRegistry, ctx.workspace, src);
|
|
465
|
-
if (readErr) return err(`cp: ${readErr}`);
|
|
441
|
+
if (readErr !== null) return err(`cp: ${readErr}`);
|
|
466
442
|
let write = await ctx.toolRegistry.executeTool("fs_write", { mode: "write", path: dst, content }, ctx.workspace);
|
|
467
443
|
return write.isError ? err(`cp: ${write.content}`) : ok("");
|
|
468
444
|
}
|
|
@@ -717,7 +693,7 @@ var duHandler = {
|
|
|
717
693
|
let bytes = parseInt(raw, 10), content = ctx.stdin;
|
|
718
694
|
if (ctx.args.length > 0) {
|
|
719
695
|
let [fileContent, error] = await shellReadFile(ctx.toolRegistry, ctx.workspace, vfsPath(ctx.args[0]));
|
|
720
|
-
if (error) return err(`head: ${error}`);
|
|
696
|
+
if (error !== null) return err(`head: ${error}`);
|
|
721
697
|
content = fileContent;
|
|
722
698
|
}
|
|
723
699
|
return content ? ok(Buffer.from(content, "utf8").subarray(0, bytes).toString("utf8")) : ok("");
|
|
@@ -729,7 +705,7 @@ var duHandler = {
|
|
|
729
705
|
let text = ctx.stdin;
|
|
730
706
|
if (ctx.args.length > 0) {
|
|
731
707
|
let [content, error] = await shellReadFile(ctx.toolRegistry, ctx.workspace, vfsPath(ctx.args[0]), { end_line: n });
|
|
732
|
-
return error ? err(`head: ${error}`) : ok(content);
|
|
708
|
+
return error !== null ? err(`head: ${error}`) : ok(content);
|
|
733
709
|
}
|
|
734
710
|
if (!text) return ok("");
|
|
735
711
|
let lines = text.split(`
|
|
@@ -761,7 +737,7 @@ var duHandler = {
|
|
|
761
737
|
let fromByte = raw.startsWith("+"), bytes = parseInt(raw, 10), content = ctx.stdin;
|
|
762
738
|
if (ctx.args.length > 0) {
|
|
763
739
|
let [fileContent, error] = await shellReadFile(ctx.toolRegistry, ctx.workspace, vfsPath(ctx.args[0]));
|
|
764
|
-
if (error) return err(`tail: ${error}`);
|
|
740
|
+
if (error !== null) return err(`tail: ${error}`);
|
|
765
741
|
content = fileContent;
|
|
766
742
|
}
|
|
767
743
|
if (!content) return ok("");
|
|
@@ -779,7 +755,7 @@ var duHandler = {
|
|
|
779
755
|
let text = ctx.stdin;
|
|
780
756
|
if (ctx.args.length > 0) {
|
|
781
757
|
let [content, error] = await shellReadFile(ctx.toolRegistry, ctx.workspace, vfsPath(ctx.args[0]));
|
|
782
|
-
if (error) return err(`tail: ${error}`);
|
|
758
|
+
if (error !== null) return err(`tail: ${error}`);
|
|
783
759
|
text = content;
|
|
784
760
|
}
|
|
785
761
|
if (!text) return ok("");
|
|
@@ -1217,7 +1193,7 @@ var GREP_KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
1217
1193
|
for (let file of files) {
|
|
1218
1194
|
if (file.mime_type && !isTextMime(file.mime_type)) continue;
|
|
1219
1195
|
let [content, readErr] = await shellReadFile(ctx.toolRegistry, ctx.workspace, file.path);
|
|
1220
|
-
if (readErr) continue;
|
|
1196
|
+
if (readErr !== null) continue;
|
|
1221
1197
|
let lines2 = splitLines(content), sel2 = await select(lines2, content?.length ?? 0);
|
|
1222
1198
|
if ("failure" in sel2) return sel2.failure;
|
|
1223
1199
|
let fileMatches = sel2.idx.length;
|
|
@@ -1236,7 +1212,7 @@ var GREP_KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
1236
1212
|
let text = ctx.stdin;
|
|
1237
1213
|
if (fileArgs.length === 1) {
|
|
1238
1214
|
let [content, readErr] = await shellReadFile(ctx.toolRegistry, ctx.workspace, vfsPath2(fileArgs[0]));
|
|
1239
|
-
if (readErr) return err(`grep: ${fileArgs[0]}: No such file or directory`);
|
|
1215
|
+
if (readErr !== null) return err(`grep: ${fileArgs[0]}: No such file or directory`);
|
|
1240
1216
|
text = content;
|
|
1241
1217
|
}
|
|
1242
1218
|
if (!text)
|
|
@@ -1320,7 +1296,7 @@ var SED_KNOWN_FLAGS = /* @__PURE__ */ new Set(["i", "E", "r"]), sedHandler = {
|
|
|
1320
1296
|
if (ctx.args.length > 1) {
|
|
1321
1297
|
filePath = vfsPath2(ctx.args[1]);
|
|
1322
1298
|
let [sedContent, sedErr] = await shellReadFile(ctx.toolRegistry, ctx.workspace, filePath);
|
|
1323
|
-
if (sedErr) return err(`sed: ${filePath}: No such file or directory`);
|
|
1299
|
+
if (sedErr !== null) return err(`sed: ${filePath}: No such file or directory`);
|
|
1324
1300
|
text = sedContent;
|
|
1325
1301
|
} else if (!text)
|
|
1326
1302
|
return ok("");
|
|
@@ -1585,7 +1561,7 @@ var xargsHandler = {
|
|
|
1585
1561
|
batchSize = n;
|
|
1586
1562
|
}
|
|
1587
1563
|
if (!text) return ok("");
|
|
1588
|
-
let { parse } = await import("./parser-
|
|
1564
|
+
let { parse } = await import("./parser-7GARNT5L.mjs"), { executeNode } = await import("./pipeline-executor-FD4FBWQA.mjs"), subCtx = {
|
|
1589
1565
|
workspace: ctx.workspace,
|
|
1590
1566
|
toolRegistry: ctx.toolRegistry,
|
|
1591
1567
|
config: ctx.config,
|
|
@@ -2405,7 +2381,7 @@ async function executeShellScript(path, ctx) {
|
|
|
2405
2381
|
authorized = ctx.workspace.isFileAuthorized(path);
|
|
2406
2382
|
} catch {
|
|
2407
2383
|
}
|
|
2408
|
-
let gate = { ...ctx.gate ?? {}, authorized }, base = baseRegistry(ctx.toolRegistry), toolRegistry = gate.authorized ? authorizedRegistry(base) : base, { parse } = await import("./parser-
|
|
2384
|
+
let gate = { ...ctx.gate ?? {}, authorized }, base = baseRegistry(ctx.toolRegistry), toolRegistry = gate.authorized ? authorizedRegistry(base) : base, { parse } = await import("./parser-7GARNT5L.mjs"), { executeNode, MAX_SHELL_DEPTH } = await import("./pipeline-executor-FD4FBWQA.mjs"), depth = (ctx.depth ?? 0) + 1;
|
|
2409
2385
|
if (depth > MAX_SHELL_DEPTH)
|
|
2410
2386
|
return err(`./${path}: script nesting too deep (${MAX_SHELL_DEPTH}) \u2014 possible infinite recursion`);
|
|
2411
2387
|
try {
|
|
@@ -2621,9 +2597,105 @@ import { createPrivateKey as createPrivateKey3, createPublicKey as createPublicK
|
|
|
2621
2597
|
// src/main/adf/adf-database.ts
|
|
2622
2598
|
import Database from "better-sqlite3";
|
|
2623
2599
|
import * as sqliteVec from "sqlite-vec";
|
|
2624
|
-
import { existsSync as existsSync2, unlinkSync, renameSync, copyFileSync, readdirSync, readFileSync as readFileSync3, openSync, closeSync, lstatSync, fstatSync, promises as fsp } from "fs";
|
|
2600
|
+
import { existsSync as existsSync2, unlinkSync, renameSync, copyFileSync, readdirSync, readFileSync as readFileSync3, openSync, closeSync, lstatSync, fstatSync, fsyncSync, statSync, promises as fsp } from "fs";
|
|
2601
|
+
import { createHash } from "crypto";
|
|
2625
2602
|
import { brotliDecompressSync } from "zlib";
|
|
2626
|
-
import { join as join2, resolve, sep } from "path";
|
|
2603
|
+
import { basename, dirname as dirname2, join as join2, resolve, sep } from "path";
|
|
2604
|
+
|
|
2605
|
+
// src/main/adf/file-ref-registry.ts
|
|
2606
|
+
var MediaPayloadCache = class {
|
|
2607
|
+
constructor(maxChars) {
|
|
2608
|
+
this.maxChars = maxChars;
|
|
2609
|
+
}
|
|
2610
|
+
maxChars;
|
|
2611
|
+
entries = /* @__PURE__ */ new Map();
|
|
2612
|
+
chars = 0;
|
|
2613
|
+
get(path) {
|
|
2614
|
+
let entry = this.entries.get(path);
|
|
2615
|
+
if (entry)
|
|
2616
|
+
return this.entries.delete(path), this.entries.set(path, entry), entry;
|
|
2617
|
+
}
|
|
2618
|
+
set(path, entry) {
|
|
2619
|
+
if (this.delete(path), !(entry.payload.length > this.maxChars)) {
|
|
2620
|
+
this.entries.set(path, entry), this.chars += entry.payload.length;
|
|
2621
|
+
for (let [oldest, value] of this.entries) {
|
|
2622
|
+
if (this.chars <= this.maxChars) break;
|
|
2623
|
+
this.entries.delete(oldest), this.chars -= value.payload.length;
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
delete(path) {
|
|
2628
|
+
let entry = this.entries.get(path);
|
|
2629
|
+
entry && (this.entries.delete(path), this.chars -= entry.payload.length);
|
|
2630
|
+
}
|
|
2631
|
+
clear() {
|
|
2632
|
+
this.entries.clear(), this.chars = 0;
|
|
2633
|
+
}
|
|
2634
|
+
get size() {
|
|
2635
|
+
return this.entries.size;
|
|
2636
|
+
}
|
|
2637
|
+
}, MEDIA_CACHE_MAX_CHARS = 48 * 1024 * 1024, FileRefRegistry = class {
|
|
2638
|
+
/**
|
|
2639
|
+
* Paths some transcript row references. Null until first needed: built by
|
|
2640
|
+
* one scan of adf_loop, then kept current by every loop write. May hold
|
|
2641
|
+
* paths whose rows are gone (cleared, compacted); that only costs a scan.
|
|
2642
|
+
*/
|
|
2643
|
+
referenced = null;
|
|
2644
|
+
media = new MediaPayloadCache(MEDIA_CACHE_MAX_CHARS);
|
|
2645
|
+
/** Bumped when a file a request has carried changes, so resolved requests rebuild. */
|
|
2646
|
+
generation = 0;
|
|
2647
|
+
/** Paths a request has carried. */
|
|
2648
|
+
resolved = /* @__PURE__ */ new Set();
|
|
2649
|
+
holders = /* @__PURE__ */ new Set();
|
|
2650
|
+
addHolder(holder) {
|
|
2651
|
+
this.holders.add(new WeakRef(holder));
|
|
2652
|
+
}
|
|
2653
|
+
/** Record the references in a loop row just written. */
|
|
2654
|
+
noteContent(content) {
|
|
2655
|
+
if (this.referenced)
|
|
2656
|
+
for (let block of content)
|
|
2657
|
+
isFileRefBlock(block) && !block.file_ref.deleted && this.referenced.add(block.file_ref.path);
|
|
2658
|
+
}
|
|
2659
|
+
/** Whether any of these changes names a referenced path (`referenced` must be built). */
|
|
2660
|
+
affects(changes) {
|
|
2661
|
+
let referenced = this.referenced;
|
|
2662
|
+
return referenced ? changes.some((change) => referenced.has(change.op === "rename" ? change.from : change.path)) : !0;
|
|
2663
|
+
}
|
|
2664
|
+
/** Keep `referenced` current after the rows were rewritten. */
|
|
2665
|
+
noteChanges(changes) {
|
|
2666
|
+
let referenced = this.referenced;
|
|
2667
|
+
if (referenced)
|
|
2668
|
+
for (let change of changes)
|
|
2669
|
+
change.op !== "rename" || !referenced.has(change.from) || (referenced.delete(change.from), referenced.add(change.to));
|
|
2670
|
+
}
|
|
2671
|
+
/** Rewrite the references every live session holds. */
|
|
2672
|
+
notifyHolders(changes) {
|
|
2673
|
+
for (let ref of this.holders) {
|
|
2674
|
+
let holder = ref.deref();
|
|
2675
|
+
if (!holder) {
|
|
2676
|
+
this.holders.delete(ref);
|
|
2677
|
+
continue;
|
|
2678
|
+
}
|
|
2679
|
+
try {
|
|
2680
|
+
holder.applyFileRefChanges(changes);
|
|
2681
|
+
} catch (error) {
|
|
2682
|
+
console.error("[FileRefRegistry] A session could not apply a file change:", error);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
/** A file was written, renamed or deleted: its loaded bytes are stale. */
|
|
2687
|
+
invalidate(path) {
|
|
2688
|
+
this.media.delete(path), this.resolved.has(path) && this.generation++;
|
|
2689
|
+
}
|
|
2690
|
+
}, byFile = /* @__PURE__ */ new Map(), byDatabase = /* @__PURE__ */ new WeakMap();
|
|
2691
|
+
function fileRefRegistry(database, canonicalPath) {
|
|
2692
|
+
if (canonicalPath === null) {
|
|
2693
|
+
let registry4 = byDatabase.get(database);
|
|
2694
|
+
return registry4 || (registry4 = new FileRefRegistry(), byDatabase.set(database, registry4)), registry4;
|
|
2695
|
+
}
|
|
2696
|
+
let registry3 = byFile.get(canonicalPath);
|
|
2697
|
+
return registry3 || (registry3 = new FileRefRegistry(), byFile.set(canonicalPath, registry3)), registry3;
|
|
2698
|
+
}
|
|
2627
2699
|
|
|
2628
2700
|
// src/shared/constants/agent-icons.ts
|
|
2629
2701
|
var AGENT_ICON_POOL = [
|
|
@@ -2834,7 +2906,20 @@ function didToPublicKey(did) {
|
|
|
2834
2906
|
}
|
|
2835
2907
|
|
|
2836
2908
|
// src/main/adf/adf-database.ts
|
|
2837
|
-
var nanoid2 = () => nanoid(10), LOOP_TOOLS_BACKFILL_META = "adf_loop_tools_backfilled", LEGACY_ESSENTIAL_LOOP_TOOLS = ["loop_send", "loop_list"], ADF_LATEST_SCHEMA_VERSION = 32,
|
|
2909
|
+
var nanoid2 = () => nanoid(10), LOOP_TOOLS_BACKFILL_META = "adf_loop_tools_backfilled", LEGACY_ESSENTIAL_LOOP_TOOLS = ["loop_send", "loop_list"], ADF_LATEST_SCHEMA_VERSION = 32, ADF_OLDEST_MIGRATABLE_SCHEMA_VERSION = 3, AdfSchemaTooOldError = class extends Error {
|
|
2910
|
+
constructor(filePath, schemaVersion, opts = {}) {
|
|
2911
|
+
let name = basename(filePath);
|
|
2912
|
+
super(
|
|
2913
|
+
(schemaVersion > 0 ? `${name} uses schema v${schemaVersion}, which this version cannot upgrade` : `${name} has no schema version and an older layout, which this version cannot upgrade`) + ` (it upgrades v${ADF_OLDEST_MIGRATABLE_SCHEMA_VERSION} and later; no released version upgrades older files).${opts.unchanged === !1 ? "" : " The file was not changed."} See "An agent file uses schema v2" in the ADF troubleshooting guide.`
|
|
2914
|
+
);
|
|
2915
|
+
this.filePath = filePath;
|
|
2916
|
+
this.schemaVersion = schemaVersion;
|
|
2917
|
+
this.name = "AdfSchemaTooOldError";
|
|
2918
|
+
}
|
|
2919
|
+
filePath;
|
|
2920
|
+
schemaVersion;
|
|
2921
|
+
code = "schema_too_old";
|
|
2922
|
+
}, CLEAN_CLOSE_META_KEY = "adf_clean_close", SNAPSHOT_DEADLINE_MS = 3e4, OPFLAG_P2ISREG = 16;
|
|
2838
2923
|
function formatMetaNumber(n) {
|
|
2839
2924
|
return Number.isInteger(n) ? String(n) : String(Number(n.toFixed(10)));
|
|
2840
2925
|
}
|
|
@@ -3144,7 +3229,12 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3144
3229
|
/** Page cap for getLogsAfterId — pollers carry a cursor, so pages catch up. */
|
|
3145
3230
|
static MAX_LOGS_AFTER_ID = 5e3;
|
|
3146
3231
|
constructor(db, filePath) {
|
|
3147
|
-
this.db = db, this.filePath = filePath, _AdfDatabase.incrementOpen(filePath)
|
|
3232
|
+
this.db = db, this.filePath = filePath, _AdfDatabase.incrementOpen(filePath);
|
|
3233
|
+
try {
|
|
3234
|
+
this.applyPragmas(), this.prepareStatements();
|
|
3235
|
+
} catch (err2) {
|
|
3236
|
+
throw _AdfDatabase.decrementOpen(filePath), err2;
|
|
3237
|
+
}
|
|
3148
3238
|
}
|
|
3149
3239
|
// ===========================================================================
|
|
3150
3240
|
// Static Factory Methods
|
|
@@ -3154,6 +3244,36 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3154
3244
|
let code = err2?.code;
|
|
3155
3245
|
return typeof code == "string" && (code.startsWith("SQLITE_BUSY") || code === "SQLITE_LOCKED");
|
|
3156
3246
|
}
|
|
3247
|
+
/**
|
|
3248
|
+
* The file's schema version: `adf_schema_version`, else the older
|
|
3249
|
+
* `schema_version`, else 0. Null when there is no adf_meta table. Reads only.
|
|
3250
|
+
*/
|
|
3251
|
+
static readSchemaVersion(db) {
|
|
3252
|
+
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='adf_meta'").get()) return null;
|
|
3253
|
+
let r = db.prepare("SELECT value FROM adf_meta WHERE key = 'adf_schema_version'").get(), parse = (v) => {
|
|
3254
|
+
let n = parseInt(v, 10);
|
|
3255
|
+
return Number.isFinite(n) ? n : 0;
|
|
3256
|
+
};
|
|
3257
|
+
if (r) return parse(r.value);
|
|
3258
|
+
let r2 = db.prepare("SELECT value FROM adf_meta WHERE key = 'schema_version'").get();
|
|
3259
|
+
return r2 ? parse(r2.value) : 0;
|
|
3260
|
+
}
|
|
3261
|
+
/**
|
|
3262
|
+
* The schema version open() refuses for this file, or null when it may be
|
|
3263
|
+
* opened (and migrated). 1 <= v < 3 is refused. No version at all (0, or
|
|
3264
|
+
* unparsable) is refused unless the file has a clone's layout: a clone
|
|
3265
|
+
* whose adf_meta was deselected (before clone-fixup restamped the version)
|
|
3266
|
+
* has `adf_audit`, `adf_loop.loop` and no `adf_archive`. Null too without
|
|
3267
|
+
* an adf_meta table (the v0.2 check reports that). Reads only.
|
|
3268
|
+
*/
|
|
3269
|
+
static refusedSchemaVersion(db) {
|
|
3270
|
+
let sv = _AdfDatabase.readSchemaVersion(db);
|
|
3271
|
+
if (sv === null) return null;
|
|
3272
|
+
if (sv > 0 && sv < ADF_OLDEST_MIGRATABLE_SCHEMA_VERSION) return sv;
|
|
3273
|
+
if (sv > 0) return null;
|
|
3274
|
+
let tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map((t) => t.name)), loopCols = tables.has("adf_loop") ? new Set(db.prepare("PRAGMA table_info(adf_loop)").all().map((c) => c.name)) : /* @__PURE__ */ new Set();
|
|
3275
|
+
return tables.has("adf_audit") && !tables.has("adf_archive") && loopCols.has("loop") ? null : 0;
|
|
3276
|
+
}
|
|
3157
3277
|
/** Retryable lock-contention error — distinct from the corruption path. */
|
|
3158
3278
|
static lockedError(filePath) {
|
|
3159
3279
|
return new Error(
|
|
@@ -3224,18 +3344,146 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3224
3344
|
}
|
|
3225
3345
|
/** Synchronous backup — only for `open()`, which cannot await mid-migration.
|
|
3226
3346
|
* A file copy is safe HERE and only here: open() runs before any writer of
|
|
3227
|
-
* this connection exists, so nothing can checkpoint underneath it.
|
|
3347
|
+
* this connection exists, so nothing can checkpoint underneath it.
|
|
3348
|
+
* `complete`: the checkpoint folded the whole WAL first, so the copy holds
|
|
3349
|
+
* every committed write (a failed open may restore from it).
|
|
3350
|
+
* `reused`: a `.bak` already on disk has the same size and SHA-256 as the
|
|
3351
|
+
* checkpointed file (a migration that failed before and rolled back), so
|
|
3352
|
+
* it is kept as is instead of being moved aside and copied again. The
|
|
3353
|
+
* copy is fsynced. */
|
|
3228
3354
|
static backupBeforeDestructiveSync(db, filePath) {
|
|
3229
|
-
let backupPath = filePath + ".bak";
|
|
3355
|
+
let backupPath = filePath + ".bak", complete = !1;
|
|
3356
|
+
try {
|
|
3357
|
+
let [ck] = db.pragma("wal_checkpoint(TRUNCATE)");
|
|
3358
|
+
complete = ck?.busy === 0;
|
|
3359
|
+
} catch {
|
|
3360
|
+
}
|
|
3361
|
+
if (_AdfDatabase.sameFileContent(filePath, backupPath)) return { backupPath, complete, reused: !0 };
|
|
3230
3362
|
try {
|
|
3231
3363
|
renameSync(backupPath, _AdfDatabase.staleBackupName(backupPath));
|
|
3232
3364
|
} catch {
|
|
3233
3365
|
}
|
|
3366
|
+
return copyFileSync(filePath, backupPath), _AdfDatabase.fsyncFileSync(backupPath), { backupPath, complete, reused: !1 };
|
|
3367
|
+
}
|
|
3368
|
+
/** Both files exist with the same size and SHA-256. */
|
|
3369
|
+
static sameFileContent(a, b) {
|
|
3234
3370
|
try {
|
|
3235
|
-
|
|
3371
|
+
if (statSync(a).size !== statSync(b).size) return !1;
|
|
3372
|
+
let hash = (p) => createHash("sha256").update(readFileSync3(p)).digest("hex");
|
|
3373
|
+
return hash(a) === hash(b);
|
|
3236
3374
|
} catch {
|
|
3375
|
+
return !1;
|
|
3237
3376
|
}
|
|
3238
|
-
|
|
3377
|
+
}
|
|
3378
|
+
static fsyncFileSync(path) {
|
|
3379
|
+
let fd = openSync(path, "r+");
|
|
3380
|
+
try {
|
|
3381
|
+
fsyncSync(fd);
|
|
3382
|
+
} finally {
|
|
3383
|
+
closeSync(fd);
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
/** POSIX: make a rename in `dir` durable. Windows cannot open a directory for fsync. */
|
|
3387
|
+
static fsyncDirSync(dir) {
|
|
3388
|
+
if (process.platform !== "win32")
|
|
3389
|
+
try {
|
|
3390
|
+
let fd = openSync(dir, "r");
|
|
3391
|
+
try {
|
|
3392
|
+
fsyncSync(fd);
|
|
3393
|
+
} finally {
|
|
3394
|
+
closeSync(fd);
|
|
3395
|
+
}
|
|
3396
|
+
} catch {
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
/**
|
|
3400
|
+
* After a failure that followed a committed migration (the constructor):
|
|
3401
|
+
* put the file back as its pre-migration backup. The `.bak` is kept (it is
|
|
3402
|
+
* removed only after a fully successful open). Null when restored, else
|
|
3403
|
+
* why not (the caller says so in its error).
|
|
3404
|
+
*
|
|
3405
|
+
* Only when no other connection can hold the file: none in this process,
|
|
3406
|
+
* no -wal left beside it (SQLite removes it at the last close), and an
|
|
3407
|
+
* exclusive lock taken with busy_timeout 0. The copy is fsynced before the
|
|
3408
|
+
* rename. On POSIX the rename happens while that lock is held, so nothing
|
|
3409
|
+
* can open the old file in between (a connection that opened it earlier
|
|
3410
|
+
* would have held a lock). On Windows the lock's own handle would block the
|
|
3411
|
+
* rename, so the probe closes first: the rename itself then fails while any
|
|
3412
|
+
* other program has the file open.
|
|
3413
|
+
*
|
|
3414
|
+
* Known POSIX residual (tracker §6, W5-H): the probe closes after the
|
|
3415
|
+
* rename, and SQLite's close unlinks `<file>-wal` by path. A program that
|
|
3416
|
+
* opens the restored file and writes in that window can lose its WAL.
|
|
3417
|
+
* Closing first would drop the lock, and better-sqlite3 cannot keep the
|
|
3418
|
+
* WAL on close (no SQLITE_FCNTL_PERSIST_WAL), so the window is not closed.
|
|
3419
|
+
*/
|
|
3420
|
+
static restoreBackupSync(filePath) {
|
|
3421
|
+
let backupPath = filePath + ".bak";
|
|
3422
|
+
if (!existsSync2(backupPath)) return "there is no backup";
|
|
3423
|
+
let busy = "another connection has the file open";
|
|
3424
|
+
if (_AdfDatabase.openCounts.has(_AdfDatabase.canonicalKey(filePath)) || existsSync2(`${filePath}-wal`)) return busy;
|
|
3425
|
+
let restorePath = `${filePath}.restore`, probe = null;
|
|
3426
|
+
try {
|
|
3427
|
+
copyFileSync(backupPath, restorePath), _AdfDatabase.fsyncFileSync(restorePath);
|
|
3428
|
+
try {
|
|
3429
|
+
probe = new Database(filePath, { fileMustExist: !0 }), probe.pragma("busy_timeout = 0"), probe.pragma("locking_mode = EXCLUSIVE"), probe.exec("BEGIN IMMEDIATE; COMMIT");
|
|
3430
|
+
} catch {
|
|
3431
|
+
try {
|
|
3432
|
+
probe?.close();
|
|
3433
|
+
} catch {
|
|
3434
|
+
}
|
|
3435
|
+
probe = null;
|
|
3436
|
+
try {
|
|
3437
|
+
unlinkSync(restorePath);
|
|
3438
|
+
} catch {
|
|
3439
|
+
}
|
|
3440
|
+
return busy;
|
|
3441
|
+
}
|
|
3442
|
+
process.platform === "win32" && (probe.close(), probe = null);
|
|
3443
|
+
try {
|
|
3444
|
+
unlinkSync(`${filePath}-shm`);
|
|
3445
|
+
} catch {
|
|
3446
|
+
}
|
|
3447
|
+
return renameSync(restorePath, filePath), _AdfDatabase.fsyncDirSync(dirname2(filePath)), null;
|
|
3448
|
+
} catch (err2) {
|
|
3449
|
+
try {
|
|
3450
|
+
unlinkSync(restorePath);
|
|
3451
|
+
} catch {
|
|
3452
|
+
}
|
|
3453
|
+
let code = err2?.code;
|
|
3454
|
+
return code === "EPERM" || code === "EBUSY" || code === "EACCES" ? busy : `the restore failed (${err2 instanceof Error ? err2.message : String(err2)})`;
|
|
3455
|
+
} finally {
|
|
3456
|
+
try {
|
|
3457
|
+
probe?.close();
|
|
3458
|
+
} catch {
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
/**
|
|
3463
|
+
* A failed open(): roll back is the caller's; here the connection is
|
|
3464
|
+
* closed, the file is put back from the backup when `restore` (the failure
|
|
3465
|
+
* followed a committed migration), and the sidecars this open created are
|
|
3466
|
+
* reaped. Returns the sentence to add to the error when the file could not
|
|
3467
|
+
* be restored, else null.
|
|
3468
|
+
*/
|
|
3469
|
+
static abandonOpen(db, filePath, opts) {
|
|
3470
|
+
try {
|
|
3471
|
+
db.close();
|
|
3472
|
+
} catch {
|
|
3473
|
+
}
|
|
3474
|
+
let note = null;
|
|
3475
|
+
if (opts.backup && !opts.restore)
|
|
3476
|
+
console.error(`[AdfDatabase] Open failed; the migration was rolled back, the file is unchanged. Backup kept at: ${filePath}.bak`);
|
|
3477
|
+
else if (opts.restore) {
|
|
3478
|
+
let why = opts.backup ? opts.backup.complete ? _AdfDatabase.restoreBackupSync(filePath) : "the backup may lack writes another connection had not checkpointed" : "there is no backup";
|
|
3479
|
+
why === null ? console.error(`[AdfDatabase] Open failed after migrating; restored ${filePath} from its backup, kept at ${filePath}.bak`) : (note = `${basename(filePath)} was migrated and could not be put back from its backup because ${why}; the backup is kept at ${filePath}.bak`, console.error(`[AdfDatabase] ${note}`));
|
|
3480
|
+
}
|
|
3481
|
+
if (!opts.walExistedBeforeOpen)
|
|
3482
|
+
try {
|
|
3483
|
+
_AdfDatabase.reapSidecars(filePath);
|
|
3484
|
+
} catch {
|
|
3485
|
+
}
|
|
3486
|
+
return note;
|
|
3239
3487
|
}
|
|
3240
3488
|
/** Remove a transient .bak file after a successful destructive operation. */
|
|
3241
3489
|
static async removeBackup(filePath) {
|
|
@@ -3290,6 +3538,29 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3290
3538
|
* 'reaped' — WAL folded into the main file, sidecars removed
|
|
3291
3539
|
* 'error' — open/checkpoint/unlink failed; sidecars left in place
|
|
3292
3540
|
*/
|
|
3541
|
+
/**
|
|
3542
|
+
* True when no other connection holds `filePath`'s write lock: a probe
|
|
3543
|
+
* connection takes it with BEGIN IMMEDIATE (waiting at most
|
|
3544
|
+
* `busyTimeoutMs`) and gives it back at once. False on SQLITE_BUSY. Lets a
|
|
3545
|
+
* background job (the boot envelope sweep) pass over a file another
|
|
3546
|
+
* connection is writing instead of blocking in AdfWorkspace.open, whose
|
|
3547
|
+
* maintenance writes wait the full busy_timeout. Other errors throw.
|
|
3548
|
+
*/
|
|
3549
|
+
static writeLockFree(filePath, busyTimeoutMs) {
|
|
3550
|
+
let db = null;
|
|
3551
|
+
try {
|
|
3552
|
+
return db = new Database(filePath, { fileMustExist: !0 }), db.pragma(`busy_timeout = ${Math.max(0, Math.floor(busyTimeoutMs))}`), db.exec("BEGIN IMMEDIATE; ROLLBACK"), !0;
|
|
3553
|
+
} catch (err2) {
|
|
3554
|
+
let code = err2?.code;
|
|
3555
|
+
if (typeof code == "string" && code.startsWith("SQLITE_BUSY")) return !1;
|
|
3556
|
+
throw err2;
|
|
3557
|
+
} finally {
|
|
3558
|
+
try {
|
|
3559
|
+
db?.close();
|
|
3560
|
+
} catch {
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3293
3564
|
static reapSidecars(filePath) {
|
|
3294
3565
|
let walPath = `${filePath}-wal`, shmPath = `${filePath}-shm`;
|
|
3295
3566
|
if (!existsSync2(walPath) && !existsSync2(shmPath)) return "no-sidecars";
|
|
@@ -3376,7 +3647,40 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3376
3647
|
}
|
|
3377
3648
|
}
|
|
3378
3649
|
static open(filePath, options) {
|
|
3379
|
-
let walExistedBeforeOpen = existsSync2(`${filePath}-wal`) || existsSync2(`${filePath}-shm`)
|
|
3650
|
+
let walExistedBeforeOpen = existsSync2(`${filePath}-wal`) || existsSync2(`${filePath}-shm`);
|
|
3651
|
+
if (walExistedBeforeOpen) {
|
|
3652
|
+
let ro = null, refused = null;
|
|
3653
|
+
try {
|
|
3654
|
+
ro = new Database(filePath, { readonly: !0, fileMustExist: !0 }), refused = _AdfDatabase.refusedSchemaVersion(ro);
|
|
3655
|
+
} catch (err2) {
|
|
3656
|
+
if (_AdfDatabase.isBusyError(err2)) throw _AdfDatabase.lockedError(filePath);
|
|
3657
|
+
} finally {
|
|
3658
|
+
try {
|
|
3659
|
+
ro?.close();
|
|
3660
|
+
} catch {
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
if (refused !== null) throw new AdfSchemaTooOldError(filePath, refused);
|
|
3664
|
+
}
|
|
3665
|
+
let db = new Database(filePath), needsMigration = !1, backup = null, migrated = !1, repaired = !1;
|
|
3666
|
+
if (!walExistedBeforeOpen) {
|
|
3667
|
+
let refused = null;
|
|
3668
|
+
try {
|
|
3669
|
+
refused = _AdfDatabase.refusedSchemaVersion(db);
|
|
3670
|
+
} catch (err2) {
|
|
3671
|
+
if (_AdfDatabase.isBusyError(err2))
|
|
3672
|
+
throw db.close(), _AdfDatabase.lockedError(filePath);
|
|
3673
|
+
}
|
|
3674
|
+
if (refused !== null) {
|
|
3675
|
+
db.close();
|
|
3676
|
+
try {
|
|
3677
|
+
_AdfDatabase.reapSidecars(filePath);
|
|
3678
|
+
} catch {
|
|
3679
|
+
}
|
|
3680
|
+
throw new AdfSchemaTooOldError(filePath, refused);
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
let integrityMode = "full";
|
|
3380
3684
|
if (!options?.forceIntegrityCheck) {
|
|
3381
3685
|
let closedCleanly = !1;
|
|
3382
3686
|
try {
|
|
@@ -3392,7 +3696,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3392
3696
|
let checkPragma = integrityMode === "quick" ? "quick_check" : "integrity_check", status = (integrityMode === "none" ? [{ [checkPragma]: "ok" }] : db.pragma(checkPragma))[0]?.[checkPragma];
|
|
3393
3697
|
if (status !== "ok")
|
|
3394
3698
|
if (console.warn(`[AdfDatabase] Integrity check failed: ${status}`), db.close(), _AdfDatabase.tryRepair(filePath)) {
|
|
3395
|
-
if (db = new Database(filePath), db.pragma("integrity_check")[0]?.integrity_check !== "ok")
|
|
3699
|
+
if (db = new Database(filePath), repaired = !0, db.pragma("integrity_check")[0]?.integrity_check !== "ok")
|
|
3396
3700
|
throw db.close(), new Error(
|
|
3397
3701
|
`ADF file is corrupt and auto-repair failed. A backup was saved at: ${filePath}.corrupt`
|
|
3398
3702
|
);
|
|
@@ -3412,7 +3716,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3412
3716
|
throw _AdfDatabase.lockedError(filePath);
|
|
3413
3717
|
}
|
|
3414
3718
|
if (console.warn("[AdfDatabase] Integrity check threw:", error), db.close(), _AdfDatabase.tryRepair(filePath))
|
|
3415
|
-
db = new Database(filePath);
|
|
3719
|
+
db = new Database(filePath), repaired = !0;
|
|
3416
3720
|
else
|
|
3417
3721
|
throw new Error(
|
|
3418
3722
|
"ADF file is corrupt and could not be repaired. Please delete and recreate the file."
|
|
@@ -3425,16 +3729,22 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3425
3729
|
throw db.close(), new Error(
|
|
3426
3730
|
"This ADF file does not have the v0.2 schema. Please delete and recreate the file."
|
|
3427
3731
|
);
|
|
3732
|
+
let refused;
|
|
3733
|
+
try {
|
|
3734
|
+
refused = _AdfDatabase.refusedSchemaVersion(db);
|
|
3735
|
+
} catch (err2) {
|
|
3736
|
+
throw _AdfDatabase.isBusyError(err2) ? _AdfDatabase.lockedError(filePath) : new Error(
|
|
3737
|
+
`Cannot read the schema version of ${basename(filePath)}: ${err2 instanceof Error ? err2.message : String(err2)}. The file was not migrated.`,
|
|
3738
|
+
{ cause: err2 }
|
|
3739
|
+
);
|
|
3740
|
+
}
|
|
3741
|
+
if (refused !== null)
|
|
3742
|
+
throw new AdfSchemaTooOldError(filePath, refused, { unchanged: !walExistedBeforeOpen && !repaired });
|
|
3428
3743
|
try {
|
|
3429
3744
|
db.prepare("DELETE FROM adf_meta WHERE key = ?").run(CLEAN_CLOSE_META_KEY), db.pragma("wal_checkpoint(PASSIVE)");
|
|
3430
3745
|
} catch {
|
|
3431
3746
|
}
|
|
3432
|
-
let sv = (
|
|
3433
|
-
let r = db.prepare("SELECT value FROM adf_meta WHERE key = 'adf_schema_version'").get();
|
|
3434
|
-
if (r) return parseInt(r.value, 10);
|
|
3435
|
-
let r2 = db.prepare("SELECT value FROM adf_meta WHERE key = 'schema_version'").get();
|
|
3436
|
-
return r2 ? parseInt(r2.value, 10) : 0;
|
|
3437
|
-
})();
|
|
3747
|
+
let sv = _AdfDatabase.readSchemaVersion(db) ?? 0;
|
|
3438
3748
|
if (needsMigration = sv < ADF_LATEST_SCHEMA_VERSION, !needsMigration) {
|
|
3439
3749
|
if (sv > ADF_LATEST_SCHEMA_VERSION) {
|
|
3440
3750
|
let msg = `[AdfDatabase] Schema version ${sv} is newer than this runtime's v${ADF_LATEST_SCHEMA_VERSION} for ${filePath}. Opening without migration; writes from an older runtime may not maintain newer invariants.`;
|
|
@@ -3450,11 +3760,13 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3450
3760
|
}
|
|
3451
3761
|
if (needsMigration)
|
|
3452
3762
|
try {
|
|
3453
|
-
_AdfDatabase.backupBeforeDestructiveSync(db, filePath)
|
|
3763
|
+
let made = _AdfDatabase.backupBeforeDestructiveSync(db, filePath);
|
|
3764
|
+
backup = made, console.log(made.reused ? `[AdfDatabase] Backup before migration (v${sv} \u2192 v${ADF_LATEST_SCHEMA_VERSION}) already on disk, unchanged: ${filePath}.bak` : `[AdfDatabase] Backup created before migration (v${sv} \u2192 v${ADF_LATEST_SCHEMA_VERSION}): ${filePath}.bak`);
|
|
3454
3765
|
} catch (e) {
|
|
3455
3766
|
console.warn("[AdfDatabase] Could not create pre-migration backup:", e);
|
|
3456
3767
|
}
|
|
3457
|
-
|
|
3768
|
+
db.exec("BEGIN IMMEDIATE");
|
|
3769
|
+
let unversionedClone = sv === 0, cols = db.prepare("PRAGMA table_info(adf_loop)").all();
|
|
3458
3770
|
new Set(cols.map((c) => c.name)).has("created_at") || (db.exec("ALTER TABLE adf_loop ADD COLUMN model TEXT"), db.exec("ALTER TABLE adf_loop ADD COLUMN tokens INTEGER"), db.exec("ALTER TABLE adf_loop ADD COLUMN created_at INTEGER NOT NULL DEFAULT 0")), db.exec("DROP TABLE IF EXISTS adf_archive"), db.prepare(
|
|
3459
3771
|
"SELECT name FROM sqlite_master WHERE type='table' AND name='adf_audit'"
|
|
3460
3772
|
).get() || db.exec(`
|
|
@@ -3972,11 +4284,24 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3972
4284
|
}
|
|
3973
4285
|
} catch {
|
|
3974
4286
|
}
|
|
3975
|
-
|
|
4287
|
+
if (!db.inTransaction) throw new Error(`The migration of ${basename(filePath)} was rolled back by SQLite.`);
|
|
4288
|
+
unversionedClone && (db.prepare("INSERT INTO adf_meta (key, value, protection) VALUES ('adf_schema_version', ?, 'readonly') ON CONFLICT(key) DO UPDATE SET value = excluded.value, protection = 'readonly'").run(String(ADF_LATEST_SCHEMA_VERSION)), console.log(`[AdfDatabase] Stamped schema v${ADF_LATEST_SCHEMA_VERSION} on an unversioned clone: ${filePath}`)), db.exec("COMMIT"), migrated = !0;
|
|
3976
4289
|
} catch (error) {
|
|
3977
|
-
|
|
4290
|
+
if (db.inTransaction)
|
|
4291
|
+
try {
|
|
4292
|
+
db.exec("ROLLBACK");
|
|
4293
|
+
} catch {
|
|
4294
|
+
}
|
|
4295
|
+
throw _AdfDatabase.abandonOpen(db, filePath, { backup, restore: !1, walExistedBeforeOpen }), error;
|
|
3978
4296
|
}
|
|
3979
|
-
|
|
4297
|
+
let opened;
|
|
4298
|
+
try {
|
|
4299
|
+
opened = new _AdfDatabase(db, filePath);
|
|
4300
|
+
} catch (error) {
|
|
4301
|
+
let note = _AdfDatabase.abandonOpen(db, filePath, { backup, restore: migrated, walExistedBeforeOpen });
|
|
4302
|
+
throw note ? new Error(`${error instanceof Error ? error.message : String(error)}. ${note}`, { cause: error }) : error;
|
|
4303
|
+
}
|
|
4304
|
+
return backup && _AdfDatabase.removeBackupSync(filePath), opened;
|
|
3980
4305
|
}
|
|
3981
4306
|
/**
|
|
3982
4307
|
* Create a new ADF without replacing an existing destination.
|
|
@@ -4048,7 +4373,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4048
4373
|
let overrideMap = new Map(options.tools.map((t) => [t.name, t]));
|
|
4049
4374
|
tools = tools.map((t) => overrideMap.has(t.name) ? { ...t, ...overrideMap.get(t.name) } : t);
|
|
4050
4375
|
for (let t of options.tools)
|
|
4051
|
-
tools.some((dt) => dt.name === t.name) || tools.push(t);
|
|
4376
|
+
tools.some((dt) => dt.name === t.name) || tools.push({ visible: !0, ...t });
|
|
4052
4377
|
}
|
|
4053
4378
|
let mergedTriggers = { ...base.triggers };
|
|
4054
4379
|
if (options.triggers)
|
|
@@ -4075,7 +4400,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4075
4400
|
context: {
|
|
4076
4401
|
...base.context,
|
|
4077
4402
|
...options.context,
|
|
4078
|
-
audit: { ...base.context.audit, ...options.context?.audit },
|
|
4403
|
+
audit: { ...AUDIT_DEFAULTS, ...base.context.audit, ...options.context?.audit },
|
|
4079
4404
|
dynamic_instructions: { ...base.context.dynamic_instructions, ...options.context?.dynamic_instructions }
|
|
4080
4405
|
},
|
|
4081
4406
|
tools,
|
|
@@ -4160,10 +4485,10 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4160
4485
|
* crash leftovers owned by the sweeps — left untouched here. Errors from
|
|
4161
4486
|
* `fn` (and the open) propagate to the caller's existing catch.
|
|
4162
4487
|
*/
|
|
4163
|
-
static peekReadonly(filePath, fn) {
|
|
4488
|
+
static peekReadonly(filePath, fn, busyTimeoutMs) {
|
|
4164
4489
|
let hadSidecars = existsSync2(`${filePath}-wal`) || existsSync2(`${filePath}-shm`), db = null;
|
|
4165
4490
|
try {
|
|
4166
|
-
return db = new Database(filePath, { readonly: !0 }), fn(db);
|
|
4491
|
+
return db = new Database(filePath, { readonly: !0 }), busyTimeoutMs !== void 0 && db.pragma(`busy_timeout = ${Math.max(0, Math.floor(busyTimeoutMs))}`), fn(db);
|
|
4167
4492
|
} finally {
|
|
4168
4493
|
try {
|
|
4169
4494
|
db?.close();
|
|
@@ -4198,6 +4523,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4198
4523
|
throw new Error(
|
|
4199
4524
|
"Snapshot timed out: the agent is being written to continuously \u2014 stop it and try again"
|
|
4200
4525
|
);
|
|
4526
|
+
return 100;
|
|
4201
4527
|
}
|
|
4202
4528
|
});
|
|
4203
4529
|
} finally {
|
|
@@ -4363,6 +4689,23 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4363
4689
|
return null;
|
|
4364
4690
|
}
|
|
4365
4691
|
}
|
|
4692
|
+
/**
|
|
4693
|
+
* What the boot envelope sweep decides on before opening a file, read
|
|
4694
|
+
* read-only: its schema version (null without an adf_meta table, 0 with no
|
|
4695
|
+
* version) and its agent id (null without a config). Errors propagate;
|
|
4696
|
+
* `busyTimeoutMs` bounds the wait for a lock (the sweep's 25 ms).
|
|
4697
|
+
*/
|
|
4698
|
+
static peekSweepStatus(filePath, busyTimeoutMs) {
|
|
4699
|
+
return _AdfDatabase.peekReadonly(filePath, (db) => {
|
|
4700
|
+
let schemaVersion = _AdfDatabase.readSchemaVersion(db), row = !!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='adf_config'").get() ? db.prepare("SELECT config_json FROM adf_config WHERE id = 1").get() : void 0, agentId = null;
|
|
4701
|
+
try {
|
|
4702
|
+
let id = row ? JSON.parse(row.config_json).id : void 0;
|
|
4703
|
+
typeof id == "string" && id && (agentId = id);
|
|
4704
|
+
} catch {
|
|
4705
|
+
}
|
|
4706
|
+
return { schemaVersion, agentId };
|
|
4707
|
+
}, busyTimeoutMs);
|
|
4708
|
+
}
|
|
4366
4709
|
static peekBootStatusDetailed(filePath) {
|
|
4367
4710
|
try {
|
|
4368
4711
|
return _AdfDatabase.peekReadonly(filePath, (db) => {
|
|
@@ -4528,8 +4871,8 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4528
4871
|
static peekIdentityPurposes(filePath, prefix) {
|
|
4529
4872
|
try {
|
|
4530
4873
|
return _AdfDatabase.peekReadonly(filePath, (db) => db.prepare(
|
|
4531
|
-
"SELECT purpose FROM adf_identity WHERE purpose
|
|
4532
|
-
).all(prefix).map((r) => r.purpose));
|
|
4874
|
+
"SELECT purpose FROM adf_identity WHERE substr(purpose, 1, length(?)) = ?"
|
|
4875
|
+
).all(prefix, prefix).map((r) => r.purpose));
|
|
4533
4876
|
} catch {
|
|
4534
4877
|
return [];
|
|
4535
4878
|
}
|
|
@@ -4584,6 +4927,8 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4584
4927
|
"INSERT INTO adf_loop (seq, role, content_json, model, tokens, created_at, ord, loop) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
4585
4928
|
), this.stmts.clearLoop = this.db.prepare("DELETE FROM adf_loop WHERE loop = ?"), this.stmts.getLoopCount = this.db.prepare("SELECT COUNT(*) as count FROM adf_loop WHERE loop = ?"), this.stmts.getLastAssistantTokens = this.db.prepare(
|
|
4586
4929
|
"SELECT tokens FROM adf_loop WHERE loop = ? AND role = 'assistant' AND tokens IS NOT NULL ORDER BY COALESCE(ord, seq) DESC, seq DESC LIMIT 1"
|
|
4930
|
+
), this.stmts.getAssistantTokenRows = this.db.prepare(
|
|
4931
|
+
"SELECT seq, tokens FROM adf_loop WHERE loop = ? AND role = 'assistant' AND tokens IS NOT NULL ORDER BY COALESCE(ord, seq), seq"
|
|
4587
4932
|
), this.stmts.getInboxMessages = this.db.prepare(
|
|
4588
4933
|
"SELECT * FROM adf_inbox ORDER BY received_at DESC"
|
|
4589
4934
|
), this.stmts.getInboxByStatus = this.db.prepare(
|
|
@@ -4666,7 +5011,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4666
5011
|
), this.stmts.deleteIdentity = this.db.prepare(
|
|
4667
5012
|
"DELETE FROM adf_identity WHERE purpose = ?"
|
|
4668
5013
|
), this.stmts.listIdentityByPrefix = this.db.prepare(
|
|
4669
|
-
"SELECT purpose FROM adf_identity WHERE purpose
|
|
5014
|
+
"SELECT purpose FROM adf_identity WHERE substr(purpose, 1, length(?)) = ?"
|
|
4670
5015
|
), this.stmts.getIdentityFull = this.db.prepare(
|
|
4671
5016
|
"SELECT purpose, value, encryption_algo, salt, kdf_params, code_access FROM adf_identity WHERE purpose = ?"
|
|
4672
5017
|
), this.stmts.getAllIdentityFull = this.db.prepare(
|
|
@@ -4703,6 +5048,8 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4703
5048
|
"UPDATE adf_tasks SET requires_authorization = 1 WHERE id = ?"
|
|
4704
5049
|
), this.stmts.setTaskExecutorManaged = this.db.prepare(
|
|
4705
5050
|
"UPDATE adf_tasks SET executor_managed = 1 WHERE id = ?"
|
|
5051
|
+
), this.stmts.setTaskApprovalMeta = this.db.prepare(
|
|
5052
|
+
"UPDATE adf_tasks SET approval_meta = ? WHERE id = ?"
|
|
4706
5053
|
), this.stmts.updateTaskArgs = this.db.prepare(
|
|
4707
5054
|
"UPDATE adf_tasks SET args = ? WHERE id = ?"
|
|
4708
5055
|
), this.stmts.getTasksByStatus = this.db.prepare(
|
|
@@ -4781,11 +5128,16 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4781
5128
|
deleteIdentity(purpose) {
|
|
4782
5129
|
return this.stmts.deleteIdentity.run(purpose).changes > 0;
|
|
4783
5130
|
}
|
|
5131
|
+
/**
|
|
5132
|
+
* Prefix matches are exact and case-sensitive (substr, not LIKE): with LIKE,
|
|
5133
|
+
* `_` and `%` in a server or adapter name were wildcards and ASCII case was
|
|
5134
|
+
* ignored, so detaching one server deleted another's credentials.
|
|
5135
|
+
*/
|
|
4784
5136
|
deleteIdentityByPrefix(prefix) {
|
|
4785
|
-
return this.db.prepare("DELETE FROM adf_identity WHERE purpose
|
|
5137
|
+
return this.db.prepare("DELETE FROM adf_identity WHERE substr(purpose, 1, length(?)) = ?").run(prefix, prefix).changes;
|
|
4786
5138
|
}
|
|
4787
5139
|
listIdentityPurposes(prefix) {
|
|
4788
|
-
return prefix ? this.stmts.listIdentityByPrefix.all(prefix).map((r) => r.purpose) : this.stmts.listAllIdentityPurposes.all().map((r) => r.purpose);
|
|
5140
|
+
return prefix ? this.stmts.listIdentityByPrefix.all(prefix, prefix).map((r) => r.purpose) : this.stmts.listAllIdentityPurposes.all().map((r) => r.purpose);
|
|
4789
5141
|
}
|
|
4790
5142
|
// --- Encrypted identity methods ---
|
|
4791
5143
|
/**
|
|
@@ -4909,9 +5261,27 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4909
5261
|
}
|
|
4910
5262
|
return added && this.setConfig(config), config;
|
|
4911
5263
|
}
|
|
5264
|
+
/**
|
|
5265
|
+
* setConfig calls through this connection: with dataVersion(), a cheap "did
|
|
5266
|
+
* the config change" check for callers that cache it (RuntimeService's
|
|
5267
|
+
* running-agent config).
|
|
5268
|
+
*/
|
|
5269
|
+
configWrites = 0;
|
|
5270
|
+
dataVersionStmt = null;
|
|
5271
|
+
configWriteCount() {
|
|
5272
|
+
return this.configWrites;
|
|
5273
|
+
}
|
|
5274
|
+
/**
|
|
5275
|
+
* SQLite's `PRAGMA data_version`: changes when another connection (another
|
|
5276
|
+
* process, or another handle in this one) commits to the file; this
|
|
5277
|
+
* connection's own writes leave it unchanged (configWriteCount counts those).
|
|
5278
|
+
*/
|
|
5279
|
+
dataVersion() {
|
|
5280
|
+
return this.dataVersionStmt ??= this.db.prepare("PRAGMA data_version").pluck(), this.dataVersionStmt.get();
|
|
5281
|
+
}
|
|
4912
5282
|
setConfig(config) {
|
|
4913
5283
|
let now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4914
|
-
config.metadata.updated_at = now, this.stmts.setConfig.run(JSON.stringify(config), now), this.setMeta("adf_name", config.name), config.handle && this.setMeta("adf_handle", config.handle), this.setMeta("adf_updated_at", now);
|
|
5284
|
+
config.metadata.updated_at = now, this.stmts.setConfig.run(JSON.stringify(config), now), this.configWrites++, this.setMeta("adf_name", config.name), config.handle && this.setMeta("adf_handle", config.handle), this.setMeta("adf_updated_at", now);
|
|
4915
5285
|
}
|
|
4916
5286
|
// ===========================================================================
|
|
4917
5287
|
// Loop Table
|
|
@@ -4970,7 +5340,25 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4970
5340
|
opts?.ord ?? null,
|
|
4971
5341
|
loop
|
|
4972
5342
|
);
|
|
4973
|
-
return this.bumpLoopRevision(loop), Number(result.lastInsertRowid);
|
|
5343
|
+
return this.bumpLoopRevision(loop), content.some((block) => block.type === "file_ref") && this.getFileRefRegistry().noteContent(content), Number(result.lastInsertRowid);
|
|
5344
|
+
}
|
|
5345
|
+
/** File-reference state of this file, shared by every connection to it (see file-ref-registry.ts). */
|
|
5346
|
+
getFileRefRegistry() {
|
|
5347
|
+
return fileRefRegistry(this, this.filePath === ":memory:" ? null : _AdfDatabase.canonicalKey(this.filePath));
|
|
5348
|
+
}
|
|
5349
|
+
/**
|
|
5350
|
+
* Rows of every loop that hold a `file_ref` block (a prefilter: the caller
|
|
5351
|
+
* parses and checks). One scan of adf_loop; callers run it only when a
|
|
5352
|
+
* referenced file is renamed or deleted.
|
|
5353
|
+
*/
|
|
5354
|
+
getLoopRowsWithFileRefs() {
|
|
5355
|
+
return this.db.prepare(
|
|
5356
|
+
`SELECT seq, loop, content_json FROM adf_loop WHERE instr(content_json, '"file_ref"') > 0`
|
|
5357
|
+
).all();
|
|
5358
|
+
}
|
|
5359
|
+
/** Replace one row's content in place (file references rewritten on rename/delete). */
|
|
5360
|
+
updateLoopContent(loop, seq, content) {
|
|
5361
|
+
this.db.prepare("UPDATE adf_loop SET content_json = ? WHERE seq = ? AND loop = ?").run(JSON.stringify(content), seq, loop), this.bumpLoopRevision(loop);
|
|
4974
5362
|
}
|
|
4975
5363
|
/** Wipe ONE stream. The whole-table wipe is clearAllLoops(). */
|
|
4976
5364
|
clearLoop(loop) {
|
|
@@ -4999,6 +5387,15 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4999
5387
|
let row = this.stmts.getLastAssistantTokens.get(loop);
|
|
5000
5388
|
return _AdfDatabase.parseLoopTokens(row?.tokens);
|
|
5001
5389
|
}
|
|
5390
|
+
/** Every assistant row's usage record, in loop order (oldest first). */
|
|
5391
|
+
getAssistantTokenRows(loop) {
|
|
5392
|
+
let rows = this.stmts.getAssistantTokenRows.all(loop), out = [];
|
|
5393
|
+
for (let row of rows) {
|
|
5394
|
+
let tokens = _AdfDatabase.parseLoopTokens(row.tokens);
|
|
5395
|
+
tokens && out.push({ seq: row.seq, tokens });
|
|
5396
|
+
}
|
|
5397
|
+
return out;
|
|
5398
|
+
}
|
|
5002
5399
|
/** Loop rows in display order: each row's seq plus its ordering key
|
|
5003
5400
|
* (COALESCE(ord, seq)). Positional operations resolve boundaries against
|
|
5004
5401
|
* ordKey, never raw seq — an ord'd summary sorts at its override position. */
|
|
@@ -5086,6 +5483,24 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
5086
5483
|
transaction(fn) {
|
|
5087
5484
|
return this.db.transaction(fn)();
|
|
5088
5485
|
}
|
|
5486
|
+
/**
|
|
5487
|
+
* `fn` in a BEGIN IMMEDIATE transaction: the file's write lock is taken
|
|
5488
|
+
* before `fn` reads, so a check-then-write in it is atomic against every
|
|
5489
|
+
* other connection (another process provisioning the same file). Inside a
|
|
5490
|
+
* transaction already, a savepoint. `busyTimeoutMs`: how long BEGIN waits
|
|
5491
|
+
* for another connection's write lock before SQLITE_BUSY (the connection's
|
|
5492
|
+
* own busy_timeout, 5 s, otherwise).
|
|
5493
|
+
*/
|
|
5494
|
+
immediateTransaction(fn, busyTimeoutMs) {
|
|
5495
|
+
if (busyTimeoutMs === void 0 || this.db.inTransaction) return this.db.transaction(fn).immediate();
|
|
5496
|
+
let before = this.db.pragma("busy_timeout", { simple: !0 });
|
|
5497
|
+
this.db.pragma(`busy_timeout = ${Math.max(0, Math.floor(busyTimeoutMs))}`);
|
|
5498
|
+
try {
|
|
5499
|
+
return this.db.transaction(fn).immediate();
|
|
5500
|
+
} finally {
|
|
5501
|
+
this.db.pragma(`busy_timeout = ${before}`);
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5089
5504
|
// ===========================================================================
|
|
5090
5505
|
// Inbox
|
|
5091
5506
|
// ===========================================================================
|
|
@@ -5482,6 +5897,9 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
5482
5897
|
setTaskExecutorManaged(id, _value) {
|
|
5483
5898
|
this.stmts.setTaskExecutorManaged.run(id);
|
|
5484
5899
|
}
|
|
5900
|
+
setTaskApprovalMeta(id, approvalMeta) {
|
|
5901
|
+
this.stmts.setTaskApprovalMeta.run(approvalMeta, id);
|
|
5902
|
+
}
|
|
5485
5903
|
updateTaskArgs(id, args) {
|
|
5486
5904
|
this.stmts.updateTaskArgs.run(args, id);
|
|
5487
5905
|
}
|
|
@@ -5624,7 +6042,9 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
5624
6042
|
}
|
|
5625
6043
|
close() {
|
|
5626
6044
|
if (this.closed) return;
|
|
5627
|
-
|
|
6045
|
+
this.closed = !0;
|
|
6046
|
+
let remaining = _AdfDatabase.decrementOpen(this.filePath);
|
|
6047
|
+
if (remaining === 0 && this.getFileRefRegistry().media.clear(), remaining === 0)
|
|
5628
6048
|
try {
|
|
5629
6049
|
this.db.prepare(
|
|
5630
6050
|
"INSERT INTO adf_meta (key, value, protection) VALUES (?, ?, 'readonly') ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
@@ -6145,6 +6565,14 @@ var CODE_FORBIDDEN_WRITE_RE = /^crypto:/;
|
|
|
6145
6565
|
function isCodeForbiddenIdentityWrite(purpose) {
|
|
6146
6566
|
return CODE_FORBIDDEN_WRITE_RE.test(purpose);
|
|
6147
6567
|
}
|
|
6568
|
+
function parseLoopContent(json) {
|
|
6569
|
+
try {
|
|
6570
|
+
let parsed = JSON.parse(json);
|
|
6571
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
6572
|
+
} catch {
|
|
6573
|
+
return [];
|
|
6574
|
+
}
|
|
6575
|
+
}
|
|
6148
6576
|
var AdfWorkspace = class _AdfWorkspace {
|
|
6149
6577
|
db;
|
|
6150
6578
|
filePath;
|
|
@@ -6812,6 +7240,14 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
6812
7240
|
getAgentConfig() {
|
|
6813
7241
|
return this.db.getConfig();
|
|
6814
7242
|
}
|
|
7243
|
+
/** Config writes through this file's connection so far (AdfDatabase.configWriteCount); no SQLite read. */
|
|
7244
|
+
configWriteCount() {
|
|
7245
|
+
return this.db.configWriteCount();
|
|
7246
|
+
}
|
|
7247
|
+
/** Commits by other connections to this file (AdfDatabase.dataVersion); no table read. */
|
|
7248
|
+
dataVersion() {
|
|
7249
|
+
return this.db.dataVersion();
|
|
7250
|
+
}
|
|
6815
7251
|
setAgentConfig(config) {
|
|
6816
7252
|
let previous = null;
|
|
6817
7253
|
try {
|
|
@@ -7151,6 +7587,10 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7151
7587
|
getLastAssistantTokens() {
|
|
7152
7588
|
return this.db.getLastAssistantTokens(this.boundLoop);
|
|
7153
7589
|
}
|
|
7590
|
+
/** Every assistant row's usage record in loop order — see AdfDatabase.getAssistantTokenRows. */
|
|
7591
|
+
getAssistantTokenRows() {
|
|
7592
|
+
return this.db.getAssistantTokenRows(this.boundLoop);
|
|
7593
|
+
}
|
|
7154
7594
|
/** Meta key of this loop's context baseline: bare for main, `:<loop>`-suffixed
|
|
7155
7595
|
* for side loops (one baseline per cognition stream). */
|
|
7156
7596
|
contextBaselineKey() {
|
|
@@ -7522,12 +7962,12 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7522
7962
|
let deleted2 = !1;
|
|
7523
7963
|
return this.db.transaction(() => {
|
|
7524
7964
|
archive && this.db.insertAudit("file", { ref: relativePath, entryCount: 1, sizeBytes: archive.json.length, data: archive.data }), opts?.force && entry && this.db.setFileProtection(relativePath, "none"), deleted2 = this.db.deleteFile(relativePath);
|
|
7525
|
-
}), deleted2 && (this.emitUmbilical("file.deleted", { path: relativePath }), this.emitFileChange(relativePath, "deleted", void 0, entry?.content, metadata)), deleted2;
|
|
7965
|
+
}), deleted2 && (this.emitUmbilical("file.deleted", { path: relativePath }), this.emitFileChange(relativePath, "deleted", void 0, entry?.content, metadata), this.rewriteFileRefs([{ op: "delete", path: relativePath }])), deleted2;
|
|
7526
7966
|
}
|
|
7527
7967
|
let previous = this.onFileChangeCallback ? this.db.readFile(relativePath)?.content : void 0;
|
|
7528
7968
|
opts?.force && this.db.fileExists(relativePath) && this.db.setFileProtection(relativePath, "none");
|
|
7529
7969
|
let deleted = this.db.deleteFile(relativePath);
|
|
7530
|
-
return deleted && (this.emitUmbilical("file.deleted", { path: relativePath }), this.emitFileChange(relativePath, "deleted", void 0, previous, metadata)), deleted;
|
|
7970
|
+
return deleted && (this.emitUmbilical("file.deleted", { path: relativePath }), this.emitFileChange(relativePath, "deleted", void 0, previous, metadata), this.rewriteFileRefs([{ op: "delete", path: relativePath }])), deleted;
|
|
7531
7971
|
}
|
|
7532
7972
|
getFileMeta(path) {
|
|
7533
7973
|
return this.db.getFileMeta(path);
|
|
@@ -7547,7 +7987,10 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7547
7987
|
return this.db.fileExists(relativePath);
|
|
7548
7988
|
}
|
|
7549
7989
|
renameInternalFile(oldPath, newPath) {
|
|
7550
|
-
let previous = this.onFileChangeCallback ? this.db.readFile(oldPath)?.content : void 0, metadata = this.fileChangeMeta(oldPath), renamed = this.db.
|
|
7990
|
+
let previous = this.onFileChangeCallback ? this.db.readFile(oldPath)?.content : void 0, metadata = this.fileChangeMeta(oldPath), renamed = this.db.transaction(() => {
|
|
7991
|
+
let done = this.db.renameFile(oldPath, newPath);
|
|
7992
|
+
return done && this.rewriteFileRefs([{ op: "rename", from: oldPath, to: newPath }]), done;
|
|
7993
|
+
});
|
|
7551
7994
|
return renamed && (this.emitFileChange(oldPath, "deleted", void 0, previous, metadata), this.emitFileChange(newPath, "created", previous, void 0, this.fileChangeMeta(newPath))), renamed;
|
|
7552
7995
|
}
|
|
7553
7996
|
renameFolder(oldPrefix, newPrefix) {
|
|
@@ -7555,14 +7998,15 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7555
7998
|
path: file.path,
|
|
7556
7999
|
content: notify ? this.db.readFile(file.path)?.content : void 0,
|
|
7557
8000
|
metadata: notify ? this.getFileMeta(file.path) : null
|
|
7558
|
-
})), count = this.db.
|
|
7559
|
-
|
|
7560
|
-
|
|
8001
|
+
})), replacementPrefix = newPrefix.endsWith("/") ? newPrefix : `${newPrefix}/`, count = this.db.transaction(() => {
|
|
8002
|
+
let changed = this.db.renameFolder(oldPrefix, newPrefix);
|
|
8003
|
+
return changed > 0 && this.rewriteFileRefs(moved.map((file) => ({ op: "rename", from: file.path, to: replacementPrefix + file.path.slice(prefix.length) }))), changed;
|
|
8004
|
+
});
|
|
8005
|
+
if (count > 0)
|
|
7561
8006
|
for (let file of moved) {
|
|
7562
8007
|
let newPath = replacementPrefix + file.path.slice(prefix.length);
|
|
7563
8008
|
this.emitFileChange(file.path, "deleted", void 0, file.content, file.metadata), this.emitFileChange(newPath, "created", file.content, void 0, this.fileChangeMeta(newPath));
|
|
7564
8009
|
}
|
|
7565
|
-
}
|
|
7566
8010
|
return count;
|
|
7567
8011
|
}
|
|
7568
8012
|
// The sinks are per-agent, not per-loop: registering through a forLoop() view
|
|
@@ -7597,8 +8041,38 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7597
8041
|
} catch {
|
|
7598
8042
|
}
|
|
7599
8043
|
}
|
|
8044
|
+
/**
|
|
8045
|
+
* Keep file references (`file_ref` blocks) pointing at the right file: a
|
|
8046
|
+
* rename or move rewrites the path, a delete leaves a tombstone. Rewrites
|
|
8047
|
+
* every loop row that names the file, then every live session's copy. Only
|
|
8048
|
+
* files some row references cost a scan; the set of referenced paths is
|
|
8049
|
+
* built once per process and kept current by loop writes.
|
|
8050
|
+
*/
|
|
8051
|
+
rewriteFileRefs(changes) {
|
|
8052
|
+
if (changes.length === 0) return;
|
|
8053
|
+
let registry3 = this.db.getFileRefRegistry();
|
|
8054
|
+
try {
|
|
8055
|
+
if (!registry3.referenced) {
|
|
8056
|
+
let referenced = /* @__PURE__ */ new Set();
|
|
8057
|
+
for (let row of this.db.getLoopRowsWithFileRefs())
|
|
8058
|
+
for (let block of parseLoopContent(row.content_json))
|
|
8059
|
+
isFileRefBlock(block) && !block.file_ref.deleted && referenced.add(block.file_ref.path);
|
|
8060
|
+
registry3.referenced = referenced;
|
|
8061
|
+
}
|
|
8062
|
+
if (!registry3.affects(changes)) return;
|
|
8063
|
+
this.db.transaction(() => {
|
|
8064
|
+
for (let row of this.db.getLoopRowsWithFileRefs()) {
|
|
8065
|
+
let content = parseLoopContent(row.content_json), next = applyFileRefChanges(content, changes);
|
|
8066
|
+
next !== content && this.db.updateLoopContent(row.loop, row.seq, next);
|
|
8067
|
+
}
|
|
8068
|
+
}), registry3.noteChanges(changes);
|
|
8069
|
+
} catch (error) {
|
|
8070
|
+
console.error("[AdfWorkspace] Could not update file references:", error);
|
|
8071
|
+
}
|
|
8072
|
+
registry3.notifyHolders(changes);
|
|
8073
|
+
}
|
|
7600
8074
|
emitFileChange(path, operation, content, previousContent, metadata) {
|
|
7601
|
-
if (this.skillIndexer.notifyPath(path), !this.onFileChangeCallback) return;
|
|
8075
|
+
if (this.db.getFileRefRegistry().invalidate(path), this.skillIndexer.notifyPath(path), this.emitDataChange("files"), !this.onFileChangeCallback) return;
|
|
7602
8076
|
let mimeType = metadata?.mime_type ?? this.getMimeType(path);
|
|
7603
8077
|
this.onFileChangeCallback({
|
|
7604
8078
|
path,
|
|
@@ -7738,6 +8212,9 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7738
8212
|
setTaskExecutorManaged(id, value) {
|
|
7739
8213
|
this.db.setTaskExecutorManaged(id, value);
|
|
7740
8214
|
}
|
|
8215
|
+
setTaskApprovalMeta(id, approvalMeta) {
|
|
8216
|
+
this.db.setTaskApprovalMeta(id, approvalMeta);
|
|
8217
|
+
}
|
|
7741
8218
|
updateTaskArgs(id, args) {
|
|
7742
8219
|
this.db.updateTaskArgs(id, args);
|
|
7743
8220
|
}
|
|
@@ -7884,8 +8361,39 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7884
8361
|
transaction(fn) {
|
|
7885
8362
|
return this.db.transaction(fn);
|
|
7886
8363
|
}
|
|
8364
|
+
/** AdfDatabase.immediateTransaction: an atomic check-then-write against other connections to the file. */
|
|
8365
|
+
immediateTransaction(fn, busyTimeoutMs) {
|
|
8366
|
+
return this.db.immediateTransaction(fn, busyTimeoutMs);
|
|
8367
|
+
}
|
|
7887
8368
|
};
|
|
7888
8369
|
|
|
8370
|
+
// src/main/runtime/loop-usage.ts
|
|
8371
|
+
function summarizeLoopUsage(entries) {
|
|
8372
|
+
let totals = createUsageTotals(), byModel = /* @__PURE__ */ new Map(), usageRows = 0, estimatedRows = 0;
|
|
8373
|
+
for (let entry of entries) {
|
|
8374
|
+
if (!entry.tokens) continue;
|
|
8375
|
+
usageRows++, hasReportedUsage(entry.tokens) || estimatedRows++, addUsage(totals, entry.tokens);
|
|
8376
|
+
let model = entry.model ?? "unknown", bucket = byModel.get(model);
|
|
8377
|
+
bucket || (bucket = { model, rows: 0, ...createUsageTotals() }, byModel.set(model, bucket)), bucket.rows++, addUsage(bucket, entry.tokens);
|
|
8378
|
+
}
|
|
8379
|
+
return {
|
|
8380
|
+
loopRows: entries.length,
|
|
8381
|
+
usageRows,
|
|
8382
|
+
estimatedRows,
|
|
8383
|
+
totals,
|
|
8384
|
+
byModel: Array.from(byModel.values()).sort((a, b) => b.total - a.total)
|
|
8385
|
+
};
|
|
8386
|
+
}
|
|
8387
|
+
function createUsageTotals() {
|
|
8388
|
+
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
8389
|
+
}
|
|
8390
|
+
function addUsage(target, usage) {
|
|
8391
|
+
target.input += usage.input ?? 0, target.output += usage.output ?? 0, target.cacheRead += usage.cache_read ?? 0, target.cacheWrite += usage.cache_write ?? 0, target.total = target.input + target.output;
|
|
8392
|
+
}
|
|
8393
|
+
function hasReportedUsage(t) {
|
|
8394
|
+
return t.estimated ? !1 : t.cache_read !== void 0 || t.cache_write !== void 0 || t.reasoning !== void 0 || t.cost_usd !== void 0;
|
|
8395
|
+
}
|
|
8396
|
+
|
|
7889
8397
|
// src/main/tools/shell/commands/status.ts
|
|
7890
8398
|
function staticArgValue3(arg) {
|
|
7891
8399
|
if (!arg) return null;
|
|
@@ -8062,7 +8570,7 @@ async function configTools(ctx) {
|
|
|
8062
8570
|
var statusHandler = {
|
|
8063
8571
|
name: "status",
|
|
8064
8572
|
summary: "Show agent status summary",
|
|
8065
|
-
helpText: "status State, token usage
|
|
8573
|
+
helpText: "status State, model, token usage in the current loop",
|
|
8066
8574
|
category: "identity",
|
|
8067
8575
|
resolvedTools: ["sys_get_config"],
|
|
8068
8576
|
async execute(ctx) {
|
|
@@ -8072,7 +8580,7 @@ var statusHandler = {
|
|
|
8072
8580
|
`Model: ${config.model?.provider ?? ""}/${config.model?.model_id ?? ""}`,
|
|
8073
8581
|
`Autonomy: ${config.autonomous ? "enabled" : "disabled"}`
|
|
8074
8582
|
];
|
|
8075
|
-
return
|
|
8583
|
+
return lines.push(`Tokens: ${tokenUsageLine(ctx)}`), ok(lines.join(`
|
|
8076
8584
|
`));
|
|
8077
8585
|
}
|
|
8078
8586
|
}, stateHandler = {
|
|
@@ -8231,6 +8739,17 @@ var statusHandler = {
|
|
|
8231
8739
|
falseHandler,
|
|
8232
8740
|
sleepHandler
|
|
8233
8741
|
];
|
|
8742
|
+
function tokenUsageLine(ctx) {
|
|
8743
|
+
let usage;
|
|
8744
|
+
try {
|
|
8745
|
+
usage = summarizeLoopUsage(ctx.workspace.getLoop());
|
|
8746
|
+
} catch {
|
|
8747
|
+
return "unavailable";
|
|
8748
|
+
}
|
|
8749
|
+
if (usage.usageRows === 0) return "none recorded in the current loop";
|
|
8750
|
+
let n = (v) => v.toLocaleString("en-US"), { input, output, cacheRead, total } = usage.totals, cached = cacheRead > 0 ? ` (${n(cacheRead)} cached)` : "", estimated = usage.estimatedRows > 0 ? ` (${usage.estimatedRows} estimated)` : "", calls = `${usage.usageRows} call${usage.usageRows === 1 ? "" : "s"}${estimated}`;
|
|
8751
|
+
return `${n(total)} total: ${n(input)} in${cached}, ${n(output)} out; ${calls} in the current loop`;
|
|
8752
|
+
}
|
|
8234
8753
|
|
|
8235
8754
|
// src/main/tools/shell/commands/meta.ts
|
|
8236
8755
|
var metaHandler = {
|
|
@@ -8426,7 +8945,7 @@ var CATEGORY_ORDER = [
|
|
|
8426
8945
|
resolvedTools: [],
|
|
8427
8946
|
async execute(ctx) {
|
|
8428
8947
|
if (ctx.args.length > 0) {
|
|
8429
|
-
let { getCommand: getCommand2 } = await import("./commands-
|
|
8948
|
+
let { getCommand: getCommand2 } = await import("./commands-E4EQUHE5.mjs"), handler = getCommand2(ctx.args[0]);
|
|
8430
8949
|
return handler ? ok(handler.helpText) : ok(`Unknown command: ${ctx.args[0]}`);
|
|
8431
8950
|
}
|
|
8432
8951
|
let commands = getAllCommands(), grouped = /* @__PURE__ */ new Map();
|
|
@@ -8495,10 +9014,13 @@ export {
|
|
|
8495
9014
|
verifyEd25519,
|
|
8496
9015
|
rawPublicKeyToSpki,
|
|
8497
9016
|
didToPublicKey,
|
|
9017
|
+
ADF_LATEST_SCHEMA_VERSION,
|
|
8498
9018
|
AdfDatabase,
|
|
8499
9019
|
withSource,
|
|
8500
9020
|
withLoop,
|
|
8501
9021
|
withTurn,
|
|
9022
|
+
currentTurnId,
|
|
9023
|
+
currentAgentId,
|
|
8502
9024
|
currentSourceOrUnknown,
|
|
8503
9025
|
init_execution_context,
|
|
8504
9026
|
SKILLS_REGISTRY_PATH,
|
|
@@ -8509,6 +9031,7 @@ export {
|
|
|
8509
9031
|
rawX25519PrivateToPkcs8,
|
|
8510
9032
|
rawX25519PublicToSpki,
|
|
8511
9033
|
extractRawX25519PublicKey,
|
|
9034
|
+
openKeySlot,
|
|
8512
9035
|
sealWithDek,
|
|
8513
9036
|
openWithDek,
|
|
8514
9037
|
envelopeFromAlgo,
|
|
@@ -8532,12 +9055,11 @@ export {
|
|
|
8532
9055
|
isCodeForbiddenIdentityWrite,
|
|
8533
9056
|
AdfWorkspace,
|
|
8534
9057
|
isTextMime,
|
|
8535
|
-
isVisionMime,
|
|
8536
|
-
isAudioInputMime,
|
|
8537
|
-
isVideoInputMime,
|
|
8538
9058
|
mimeToAudioFormat,
|
|
8539
9059
|
formatSize,
|
|
8540
9060
|
mimeToExt,
|
|
9061
|
+
summarizeLoopUsage,
|
|
9062
|
+
hasReportedUsage,
|
|
8541
9063
|
EXIT,
|
|
8542
9064
|
err,
|
|
8543
9065
|
shellReadFile,
|