@agentdocumentformat/cli 0.7.8 → 0.8.1
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-4AZZJNN2.mjs} +917 -826
- 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-AIPBQTR2.mjs} +9 -3
- 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-2Z7X3UUR.mjs +678 -0
- package/dist/chunks/chunk-3UGZFWIF.mjs +26 -0
- package/dist/chunks/{chunk-XNPZBLYL.mjs → chunk-75DC5EWN.mjs} +13 -2
- package/dist/chunks/{chunk-5PO5PKRV.mjs → chunk-7JZXMDDF.mjs} +158 -67
- package/dist/chunks/chunk-AEAJDCVK.mjs +517 -0
- package/dist/chunks/{chunk-6VQEXLE2.mjs → chunk-BHY7QDCM.mjs} +41 -3
- package/dist/chunks/{chunk-CD3ATSXE.mjs → chunk-BR3AD5QZ.mjs} +8 -2
- package/dist/chunks/chunk-BS3USVEA.mjs +301 -0
- package/dist/chunks/chunk-C2S64Y6F.mjs +203 -0
- package/dist/chunks/chunk-D7A77SNL.mjs +976 -0
- package/dist/chunks/chunk-DDTTSKGG.mjs +2443 -0
- package/dist/chunks/{chunk-VAUOM3IN.mjs → chunk-DLWCKKIO.mjs} +1 -1
- package/dist/chunks/{chunk-G6PH7RPW.mjs → chunk-E7LC6SHP.mjs} +3 -3
- package/dist/chunks/{chunk-ZESQMP66.mjs → chunk-F3WSCAJD.mjs} +3 -3
- 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-VN7UH7I5.mjs → chunk-IMVMSU3A.mjs} +251 -68
- package/dist/chunks/{chunk-XYV3VCIX.mjs → chunk-JZFONJSL.mjs} +79 -20
- package/dist/chunks/chunk-KCIRFUZH.mjs +12799 -0
- package/dist/chunks/chunk-KDDVQKCG.mjs +65 -0
- package/dist/chunks/chunk-KRPKWOMB.mjs +290 -0
- package/dist/chunks/chunk-LKFHERRV.mjs +145 -0
- package/dist/chunks/chunk-LTX74NU6.mjs +29 -0
- package/dist/chunks/{chunk-HSBV6YFU.mjs → chunk-LVF2DRBR.mjs} +634 -99
- 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-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-RHBGCOS3.mjs} +67 -26
- package/dist/chunks/{chunk-J74STFKE.mjs → chunk-RJDOVMM4.mjs} +362 -58
- package/dist/chunks/{chunk-QPLEAE6V.mjs → chunk-SOSPQPEN.mjs} +167 -650
- 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-V3MM6PCH.mjs +366 -0
- package/dist/chunks/chunk-VENQBMOA.mjs +62 -0
- package/dist/chunks/chunk-WJGPNHGX.mjs +67 -0
- package/dist/chunks/{chunk-Y5ZLKFLE.mjs → chunk-XNNJQCZP.mjs} +196 -165
- package/dist/chunks/{chunk-HDBPBORT.mjs → chunk-YRBFIDLB.mjs} +46 -10
- package/dist/chunks/{chunk-BNXUAOO4.mjs → chunk-ZGXUOAWZ.mjs} +3186 -959
- package/dist/chunks/chunk-ZPB3HYM7.mjs +45 -0
- package/dist/chunks/claude-subscription-EB4LAAVV.mjs +29 -0
- package/dist/chunks/{cli-NMFIAR7Z.mjs → cli-PCVFB6B4.mjs} +797 -56
- package/dist/chunks/{commands-KPJWFRUE.mjs → commands-KOMPXJZS.mjs} +4 -3
- package/dist/chunks/{daemon-YDIK3AAM.mjs → daemon-BVDB565V.mjs} +83142 -64424
- package/dist/chunks/daemon-control-MKN7G5M3.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/{layout-EPJQNFAI.mjs → layout-ECWGJF6M.mjs} +2 -2
- package/dist/chunks/{lib-N7FCZWKH.mjs → lib-L27L5PXM.mjs} +22 -22
- package/dist/chunks/{open-VAU7PXT6.mjs → open-3OR6WT4T.mjs} +6 -6
- package/dist/chunks/{parser-JVY2PWSM.mjs → parser-7GARNT5L.mjs} +1 -1
- package/dist/chunks/{pipeline-executor-O77NTGLP.mjs → pipeline-executor-DRYEEFDR.mjs} +6 -5
- package/dist/chunks/prompt-RRHZX4YT.mjs +13 -0
- package/dist/chunks/remote-access-cli-GLEEP7DI.mjs +268 -0
- package/dist/chunks/remote-hello-UFWUBDQI.mjs +37 -0
- package/dist/chunks/sdk-OJOAF4DP.mjs +30014 -0
- package/dist/chunks/{slack-OCNNF4DX.mjs → slack-IYC5RAB5.mjs} +3 -4
- package/dist/chunks/{store-HUVDJPYN.mjs → store-55CQSAOX.mjs} +7 -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-RAUVFOYY.mjs} +21 -18
- 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/{chunk-APRU6PHC.mjs → chunk-JZ7AQNPM.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-E7LC6SHP.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,
|
|
@@ -20,8 +28,9 @@ import {
|
|
|
20
28
|
LOOP_PROHIBITED_TOOLS,
|
|
21
29
|
META_PROTECTION_LEVELS,
|
|
22
30
|
SETTABLE_STATES,
|
|
23
|
-
getDefaultDocumentContent
|
|
24
|
-
|
|
31
|
+
getDefaultDocumentContent,
|
|
32
|
+
hasLoopGoal
|
|
33
|
+
} from "./chunk-BR3AD5QZ.mjs";
|
|
25
34
|
import {
|
|
26
35
|
__esm,
|
|
27
36
|
__export
|
|
@@ -211,38 +220,6 @@ var TEXT_MIME_PREFIXES = ["text/"], TEXT_MIME_EXACT = /* @__PURE__ */ new Set([
|
|
|
211
220
|
function isTextMime(mime) {
|
|
212
221
|
return mime ? TEXT_MIME_PREFIXES.some((p) => mime.startsWith(p)) || /\+(json|xml|yaml)$/.test(mime) ? !0 : TEXT_MIME_EXACT.has(mime) : !1;
|
|
213
222
|
}
|
|
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
223
|
var AUDIO_FORMAT_MAP = {
|
|
247
224
|
"audio/wav": "wav",
|
|
248
225
|
"audio/mpeg": "mp3",
|
|
@@ -462,7 +439,7 @@ var catHandler = {
|
|
|
462
439
|
async execute(ctx) {
|
|
463
440
|
if (ctx.args.length < 2) return err("cp: usage: cp <src> <dst>");
|
|
464
441
|
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}`);
|
|
442
|
+
if (readErr !== null) return err(`cp: ${readErr}`);
|
|
466
443
|
let write = await ctx.toolRegistry.executeTool("fs_write", { mode: "write", path: dst, content }, ctx.workspace);
|
|
467
444
|
return write.isError ? err(`cp: ${write.content}`) : ok("");
|
|
468
445
|
}
|
|
@@ -717,7 +694,7 @@ var duHandler = {
|
|
|
717
694
|
let bytes = parseInt(raw, 10), content = ctx.stdin;
|
|
718
695
|
if (ctx.args.length > 0) {
|
|
719
696
|
let [fileContent, error] = await shellReadFile(ctx.toolRegistry, ctx.workspace, vfsPath(ctx.args[0]));
|
|
720
|
-
if (error) return err(`head: ${error}`);
|
|
697
|
+
if (error !== null) return err(`head: ${error}`);
|
|
721
698
|
content = fileContent;
|
|
722
699
|
}
|
|
723
700
|
return content ? ok(Buffer.from(content, "utf8").subarray(0, bytes).toString("utf8")) : ok("");
|
|
@@ -729,7 +706,7 @@ var duHandler = {
|
|
|
729
706
|
let text = ctx.stdin;
|
|
730
707
|
if (ctx.args.length > 0) {
|
|
731
708
|
let [content, error] = await shellReadFile(ctx.toolRegistry, ctx.workspace, vfsPath(ctx.args[0]), { end_line: n });
|
|
732
|
-
return error ? err(`head: ${error}`) : ok(content);
|
|
709
|
+
return error !== null ? err(`head: ${error}`) : ok(content);
|
|
733
710
|
}
|
|
734
711
|
if (!text) return ok("");
|
|
735
712
|
let lines = text.split(`
|
|
@@ -761,7 +738,7 @@ var duHandler = {
|
|
|
761
738
|
let fromByte = raw.startsWith("+"), bytes = parseInt(raw, 10), content = ctx.stdin;
|
|
762
739
|
if (ctx.args.length > 0) {
|
|
763
740
|
let [fileContent, error] = await shellReadFile(ctx.toolRegistry, ctx.workspace, vfsPath(ctx.args[0]));
|
|
764
|
-
if (error) return err(`tail: ${error}`);
|
|
741
|
+
if (error !== null) return err(`tail: ${error}`);
|
|
765
742
|
content = fileContent;
|
|
766
743
|
}
|
|
767
744
|
if (!content) return ok("");
|
|
@@ -779,7 +756,7 @@ var duHandler = {
|
|
|
779
756
|
let text = ctx.stdin;
|
|
780
757
|
if (ctx.args.length > 0) {
|
|
781
758
|
let [content, error] = await shellReadFile(ctx.toolRegistry, ctx.workspace, vfsPath(ctx.args[0]));
|
|
782
|
-
if (error) return err(`tail: ${error}`);
|
|
759
|
+
if (error !== null) return err(`tail: ${error}`);
|
|
783
760
|
text = content;
|
|
784
761
|
}
|
|
785
762
|
if (!text) return ok("");
|
|
@@ -1217,7 +1194,7 @@ var GREP_KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
1217
1194
|
for (let file of files) {
|
|
1218
1195
|
if (file.mime_type && !isTextMime(file.mime_type)) continue;
|
|
1219
1196
|
let [content, readErr] = await shellReadFile(ctx.toolRegistry, ctx.workspace, file.path);
|
|
1220
|
-
if (readErr) continue;
|
|
1197
|
+
if (readErr !== null) continue;
|
|
1221
1198
|
let lines2 = splitLines(content), sel2 = await select(lines2, content?.length ?? 0);
|
|
1222
1199
|
if ("failure" in sel2) return sel2.failure;
|
|
1223
1200
|
let fileMatches = sel2.idx.length;
|
|
@@ -1236,7 +1213,7 @@ var GREP_KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
1236
1213
|
let text = ctx.stdin;
|
|
1237
1214
|
if (fileArgs.length === 1) {
|
|
1238
1215
|
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`);
|
|
1216
|
+
if (readErr !== null) return err(`grep: ${fileArgs[0]}: No such file or directory`);
|
|
1240
1217
|
text = content;
|
|
1241
1218
|
}
|
|
1242
1219
|
if (!text)
|
|
@@ -1320,7 +1297,7 @@ var SED_KNOWN_FLAGS = /* @__PURE__ */ new Set(["i", "E", "r"]), sedHandler = {
|
|
|
1320
1297
|
if (ctx.args.length > 1) {
|
|
1321
1298
|
filePath = vfsPath2(ctx.args[1]);
|
|
1322
1299
|
let [sedContent, sedErr] = await shellReadFile(ctx.toolRegistry, ctx.workspace, filePath);
|
|
1323
|
-
if (sedErr) return err(`sed: ${filePath}: No such file or directory`);
|
|
1300
|
+
if (sedErr !== null) return err(`sed: ${filePath}: No such file or directory`);
|
|
1324
1301
|
text = sedContent;
|
|
1325
1302
|
} else if (!text)
|
|
1326
1303
|
return ok("");
|
|
@@ -1585,7 +1562,7 @@ var xargsHandler = {
|
|
|
1585
1562
|
batchSize = n;
|
|
1586
1563
|
}
|
|
1587
1564
|
if (!text) return ok("");
|
|
1588
|
-
let { parse } = await import("./parser-
|
|
1565
|
+
let { parse } = await import("./parser-7GARNT5L.mjs"), { executeNode } = await import("./pipeline-executor-DRYEEFDR.mjs"), subCtx = {
|
|
1589
1566
|
workspace: ctx.workspace,
|
|
1590
1567
|
toolRegistry: ctx.toolRegistry,
|
|
1591
1568
|
config: ctx.config,
|
|
@@ -2405,7 +2382,7 @@ async function executeShellScript(path, ctx) {
|
|
|
2405
2382
|
authorized = ctx.workspace.isFileAuthorized(path);
|
|
2406
2383
|
} catch {
|
|
2407
2384
|
}
|
|
2408
|
-
let gate = { ...ctx.gate ?? {}, authorized }, base = baseRegistry(ctx.toolRegistry), toolRegistry = gate.authorized ? authorizedRegistry(base) : base, { parse } = await import("./parser-
|
|
2385
|
+
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-DRYEEFDR.mjs"), depth = (ctx.depth ?? 0) + 1;
|
|
2409
2386
|
if (depth > MAX_SHELL_DEPTH)
|
|
2410
2387
|
return err(`./${path}: script nesting too deep (${MAX_SHELL_DEPTH}) \u2014 possible infinite recursion`);
|
|
2411
2388
|
try {
|
|
@@ -2621,9 +2598,105 @@ import { createPrivateKey as createPrivateKey3, createPublicKey as createPublicK
|
|
|
2621
2598
|
// src/main/adf/adf-database.ts
|
|
2622
2599
|
import Database from "better-sqlite3";
|
|
2623
2600
|
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";
|
|
2601
|
+
import { existsSync as existsSync2, unlinkSync, renameSync, copyFileSync, readdirSync, readFileSync as readFileSync3, openSync, closeSync, lstatSync, fstatSync, fsyncSync, statSync, promises as fsp } from "fs";
|
|
2602
|
+
import { createHash } from "crypto";
|
|
2625
2603
|
import { brotliDecompressSync } from "zlib";
|
|
2626
|
-
import { join as join2, resolve, sep } from "path";
|
|
2604
|
+
import { basename, dirname as dirname2, join as join2, resolve, sep } from "path";
|
|
2605
|
+
|
|
2606
|
+
// src/main/adf/file-ref-registry.ts
|
|
2607
|
+
var MediaPayloadCache = class {
|
|
2608
|
+
constructor(maxChars) {
|
|
2609
|
+
this.maxChars = maxChars;
|
|
2610
|
+
}
|
|
2611
|
+
maxChars;
|
|
2612
|
+
entries = /* @__PURE__ */ new Map();
|
|
2613
|
+
chars = 0;
|
|
2614
|
+
get(path) {
|
|
2615
|
+
let entry = this.entries.get(path);
|
|
2616
|
+
if (entry)
|
|
2617
|
+
return this.entries.delete(path), this.entries.set(path, entry), entry;
|
|
2618
|
+
}
|
|
2619
|
+
set(path, entry) {
|
|
2620
|
+
if (this.delete(path), !(entry.payload.length > this.maxChars)) {
|
|
2621
|
+
this.entries.set(path, entry), this.chars += entry.payload.length;
|
|
2622
|
+
for (let [oldest, value] of this.entries) {
|
|
2623
|
+
if (this.chars <= this.maxChars) break;
|
|
2624
|
+
this.entries.delete(oldest), this.chars -= value.payload.length;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
delete(path) {
|
|
2629
|
+
let entry = this.entries.get(path);
|
|
2630
|
+
entry && (this.entries.delete(path), this.chars -= entry.payload.length);
|
|
2631
|
+
}
|
|
2632
|
+
clear() {
|
|
2633
|
+
this.entries.clear(), this.chars = 0;
|
|
2634
|
+
}
|
|
2635
|
+
get size() {
|
|
2636
|
+
return this.entries.size;
|
|
2637
|
+
}
|
|
2638
|
+
}, MEDIA_CACHE_MAX_CHARS = 48 * 1024 * 1024, FileRefRegistry = class {
|
|
2639
|
+
/**
|
|
2640
|
+
* Paths some transcript row references. Null until first needed: built by
|
|
2641
|
+
* one scan of adf_loop, then kept current by every loop write. May hold
|
|
2642
|
+
* paths whose rows are gone (cleared, compacted); that only costs a scan.
|
|
2643
|
+
*/
|
|
2644
|
+
referenced = null;
|
|
2645
|
+
media = new MediaPayloadCache(MEDIA_CACHE_MAX_CHARS);
|
|
2646
|
+
/** Bumped when a file a request has carried changes, so resolved requests rebuild. */
|
|
2647
|
+
generation = 0;
|
|
2648
|
+
/** Paths a request has carried. */
|
|
2649
|
+
resolved = /* @__PURE__ */ new Set();
|
|
2650
|
+
holders = /* @__PURE__ */ new Set();
|
|
2651
|
+
addHolder(holder) {
|
|
2652
|
+
this.holders.add(new WeakRef(holder));
|
|
2653
|
+
}
|
|
2654
|
+
/** Record the references in a loop row just written. */
|
|
2655
|
+
noteContent(content) {
|
|
2656
|
+
if (this.referenced)
|
|
2657
|
+
for (let block of content)
|
|
2658
|
+
isFileRefBlock(block) && !block.file_ref.deleted && this.referenced.add(block.file_ref.path);
|
|
2659
|
+
}
|
|
2660
|
+
/** Whether any of these changes names a referenced path (`referenced` must be built). */
|
|
2661
|
+
affects(changes) {
|
|
2662
|
+
let referenced = this.referenced;
|
|
2663
|
+
return referenced ? changes.some((change) => referenced.has(change.op === "rename" ? change.from : change.path)) : !0;
|
|
2664
|
+
}
|
|
2665
|
+
/** Keep `referenced` current after the rows were rewritten. */
|
|
2666
|
+
noteChanges(changes) {
|
|
2667
|
+
let referenced = this.referenced;
|
|
2668
|
+
if (referenced)
|
|
2669
|
+
for (let change of changes)
|
|
2670
|
+
change.op !== "rename" || !referenced.has(change.from) || (referenced.delete(change.from), referenced.add(change.to));
|
|
2671
|
+
}
|
|
2672
|
+
/** Rewrite the references every live session holds. */
|
|
2673
|
+
notifyHolders(changes) {
|
|
2674
|
+
for (let ref of this.holders) {
|
|
2675
|
+
let holder = ref.deref();
|
|
2676
|
+
if (!holder) {
|
|
2677
|
+
this.holders.delete(ref);
|
|
2678
|
+
continue;
|
|
2679
|
+
}
|
|
2680
|
+
try {
|
|
2681
|
+
holder.applyFileRefChanges(changes);
|
|
2682
|
+
} catch (error) {
|
|
2683
|
+
console.error("[FileRefRegistry] A session could not apply a file change:", error);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
/** A file was written, renamed or deleted: its loaded bytes are stale. */
|
|
2688
|
+
invalidate(path) {
|
|
2689
|
+
this.media.delete(path), this.resolved.has(path) && this.generation++;
|
|
2690
|
+
}
|
|
2691
|
+
}, byFile = /* @__PURE__ */ new Map(), byDatabase = /* @__PURE__ */ new WeakMap();
|
|
2692
|
+
function fileRefRegistry(database, canonicalPath) {
|
|
2693
|
+
if (canonicalPath === null) {
|
|
2694
|
+
let registry4 = byDatabase.get(database);
|
|
2695
|
+
return registry4 || (registry4 = new FileRefRegistry(), byDatabase.set(database, registry4)), registry4;
|
|
2696
|
+
}
|
|
2697
|
+
let registry3 = byFile.get(canonicalPath);
|
|
2698
|
+
return registry3 || (registry3 = new FileRefRegistry(), byFile.set(canonicalPath, registry3)), registry3;
|
|
2699
|
+
}
|
|
2627
2700
|
|
|
2628
2701
|
// src/shared/constants/agent-icons.ts
|
|
2629
2702
|
var AGENT_ICON_POOL = [
|
|
@@ -2834,7 +2907,20 @@ function didToPublicKey(did) {
|
|
|
2834
2907
|
}
|
|
2835
2908
|
|
|
2836
2909
|
// 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,
|
|
2910
|
+
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 {
|
|
2911
|
+
constructor(filePath, schemaVersion, opts = {}) {
|
|
2912
|
+
let name = basename(filePath);
|
|
2913
|
+
super(
|
|
2914
|
+
(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.`
|
|
2915
|
+
);
|
|
2916
|
+
this.filePath = filePath;
|
|
2917
|
+
this.schemaVersion = schemaVersion;
|
|
2918
|
+
this.name = "AdfSchemaTooOldError";
|
|
2919
|
+
}
|
|
2920
|
+
filePath;
|
|
2921
|
+
schemaVersion;
|
|
2922
|
+
code = "schema_too_old";
|
|
2923
|
+
}, CLEAN_CLOSE_META_KEY = "adf_clean_close", SNAPSHOT_DEADLINE_MS = 3e4, OPFLAG_P2ISREG = 16;
|
|
2838
2924
|
function formatMetaNumber(n) {
|
|
2839
2925
|
return Number.isInteger(n) ? String(n) : String(Number(n.toFixed(10)));
|
|
2840
2926
|
}
|
|
@@ -3144,7 +3230,12 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3144
3230
|
/** Page cap for getLogsAfterId — pollers carry a cursor, so pages catch up. */
|
|
3145
3231
|
static MAX_LOGS_AFTER_ID = 5e3;
|
|
3146
3232
|
constructor(db, filePath) {
|
|
3147
|
-
this.db = db, this.filePath = filePath, _AdfDatabase.incrementOpen(filePath)
|
|
3233
|
+
this.db = db, this.filePath = filePath, _AdfDatabase.incrementOpen(filePath);
|
|
3234
|
+
try {
|
|
3235
|
+
this.applyPragmas(), this.prepareStatements();
|
|
3236
|
+
} catch (err2) {
|
|
3237
|
+
throw _AdfDatabase.decrementOpen(filePath), err2;
|
|
3238
|
+
}
|
|
3148
3239
|
}
|
|
3149
3240
|
// ===========================================================================
|
|
3150
3241
|
// Static Factory Methods
|
|
@@ -3154,6 +3245,36 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3154
3245
|
let code = err2?.code;
|
|
3155
3246
|
return typeof code == "string" && (code.startsWith("SQLITE_BUSY") || code === "SQLITE_LOCKED");
|
|
3156
3247
|
}
|
|
3248
|
+
/**
|
|
3249
|
+
* The file's schema version: `adf_schema_version`, else the older
|
|
3250
|
+
* `schema_version`, else 0. Null when there is no adf_meta table. Reads only.
|
|
3251
|
+
*/
|
|
3252
|
+
static readSchemaVersion(db) {
|
|
3253
|
+
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='adf_meta'").get()) return null;
|
|
3254
|
+
let r = db.prepare("SELECT value FROM adf_meta WHERE key = 'adf_schema_version'").get(), parse = (v) => {
|
|
3255
|
+
let n = parseInt(v, 10);
|
|
3256
|
+
return Number.isFinite(n) ? n : 0;
|
|
3257
|
+
};
|
|
3258
|
+
if (r) return parse(r.value);
|
|
3259
|
+
let r2 = db.prepare("SELECT value FROM adf_meta WHERE key = 'schema_version'").get();
|
|
3260
|
+
return r2 ? parse(r2.value) : 0;
|
|
3261
|
+
}
|
|
3262
|
+
/**
|
|
3263
|
+
* The schema version open() refuses for this file, or null when it may be
|
|
3264
|
+
* opened (and migrated). 1 <= v < 3 is refused. No version at all (0, or
|
|
3265
|
+
* unparsable) is refused unless the file has a clone's layout: a clone
|
|
3266
|
+
* whose adf_meta was deselected (before clone-fixup restamped the version)
|
|
3267
|
+
* has `adf_audit`, `adf_loop.loop` and no `adf_archive`. Null too without
|
|
3268
|
+
* an adf_meta table (the v0.2 check reports that). Reads only.
|
|
3269
|
+
*/
|
|
3270
|
+
static refusedSchemaVersion(db) {
|
|
3271
|
+
let sv = _AdfDatabase.readSchemaVersion(db);
|
|
3272
|
+
if (sv === null) return null;
|
|
3273
|
+
if (sv > 0 && sv < ADF_OLDEST_MIGRATABLE_SCHEMA_VERSION) return sv;
|
|
3274
|
+
if (sv > 0) return null;
|
|
3275
|
+
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();
|
|
3276
|
+
return tables.has("adf_audit") && !tables.has("adf_archive") && loopCols.has("loop") ? null : 0;
|
|
3277
|
+
}
|
|
3157
3278
|
/** Retryable lock-contention error — distinct from the corruption path. */
|
|
3158
3279
|
static lockedError(filePath) {
|
|
3159
3280
|
return new Error(
|
|
@@ -3224,18 +3345,146 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3224
3345
|
}
|
|
3225
3346
|
/** Synchronous backup — only for `open()`, which cannot await mid-migration.
|
|
3226
3347
|
* 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.
|
|
3348
|
+
* this connection exists, so nothing can checkpoint underneath it.
|
|
3349
|
+
* `complete`: the checkpoint folded the whole WAL first, so the copy holds
|
|
3350
|
+
* every committed write (a failed open may restore from it).
|
|
3351
|
+
* `reused`: a `.bak` already on disk has the same size and SHA-256 as the
|
|
3352
|
+
* checkpointed file (a migration that failed before and rolled back), so
|
|
3353
|
+
* it is kept as is instead of being moved aside and copied again. The
|
|
3354
|
+
* copy is fsynced. */
|
|
3228
3355
|
static backupBeforeDestructiveSync(db, filePath) {
|
|
3229
|
-
let backupPath = filePath + ".bak";
|
|
3356
|
+
let backupPath = filePath + ".bak", complete = !1;
|
|
3357
|
+
try {
|
|
3358
|
+
let [ck] = db.pragma("wal_checkpoint(TRUNCATE)");
|
|
3359
|
+
complete = ck?.busy === 0;
|
|
3360
|
+
} catch {
|
|
3361
|
+
}
|
|
3362
|
+
if (_AdfDatabase.sameFileContent(filePath, backupPath)) return { backupPath, complete, reused: !0 };
|
|
3230
3363
|
try {
|
|
3231
3364
|
renameSync(backupPath, _AdfDatabase.staleBackupName(backupPath));
|
|
3232
3365
|
} catch {
|
|
3233
3366
|
}
|
|
3367
|
+
return copyFileSync(filePath, backupPath), _AdfDatabase.fsyncFileSync(backupPath), { backupPath, complete, reused: !1 };
|
|
3368
|
+
}
|
|
3369
|
+
/** Both files exist with the same size and SHA-256. */
|
|
3370
|
+
static sameFileContent(a, b) {
|
|
3234
3371
|
try {
|
|
3235
|
-
|
|
3372
|
+
if (statSync(a).size !== statSync(b).size) return !1;
|
|
3373
|
+
let hash = (p) => createHash("sha256").update(readFileSync3(p)).digest("hex");
|
|
3374
|
+
return hash(a) === hash(b);
|
|
3236
3375
|
} catch {
|
|
3376
|
+
return !1;
|
|
3237
3377
|
}
|
|
3238
|
-
|
|
3378
|
+
}
|
|
3379
|
+
static fsyncFileSync(path) {
|
|
3380
|
+
let fd = openSync(path, "r+");
|
|
3381
|
+
try {
|
|
3382
|
+
fsyncSync(fd);
|
|
3383
|
+
} finally {
|
|
3384
|
+
closeSync(fd);
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
/** POSIX: make a rename in `dir` durable. Windows cannot open a directory for fsync. */
|
|
3388
|
+
static fsyncDirSync(dir) {
|
|
3389
|
+
if (process.platform !== "win32")
|
|
3390
|
+
try {
|
|
3391
|
+
let fd = openSync(dir, "r");
|
|
3392
|
+
try {
|
|
3393
|
+
fsyncSync(fd);
|
|
3394
|
+
} finally {
|
|
3395
|
+
closeSync(fd);
|
|
3396
|
+
}
|
|
3397
|
+
} catch {
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
/**
|
|
3401
|
+
* After a failure that followed a committed migration (the constructor):
|
|
3402
|
+
* put the file back as its pre-migration backup. The `.bak` is kept (it is
|
|
3403
|
+
* removed only after a fully successful open). Null when restored, else
|
|
3404
|
+
* why not (the caller says so in its error).
|
|
3405
|
+
*
|
|
3406
|
+
* Only when no other connection can hold the file: none in this process,
|
|
3407
|
+
* no -wal left beside it (SQLite removes it at the last close), and an
|
|
3408
|
+
* exclusive lock taken with busy_timeout 0. The copy is fsynced before the
|
|
3409
|
+
* rename. On POSIX the rename happens while that lock is held, so nothing
|
|
3410
|
+
* can open the old file in between (a connection that opened it earlier
|
|
3411
|
+
* would have held a lock). On Windows the lock's own handle would block the
|
|
3412
|
+
* rename, so the probe closes first: the rename itself then fails while any
|
|
3413
|
+
* other program has the file open.
|
|
3414
|
+
*
|
|
3415
|
+
* Known POSIX residual (tracker §6, W5-H): the probe closes after the
|
|
3416
|
+
* rename, and SQLite's close unlinks `<file>-wal` by path. A program that
|
|
3417
|
+
* opens the restored file and writes in that window can lose its WAL.
|
|
3418
|
+
* Closing first would drop the lock, and better-sqlite3 cannot keep the
|
|
3419
|
+
* WAL on close (no SQLITE_FCNTL_PERSIST_WAL), so the window is not closed.
|
|
3420
|
+
*/
|
|
3421
|
+
static restoreBackupSync(filePath) {
|
|
3422
|
+
let backupPath = filePath + ".bak";
|
|
3423
|
+
if (!existsSync2(backupPath)) return "there is no backup";
|
|
3424
|
+
let busy = "another connection has the file open";
|
|
3425
|
+
if (_AdfDatabase.openCounts.has(_AdfDatabase.canonicalKey(filePath)) || existsSync2(`${filePath}-wal`)) return busy;
|
|
3426
|
+
let restorePath = `${filePath}.restore`, probe = null;
|
|
3427
|
+
try {
|
|
3428
|
+
copyFileSync(backupPath, restorePath), _AdfDatabase.fsyncFileSync(restorePath);
|
|
3429
|
+
try {
|
|
3430
|
+
probe = new Database(filePath, { fileMustExist: !0 }), probe.pragma("busy_timeout = 0"), probe.pragma("locking_mode = EXCLUSIVE"), probe.exec("BEGIN IMMEDIATE; COMMIT");
|
|
3431
|
+
} catch {
|
|
3432
|
+
try {
|
|
3433
|
+
probe?.close();
|
|
3434
|
+
} catch {
|
|
3435
|
+
}
|
|
3436
|
+
probe = null;
|
|
3437
|
+
try {
|
|
3438
|
+
unlinkSync(restorePath);
|
|
3439
|
+
} catch {
|
|
3440
|
+
}
|
|
3441
|
+
return busy;
|
|
3442
|
+
}
|
|
3443
|
+
process.platform === "win32" && (probe.close(), probe = null);
|
|
3444
|
+
try {
|
|
3445
|
+
unlinkSync(`${filePath}-shm`);
|
|
3446
|
+
} catch {
|
|
3447
|
+
}
|
|
3448
|
+
return renameSync(restorePath, filePath), _AdfDatabase.fsyncDirSync(dirname2(filePath)), null;
|
|
3449
|
+
} catch (err2) {
|
|
3450
|
+
try {
|
|
3451
|
+
unlinkSync(restorePath);
|
|
3452
|
+
} catch {
|
|
3453
|
+
}
|
|
3454
|
+
let code = err2?.code;
|
|
3455
|
+
return code === "EPERM" || code === "EBUSY" || code === "EACCES" ? busy : `the restore failed (${err2 instanceof Error ? err2.message : String(err2)})`;
|
|
3456
|
+
} finally {
|
|
3457
|
+
try {
|
|
3458
|
+
probe?.close();
|
|
3459
|
+
} catch {
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
/**
|
|
3464
|
+
* A failed open(): roll back is the caller's; here the connection is
|
|
3465
|
+
* closed, the file is put back from the backup when `restore` (the failure
|
|
3466
|
+
* followed a committed migration), and the sidecars this open created are
|
|
3467
|
+
* reaped. Returns the sentence to add to the error when the file could not
|
|
3468
|
+
* be restored, else null.
|
|
3469
|
+
*/
|
|
3470
|
+
static abandonOpen(db, filePath, opts) {
|
|
3471
|
+
try {
|
|
3472
|
+
db.close();
|
|
3473
|
+
} catch {
|
|
3474
|
+
}
|
|
3475
|
+
let note = null;
|
|
3476
|
+
if (opts.backup && !opts.restore)
|
|
3477
|
+
console.error(`[AdfDatabase] Open failed; the migration was rolled back, the file is unchanged. Backup kept at: ${filePath}.bak`);
|
|
3478
|
+
else if (opts.restore) {
|
|
3479
|
+
let why = opts.backup ? opts.backup.complete ? _AdfDatabase.restoreBackupSync(filePath) : "the backup may lack writes another connection had not checkpointed" : "there is no backup";
|
|
3480
|
+
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}`));
|
|
3481
|
+
}
|
|
3482
|
+
if (!opts.walExistedBeforeOpen)
|
|
3483
|
+
try {
|
|
3484
|
+
_AdfDatabase.reapSidecars(filePath);
|
|
3485
|
+
} catch {
|
|
3486
|
+
}
|
|
3487
|
+
return note;
|
|
3239
3488
|
}
|
|
3240
3489
|
/** Remove a transient .bak file after a successful destructive operation. */
|
|
3241
3490
|
static async removeBackup(filePath) {
|
|
@@ -3290,6 +3539,29 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3290
3539
|
* 'reaped' — WAL folded into the main file, sidecars removed
|
|
3291
3540
|
* 'error' — open/checkpoint/unlink failed; sidecars left in place
|
|
3292
3541
|
*/
|
|
3542
|
+
/**
|
|
3543
|
+
* True when no other connection holds `filePath`'s write lock: a probe
|
|
3544
|
+
* connection takes it with BEGIN IMMEDIATE (waiting at most
|
|
3545
|
+
* `busyTimeoutMs`) and gives it back at once. False on SQLITE_BUSY. Lets a
|
|
3546
|
+
* background job (the boot envelope sweep) pass over a file another
|
|
3547
|
+
* connection is writing instead of blocking in AdfWorkspace.open, whose
|
|
3548
|
+
* maintenance writes wait the full busy_timeout. Other errors throw.
|
|
3549
|
+
*/
|
|
3550
|
+
static writeLockFree(filePath, busyTimeoutMs) {
|
|
3551
|
+
let db = null;
|
|
3552
|
+
try {
|
|
3553
|
+
return db = new Database(filePath, { fileMustExist: !0 }), db.pragma(`busy_timeout = ${Math.max(0, Math.floor(busyTimeoutMs))}`), db.exec("BEGIN IMMEDIATE; ROLLBACK"), !0;
|
|
3554
|
+
} catch (err2) {
|
|
3555
|
+
let code = err2?.code;
|
|
3556
|
+
if (typeof code == "string" && code.startsWith("SQLITE_BUSY")) return !1;
|
|
3557
|
+
throw err2;
|
|
3558
|
+
} finally {
|
|
3559
|
+
try {
|
|
3560
|
+
db?.close();
|
|
3561
|
+
} catch {
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3293
3565
|
static reapSidecars(filePath) {
|
|
3294
3566
|
let walPath = `${filePath}-wal`, shmPath = `${filePath}-shm`;
|
|
3295
3567
|
if (!existsSync2(walPath) && !existsSync2(shmPath)) return "no-sidecars";
|
|
@@ -3376,7 +3648,40 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3376
3648
|
}
|
|
3377
3649
|
}
|
|
3378
3650
|
static open(filePath, options) {
|
|
3379
|
-
let walExistedBeforeOpen = existsSync2(`${filePath}-wal`) || existsSync2(`${filePath}-shm`)
|
|
3651
|
+
let walExistedBeforeOpen = existsSync2(`${filePath}-wal`) || existsSync2(`${filePath}-shm`);
|
|
3652
|
+
if (walExistedBeforeOpen) {
|
|
3653
|
+
let ro = null, refused = null;
|
|
3654
|
+
try {
|
|
3655
|
+
ro = new Database(filePath, { readonly: !0, fileMustExist: !0 }), refused = _AdfDatabase.refusedSchemaVersion(ro);
|
|
3656
|
+
} catch (err2) {
|
|
3657
|
+
if (_AdfDatabase.isBusyError(err2)) throw _AdfDatabase.lockedError(filePath);
|
|
3658
|
+
} finally {
|
|
3659
|
+
try {
|
|
3660
|
+
ro?.close();
|
|
3661
|
+
} catch {
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
if (refused !== null) throw new AdfSchemaTooOldError(filePath, refused);
|
|
3665
|
+
}
|
|
3666
|
+
let db = new Database(filePath), needsMigration = !1, backup = null, migrated = !1, repaired = !1;
|
|
3667
|
+
if (!walExistedBeforeOpen) {
|
|
3668
|
+
let refused = null;
|
|
3669
|
+
try {
|
|
3670
|
+
refused = _AdfDatabase.refusedSchemaVersion(db);
|
|
3671
|
+
} catch (err2) {
|
|
3672
|
+
if (_AdfDatabase.isBusyError(err2))
|
|
3673
|
+
throw db.close(), _AdfDatabase.lockedError(filePath);
|
|
3674
|
+
}
|
|
3675
|
+
if (refused !== null) {
|
|
3676
|
+
db.close();
|
|
3677
|
+
try {
|
|
3678
|
+
_AdfDatabase.reapSidecars(filePath);
|
|
3679
|
+
} catch {
|
|
3680
|
+
}
|
|
3681
|
+
throw new AdfSchemaTooOldError(filePath, refused);
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
let integrityMode = "full";
|
|
3380
3685
|
if (!options?.forceIntegrityCheck) {
|
|
3381
3686
|
let closedCleanly = !1;
|
|
3382
3687
|
try {
|
|
@@ -3392,7 +3697,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3392
3697
|
let checkPragma = integrityMode === "quick" ? "quick_check" : "integrity_check", status = (integrityMode === "none" ? [{ [checkPragma]: "ok" }] : db.pragma(checkPragma))[0]?.[checkPragma];
|
|
3393
3698
|
if (status !== "ok")
|
|
3394
3699
|
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")
|
|
3700
|
+
if (db = new Database(filePath), repaired = !0, db.pragma("integrity_check")[0]?.integrity_check !== "ok")
|
|
3396
3701
|
throw db.close(), new Error(
|
|
3397
3702
|
`ADF file is corrupt and auto-repair failed. A backup was saved at: ${filePath}.corrupt`
|
|
3398
3703
|
);
|
|
@@ -3412,7 +3717,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3412
3717
|
throw _AdfDatabase.lockedError(filePath);
|
|
3413
3718
|
}
|
|
3414
3719
|
if (console.warn("[AdfDatabase] Integrity check threw:", error), db.close(), _AdfDatabase.tryRepair(filePath))
|
|
3415
|
-
db = new Database(filePath);
|
|
3720
|
+
db = new Database(filePath), repaired = !0;
|
|
3416
3721
|
else
|
|
3417
3722
|
throw new Error(
|
|
3418
3723
|
"ADF file is corrupt and could not be repaired. Please delete and recreate the file."
|
|
@@ -3425,16 +3730,22 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3425
3730
|
throw db.close(), new Error(
|
|
3426
3731
|
"This ADF file does not have the v0.2 schema. Please delete and recreate the file."
|
|
3427
3732
|
);
|
|
3733
|
+
let refused;
|
|
3734
|
+
try {
|
|
3735
|
+
refused = _AdfDatabase.refusedSchemaVersion(db);
|
|
3736
|
+
} catch (err2) {
|
|
3737
|
+
throw _AdfDatabase.isBusyError(err2) ? _AdfDatabase.lockedError(filePath) : new Error(
|
|
3738
|
+
`Cannot read the schema version of ${basename(filePath)}: ${err2 instanceof Error ? err2.message : String(err2)}. The file was not migrated.`,
|
|
3739
|
+
{ cause: err2 }
|
|
3740
|
+
);
|
|
3741
|
+
}
|
|
3742
|
+
if (refused !== null)
|
|
3743
|
+
throw new AdfSchemaTooOldError(filePath, refused, { unchanged: !walExistedBeforeOpen && !repaired });
|
|
3428
3744
|
try {
|
|
3429
3745
|
db.prepare("DELETE FROM adf_meta WHERE key = ?").run(CLEAN_CLOSE_META_KEY), db.pragma("wal_checkpoint(PASSIVE)");
|
|
3430
3746
|
} catch {
|
|
3431
3747
|
}
|
|
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
|
-
})();
|
|
3748
|
+
let sv = _AdfDatabase.readSchemaVersion(db) ?? 0;
|
|
3438
3749
|
if (needsMigration = sv < ADF_LATEST_SCHEMA_VERSION, !needsMigration) {
|
|
3439
3750
|
if (sv > ADF_LATEST_SCHEMA_VERSION) {
|
|
3440
3751
|
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 +3761,13 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3450
3761
|
}
|
|
3451
3762
|
if (needsMigration)
|
|
3452
3763
|
try {
|
|
3453
|
-
_AdfDatabase.backupBeforeDestructiveSync(db, filePath)
|
|
3764
|
+
let made = _AdfDatabase.backupBeforeDestructiveSync(db, filePath);
|
|
3765
|
+
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
3766
|
} catch (e) {
|
|
3455
3767
|
console.warn("[AdfDatabase] Could not create pre-migration backup:", e);
|
|
3456
3768
|
}
|
|
3457
|
-
|
|
3769
|
+
db.exec("BEGIN IMMEDIATE");
|
|
3770
|
+
let unversionedClone = sv === 0, cols = db.prepare("PRAGMA table_info(adf_loop)").all();
|
|
3458
3771
|
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
3772
|
"SELECT name FROM sqlite_master WHERE type='table' AND name='adf_audit'"
|
|
3460
3773
|
).get() || db.exec(`
|
|
@@ -3972,11 +4285,24 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
3972
4285
|
}
|
|
3973
4286
|
} catch {
|
|
3974
4287
|
}
|
|
3975
|
-
|
|
4288
|
+
if (!db.inTransaction) throw new Error(`The migration of ${basename(filePath)} was rolled back by SQLite.`);
|
|
4289
|
+
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
4290
|
} catch (error) {
|
|
3977
|
-
|
|
4291
|
+
if (db.inTransaction)
|
|
4292
|
+
try {
|
|
4293
|
+
db.exec("ROLLBACK");
|
|
4294
|
+
} catch {
|
|
4295
|
+
}
|
|
4296
|
+
throw _AdfDatabase.abandonOpen(db, filePath, { backup, restore: !1, walExistedBeforeOpen }), error;
|
|
3978
4297
|
}
|
|
3979
|
-
|
|
4298
|
+
let opened;
|
|
4299
|
+
try {
|
|
4300
|
+
opened = new _AdfDatabase(db, filePath);
|
|
4301
|
+
} catch (error) {
|
|
4302
|
+
let note = _AdfDatabase.abandonOpen(db, filePath, { backup, restore: migrated, walExistedBeforeOpen });
|
|
4303
|
+
throw note ? new Error(`${error instanceof Error ? error.message : String(error)}. ${note}`, { cause: error }) : error;
|
|
4304
|
+
}
|
|
4305
|
+
return backup && _AdfDatabase.removeBackupSync(filePath), opened;
|
|
3980
4306
|
}
|
|
3981
4307
|
/**
|
|
3982
4308
|
* Create a new ADF without replacing an existing destination.
|
|
@@ -4048,7 +4374,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4048
4374
|
let overrideMap = new Map(options.tools.map((t) => [t.name, t]));
|
|
4049
4375
|
tools = tools.map((t) => overrideMap.has(t.name) ? { ...t, ...overrideMap.get(t.name) } : t);
|
|
4050
4376
|
for (let t of options.tools)
|
|
4051
|
-
tools.some((dt) => dt.name === t.name) || tools.push(t);
|
|
4377
|
+
tools.some((dt) => dt.name === t.name) || tools.push({ visible: !0, ...t });
|
|
4052
4378
|
}
|
|
4053
4379
|
let mergedTriggers = { ...base.triggers };
|
|
4054
4380
|
if (options.triggers)
|
|
@@ -4075,7 +4401,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4075
4401
|
context: {
|
|
4076
4402
|
...base.context,
|
|
4077
4403
|
...options.context,
|
|
4078
|
-
audit: { ...base.context.audit, ...options.context?.audit },
|
|
4404
|
+
audit: { ...AUDIT_DEFAULTS, ...base.context.audit, ...options.context?.audit },
|
|
4079
4405
|
dynamic_instructions: { ...base.context.dynamic_instructions, ...options.context?.dynamic_instructions }
|
|
4080
4406
|
},
|
|
4081
4407
|
tools,
|
|
@@ -4085,6 +4411,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4085
4411
|
messaging: { ...base.messaging, ...options.messaging },
|
|
4086
4412
|
audit: { ...base.audit, ...options.audit },
|
|
4087
4413
|
pre_llm_hook: options.pre_llm_hook ?? base.pre_llm_hook,
|
|
4414
|
+
commands: options.commands ?? base.commands,
|
|
4088
4415
|
code_execution: { ...base.code_execution, ...options.code_execution },
|
|
4089
4416
|
compute: { ...base.compute },
|
|
4090
4417
|
logging: { ...base.logging, ...options.logging },
|
|
@@ -4160,10 +4487,10 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4160
4487
|
* crash leftovers owned by the sweeps — left untouched here. Errors from
|
|
4161
4488
|
* `fn` (and the open) propagate to the caller's existing catch.
|
|
4162
4489
|
*/
|
|
4163
|
-
static peekReadonly(filePath, fn) {
|
|
4490
|
+
static peekReadonly(filePath, fn, busyTimeoutMs) {
|
|
4164
4491
|
let hadSidecars = existsSync2(`${filePath}-wal`) || existsSync2(`${filePath}-shm`), db = null;
|
|
4165
4492
|
try {
|
|
4166
|
-
return db = new Database(filePath, { readonly: !0 }), fn(db);
|
|
4493
|
+
return db = new Database(filePath, { readonly: !0 }), busyTimeoutMs !== void 0 && db.pragma(`busy_timeout = ${Math.max(0, Math.floor(busyTimeoutMs))}`), fn(db);
|
|
4167
4494
|
} finally {
|
|
4168
4495
|
try {
|
|
4169
4496
|
db?.close();
|
|
@@ -4198,6 +4525,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4198
4525
|
throw new Error(
|
|
4199
4526
|
"Snapshot timed out: the agent is being written to continuously \u2014 stop it and try again"
|
|
4200
4527
|
);
|
|
4528
|
+
return 100;
|
|
4201
4529
|
}
|
|
4202
4530
|
});
|
|
4203
4531
|
} finally {
|
|
@@ -4363,6 +4691,23 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4363
4691
|
return null;
|
|
4364
4692
|
}
|
|
4365
4693
|
}
|
|
4694
|
+
/**
|
|
4695
|
+
* What the boot envelope sweep decides on before opening a file, read
|
|
4696
|
+
* read-only: its schema version (null without an adf_meta table, 0 with no
|
|
4697
|
+
* version) and its agent id (null without a config). Errors propagate;
|
|
4698
|
+
* `busyTimeoutMs` bounds the wait for a lock (the sweep's 25 ms).
|
|
4699
|
+
*/
|
|
4700
|
+
static peekSweepStatus(filePath, busyTimeoutMs) {
|
|
4701
|
+
return _AdfDatabase.peekReadonly(filePath, (db) => {
|
|
4702
|
+
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;
|
|
4703
|
+
try {
|
|
4704
|
+
let id = row ? JSON.parse(row.config_json).id : void 0;
|
|
4705
|
+
typeof id == "string" && id && (agentId = id);
|
|
4706
|
+
} catch {
|
|
4707
|
+
}
|
|
4708
|
+
return { schemaVersion, agentId };
|
|
4709
|
+
}, busyTimeoutMs);
|
|
4710
|
+
}
|
|
4366
4711
|
static peekBootStatusDetailed(filePath) {
|
|
4367
4712
|
try {
|
|
4368
4713
|
return _AdfDatabase.peekReadonly(filePath, (db) => {
|
|
@@ -4528,8 +4873,8 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4528
4873
|
static peekIdentityPurposes(filePath, prefix) {
|
|
4529
4874
|
try {
|
|
4530
4875
|
return _AdfDatabase.peekReadonly(filePath, (db) => db.prepare(
|
|
4531
|
-
"SELECT purpose FROM adf_identity WHERE purpose
|
|
4532
|
-
).all(prefix).map((r) => r.purpose));
|
|
4876
|
+
"SELECT purpose FROM adf_identity WHERE substr(purpose, 1, length(?)) = ?"
|
|
4877
|
+
).all(prefix, prefix).map((r) => r.purpose));
|
|
4533
4878
|
} catch {
|
|
4534
4879
|
return [];
|
|
4535
4880
|
}
|
|
@@ -4584,6 +4929,8 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4584
4929
|
"INSERT INTO adf_loop (seq, role, content_json, model, tokens, created_at, ord, loop) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
4585
4930
|
), 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
4931
|
"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"
|
|
4932
|
+
), this.stmts.getAssistantTokenRows = this.db.prepare(
|
|
4933
|
+
"SELECT seq, tokens FROM adf_loop WHERE loop = ? AND role = 'assistant' AND tokens IS NOT NULL ORDER BY COALESCE(ord, seq), seq"
|
|
4587
4934
|
), this.stmts.getInboxMessages = this.db.prepare(
|
|
4588
4935
|
"SELECT * FROM adf_inbox ORDER BY received_at DESC"
|
|
4589
4936
|
), this.stmts.getInboxByStatus = this.db.prepare(
|
|
@@ -4666,7 +5013,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4666
5013
|
), this.stmts.deleteIdentity = this.db.prepare(
|
|
4667
5014
|
"DELETE FROM adf_identity WHERE purpose = ?"
|
|
4668
5015
|
), this.stmts.listIdentityByPrefix = this.db.prepare(
|
|
4669
|
-
"SELECT purpose FROM adf_identity WHERE purpose
|
|
5016
|
+
"SELECT purpose FROM adf_identity WHERE substr(purpose, 1, length(?)) = ?"
|
|
4670
5017
|
), this.stmts.getIdentityFull = this.db.prepare(
|
|
4671
5018
|
"SELECT purpose, value, encryption_algo, salt, kdf_params, code_access FROM adf_identity WHERE purpose = ?"
|
|
4672
5019
|
), this.stmts.getAllIdentityFull = this.db.prepare(
|
|
@@ -4703,6 +5050,8 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4703
5050
|
"UPDATE adf_tasks SET requires_authorization = 1 WHERE id = ?"
|
|
4704
5051
|
), this.stmts.setTaskExecutorManaged = this.db.prepare(
|
|
4705
5052
|
"UPDATE adf_tasks SET executor_managed = 1 WHERE id = ?"
|
|
5053
|
+
), this.stmts.setTaskApprovalMeta = this.db.prepare(
|
|
5054
|
+
"UPDATE adf_tasks SET approval_meta = ? WHERE id = ?"
|
|
4706
5055
|
), this.stmts.updateTaskArgs = this.db.prepare(
|
|
4707
5056
|
"UPDATE adf_tasks SET args = ? WHERE id = ?"
|
|
4708
5057
|
), this.stmts.getTasksByStatus = this.db.prepare(
|
|
@@ -4781,11 +5130,16 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4781
5130
|
deleteIdentity(purpose) {
|
|
4782
5131
|
return this.stmts.deleteIdentity.run(purpose).changes > 0;
|
|
4783
5132
|
}
|
|
5133
|
+
/**
|
|
5134
|
+
* Prefix matches are exact and case-sensitive (substr, not LIKE): with LIKE,
|
|
5135
|
+
* `_` and `%` in a server or adapter name were wildcards and ASCII case was
|
|
5136
|
+
* ignored, so detaching one server deleted another's credentials.
|
|
5137
|
+
*/
|
|
4784
5138
|
deleteIdentityByPrefix(prefix) {
|
|
4785
|
-
return this.db.prepare("DELETE FROM adf_identity WHERE purpose
|
|
5139
|
+
return this.db.prepare("DELETE FROM adf_identity WHERE substr(purpose, 1, length(?)) = ?").run(prefix, prefix).changes;
|
|
4786
5140
|
}
|
|
4787
5141
|
listIdentityPurposes(prefix) {
|
|
4788
|
-
return prefix ? this.stmts.listIdentityByPrefix.all(prefix).map((r) => r.purpose) : this.stmts.listAllIdentityPurposes.all().map((r) => r.purpose);
|
|
5142
|
+
return prefix ? this.stmts.listIdentityByPrefix.all(prefix, prefix).map((r) => r.purpose) : this.stmts.listAllIdentityPurposes.all().map((r) => r.purpose);
|
|
4789
5143
|
}
|
|
4790
5144
|
// --- Encrypted identity methods ---
|
|
4791
5145
|
/**
|
|
@@ -4879,6 +5233,17 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4879
5233
|
deleteAllAttestations() {
|
|
4880
5234
|
this.db.prepare("DELETE FROM adf_attestations").run();
|
|
4881
5235
|
}
|
|
5236
|
+
/**
|
|
5237
|
+
* Re-sign rows in place: each row whose signature is `from` takes `to`
|
|
5238
|
+
* (same issuer, subject and role), keeping its id and place. A row changed
|
|
5239
|
+
* or removed meanwhile matches nothing and is left as it is. Returns the
|
|
5240
|
+
* rows updated.
|
|
5241
|
+
*/
|
|
5242
|
+
resignAttestations(updates) {
|
|
5243
|
+
let stmt = this.db.prepare("UPDATE adf_attestations SET signature = ?, raw_json = ? WHERE signature = ?"), changed = 0;
|
|
5244
|
+
for (let { from, to } of updates) changed += stmt.run(to.signature, JSON.stringify(to), from).changes;
|
|
5245
|
+
return changed;
|
|
5246
|
+
}
|
|
4882
5247
|
deleteAllIdentity() {
|
|
4883
5248
|
this.db.prepare("DELETE FROM adf_identity").run();
|
|
4884
5249
|
}
|
|
@@ -4909,9 +5274,27 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4909
5274
|
}
|
|
4910
5275
|
return added && this.setConfig(config), config;
|
|
4911
5276
|
}
|
|
5277
|
+
/**
|
|
5278
|
+
* setConfig calls through this connection: with dataVersion(), a cheap "did
|
|
5279
|
+
* the config change" check for callers that cache it (RuntimeService's
|
|
5280
|
+
* running-agent config).
|
|
5281
|
+
*/
|
|
5282
|
+
configWrites = 0;
|
|
5283
|
+
dataVersionStmt = null;
|
|
5284
|
+
configWriteCount() {
|
|
5285
|
+
return this.configWrites;
|
|
5286
|
+
}
|
|
5287
|
+
/**
|
|
5288
|
+
* SQLite's `PRAGMA data_version`: changes when another connection (another
|
|
5289
|
+
* process, or another handle in this one) commits to the file; this
|
|
5290
|
+
* connection's own writes leave it unchanged (configWriteCount counts those).
|
|
5291
|
+
*/
|
|
5292
|
+
dataVersion() {
|
|
5293
|
+
return this.dataVersionStmt ??= this.db.prepare("PRAGMA data_version").pluck(), this.dataVersionStmt.get();
|
|
5294
|
+
}
|
|
4912
5295
|
setConfig(config) {
|
|
4913
5296
|
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);
|
|
5297
|
+
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
5298
|
}
|
|
4916
5299
|
// ===========================================================================
|
|
4917
5300
|
// Loop Table
|
|
@@ -4970,7 +5353,25 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4970
5353
|
opts?.ord ?? null,
|
|
4971
5354
|
loop
|
|
4972
5355
|
);
|
|
4973
|
-
return this.bumpLoopRevision(loop), Number(result.lastInsertRowid);
|
|
5356
|
+
return this.bumpLoopRevision(loop), content.some((block) => block.type === "file_ref") && this.getFileRefRegistry().noteContent(content), Number(result.lastInsertRowid);
|
|
5357
|
+
}
|
|
5358
|
+
/** File-reference state of this file, shared by every connection to it (see file-ref-registry.ts). */
|
|
5359
|
+
getFileRefRegistry() {
|
|
5360
|
+
return fileRefRegistry(this, this.filePath === ":memory:" ? null : _AdfDatabase.canonicalKey(this.filePath));
|
|
5361
|
+
}
|
|
5362
|
+
/**
|
|
5363
|
+
* Rows of every loop that hold a `file_ref` block (a prefilter: the caller
|
|
5364
|
+
* parses and checks). One scan of adf_loop; callers run it only when a
|
|
5365
|
+
* referenced file is renamed or deleted.
|
|
5366
|
+
*/
|
|
5367
|
+
getLoopRowsWithFileRefs() {
|
|
5368
|
+
return this.db.prepare(
|
|
5369
|
+
`SELECT seq, loop, content_json FROM adf_loop WHERE instr(content_json, '"file_ref"') > 0`
|
|
5370
|
+
).all();
|
|
5371
|
+
}
|
|
5372
|
+
/** Replace one row's content in place (file references rewritten on rename/delete). */
|
|
5373
|
+
updateLoopContent(loop, seq, content) {
|
|
5374
|
+
this.db.prepare("UPDATE adf_loop SET content_json = ? WHERE seq = ? AND loop = ?").run(JSON.stringify(content), seq, loop), this.bumpLoopRevision(loop);
|
|
4974
5375
|
}
|
|
4975
5376
|
/** Wipe ONE stream. The whole-table wipe is clearAllLoops(). */
|
|
4976
5377
|
clearLoop(loop) {
|
|
@@ -4999,6 +5400,15 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
4999
5400
|
let row = this.stmts.getLastAssistantTokens.get(loop);
|
|
5000
5401
|
return _AdfDatabase.parseLoopTokens(row?.tokens);
|
|
5001
5402
|
}
|
|
5403
|
+
/** Every assistant row's usage record, in loop order (oldest first). */
|
|
5404
|
+
getAssistantTokenRows(loop) {
|
|
5405
|
+
let rows = this.stmts.getAssistantTokenRows.all(loop), out = [];
|
|
5406
|
+
for (let row of rows) {
|
|
5407
|
+
let tokens = _AdfDatabase.parseLoopTokens(row.tokens);
|
|
5408
|
+
tokens && out.push({ seq: row.seq, tokens });
|
|
5409
|
+
}
|
|
5410
|
+
return out;
|
|
5411
|
+
}
|
|
5002
5412
|
/** Loop rows in display order: each row's seq plus its ordering key
|
|
5003
5413
|
* (COALESCE(ord, seq)). Positional operations resolve boundaries against
|
|
5004
5414
|
* ordKey, never raw seq — an ord'd summary sorts at its override position. */
|
|
@@ -5086,6 +5496,24 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
5086
5496
|
transaction(fn) {
|
|
5087
5497
|
return this.db.transaction(fn)();
|
|
5088
5498
|
}
|
|
5499
|
+
/**
|
|
5500
|
+
* `fn` in a BEGIN IMMEDIATE transaction: the file's write lock is taken
|
|
5501
|
+
* before `fn` reads, so a check-then-write in it is atomic against every
|
|
5502
|
+
* other connection (another process provisioning the same file). Inside a
|
|
5503
|
+
* transaction already, a savepoint. `busyTimeoutMs`: how long BEGIN waits
|
|
5504
|
+
* for another connection's write lock before SQLITE_BUSY (the connection's
|
|
5505
|
+
* own busy_timeout, 5 s, otherwise).
|
|
5506
|
+
*/
|
|
5507
|
+
immediateTransaction(fn, busyTimeoutMs) {
|
|
5508
|
+
if (busyTimeoutMs === void 0 || this.db.inTransaction) return this.db.transaction(fn).immediate();
|
|
5509
|
+
let before = this.db.pragma("busy_timeout", { simple: !0 });
|
|
5510
|
+
this.db.pragma(`busy_timeout = ${Math.max(0, Math.floor(busyTimeoutMs))}`);
|
|
5511
|
+
try {
|
|
5512
|
+
return this.db.transaction(fn).immediate();
|
|
5513
|
+
} finally {
|
|
5514
|
+
this.db.pragma(`busy_timeout = ${before}`);
|
|
5515
|
+
}
|
|
5516
|
+
}
|
|
5089
5517
|
// ===========================================================================
|
|
5090
5518
|
// Inbox
|
|
5091
5519
|
// ===========================================================================
|
|
@@ -5482,6 +5910,9 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
5482
5910
|
setTaskExecutorManaged(id, _value) {
|
|
5483
5911
|
this.stmts.setTaskExecutorManaged.run(id);
|
|
5484
5912
|
}
|
|
5913
|
+
setTaskApprovalMeta(id, approvalMeta) {
|
|
5914
|
+
this.stmts.setTaskApprovalMeta.run(approvalMeta, id);
|
|
5915
|
+
}
|
|
5485
5916
|
updateTaskArgs(id, args) {
|
|
5486
5917
|
this.stmts.updateTaskArgs.run(args, id);
|
|
5487
5918
|
}
|
|
@@ -5624,7 +6055,9 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
|
|
|
5624
6055
|
}
|
|
5625
6056
|
close() {
|
|
5626
6057
|
if (this.closed) return;
|
|
5627
|
-
|
|
6058
|
+
this.closed = !0;
|
|
6059
|
+
let remaining = _AdfDatabase.decrementOpen(this.filePath);
|
|
6060
|
+
if (remaining === 0 && this.getFileRefRegistry().media.clear(), remaining === 0)
|
|
5628
6061
|
try {
|
|
5629
6062
|
this.db.prepare(
|
|
5630
6063
|
"INSERT INTO adf_meta (key, value, protection) VALUES (?, ?, 'readonly') ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
@@ -5970,12 +6403,12 @@ function buildLoopPreamble(loopName, agentHandle, grantedTools = []) {
|
|
|
5970
6403
|
|
|
5971
6404
|
${reachParagraph}
|
|
5972
6405
|
|
|
5973
|
-
A timer, a trigger,
|
|
6406
|
+
A timer, a trigger, a message from another loop, or the human writing to you directly woke you. Do that work, write tersely, and end your turn.`;
|
|
5974
6407
|
}
|
|
5975
6408
|
var LOOP_GOAL_SUMMARY_CHARS = 160;
|
|
5976
6409
|
function summarizeGoal(goal) {
|
|
5977
|
-
let flat = goal.replace(/\s+/g, " ").trim();
|
|
5978
|
-
return flat.length <= LOOP_GOAL_SUMMARY_CHARS ? flat : `${flat.slice(0, LOOP_GOAL_SUMMARY_CHARS - 1).trimEnd()}\u2026
|
|
6410
|
+
let flat = (goal ?? "").replace(/\s+/g, " ").trim();
|
|
6411
|
+
return flat ? flat.length <= LOOP_GOAL_SUMMARY_CHARS ? flat : `${flat.slice(0, LOOP_GOAL_SUMMARY_CHARS - 1).trimEnd()}\u2026` : "(no goal)";
|
|
5979
6412
|
}
|
|
5980
6413
|
var LOOP_ARCHETYPES = `Use an inner loop when a piece of thinking should run on its own stream: tending memory on a timer, a sub-task that needs your full context, reviewing a draft before you act, thinking while nothing external is happening. Name each one for what it does for you. Guide: ${DOCS_GUIDES_URL}/inner-loops.md`;
|
|
5981
6414
|
function buildMainLoopsSection(loops, options) {
|
|
@@ -6081,12 +6514,12 @@ function deriveLoopConfig(parent, loop) {
|
|
|
6081
6514
|
throw new Error("deriveLoopConfig: 'main' is the host loop \u2014 use the raw agent config");
|
|
6082
6515
|
let derived = cloneJson(parent);
|
|
6083
6516
|
derived.tools = deriveTools(parent, loop);
|
|
6084
|
-
let grantedNames = derived.tools.filter((t) => t.enabled).map((t) => t.name);
|
|
6085
|
-
return derived.instructions =
|
|
6517
|
+
let grantedNames = derived.tools.filter((t) => t.enabled).map((t) => t.name), preamble = buildLoopPreamble(loop.name, parent.handle || parent.name, grantedNames);
|
|
6518
|
+
return derived.instructions = hasLoopGoal(loop) ? `${preamble}
|
|
6086
6519
|
|
|
6087
6520
|
Your goal:
|
|
6088
6521
|
|
|
6089
|
-
${loop.goal}
|
|
6522
|
+
${loop.goal}` : preamble, derived.model = cloneJson(loop.model ?? parent.model), derived.autonomous = loop.autonomous === !0, loop.compact_threshold != null && (derived.context = { ...derived.context, compact_threshold: loop.compact_threshold }), derived.triggers = deriveTriggers(parent, loop.name), derived.code_execution = {
|
|
6090
6523
|
...deriveLoopCodeExecutionFlags(parent),
|
|
6091
6524
|
// No inherited packages. A pure-JS package is loaded in the sandbox worker
|
|
6092
6525
|
// through a worker-scope `createRequire` with an UNRESTRICTED `require` —
|
|
@@ -6145,6 +6578,14 @@ var CODE_FORBIDDEN_WRITE_RE = /^crypto:/;
|
|
|
6145
6578
|
function isCodeForbiddenIdentityWrite(purpose) {
|
|
6146
6579
|
return CODE_FORBIDDEN_WRITE_RE.test(purpose);
|
|
6147
6580
|
}
|
|
6581
|
+
function parseLoopContent(json) {
|
|
6582
|
+
try {
|
|
6583
|
+
let parsed = JSON.parse(json);
|
|
6584
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
6585
|
+
} catch {
|
|
6586
|
+
return [];
|
|
6587
|
+
}
|
|
6588
|
+
}
|
|
6148
6589
|
var AdfWorkspace = class _AdfWorkspace {
|
|
6149
6590
|
db;
|
|
6150
6591
|
filePath;
|
|
@@ -6812,6 +7253,14 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
6812
7253
|
getAgentConfig() {
|
|
6813
7254
|
return this.db.getConfig();
|
|
6814
7255
|
}
|
|
7256
|
+
/** Config writes through this file's connection so far (AdfDatabase.configWriteCount); no SQLite read. */
|
|
7257
|
+
configWriteCount() {
|
|
7258
|
+
return this.db.configWriteCount();
|
|
7259
|
+
}
|
|
7260
|
+
/** Commits by other connections to this file (AdfDatabase.dataVersion); no table read. */
|
|
7261
|
+
dataVersion() {
|
|
7262
|
+
return this.db.dataVersion();
|
|
7263
|
+
}
|
|
6815
7264
|
setAgentConfig(config) {
|
|
6816
7265
|
let previous = null;
|
|
6817
7266
|
try {
|
|
@@ -7151,6 +7600,10 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7151
7600
|
getLastAssistantTokens() {
|
|
7152
7601
|
return this.db.getLastAssistantTokens(this.boundLoop);
|
|
7153
7602
|
}
|
|
7603
|
+
/** Every assistant row's usage record in loop order — see AdfDatabase.getAssistantTokenRows. */
|
|
7604
|
+
getAssistantTokenRows() {
|
|
7605
|
+
return this.db.getAssistantTokenRows(this.boundLoop);
|
|
7606
|
+
}
|
|
7154
7607
|
/** Meta key of this loop's context baseline: bare for main, `:<loop>`-suffixed
|
|
7155
7608
|
* for side loops (one baseline per cognition stream). */
|
|
7156
7609
|
contextBaselineKey() {
|
|
@@ -7522,12 +7975,12 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7522
7975
|
let deleted2 = !1;
|
|
7523
7976
|
return this.db.transaction(() => {
|
|
7524
7977
|
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;
|
|
7978
|
+
}), deleted2 && (this.emitUmbilical("file.deleted", { path: relativePath }), this.emitFileChange(relativePath, "deleted", void 0, entry?.content, metadata), this.rewriteFileRefs([{ op: "delete", path: relativePath }])), deleted2;
|
|
7526
7979
|
}
|
|
7527
7980
|
let previous = this.onFileChangeCallback ? this.db.readFile(relativePath)?.content : void 0;
|
|
7528
7981
|
opts?.force && this.db.fileExists(relativePath) && this.db.setFileProtection(relativePath, "none");
|
|
7529
7982
|
let deleted = this.db.deleteFile(relativePath);
|
|
7530
|
-
return deleted && (this.emitUmbilical("file.deleted", { path: relativePath }), this.emitFileChange(relativePath, "deleted", void 0, previous, metadata)), deleted;
|
|
7983
|
+
return deleted && (this.emitUmbilical("file.deleted", { path: relativePath }), this.emitFileChange(relativePath, "deleted", void 0, previous, metadata), this.rewriteFileRefs([{ op: "delete", path: relativePath }])), deleted;
|
|
7531
7984
|
}
|
|
7532
7985
|
getFileMeta(path) {
|
|
7533
7986
|
return this.db.getFileMeta(path);
|
|
@@ -7547,7 +8000,10 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7547
8000
|
return this.db.fileExists(relativePath);
|
|
7548
8001
|
}
|
|
7549
8002
|
renameInternalFile(oldPath, newPath) {
|
|
7550
|
-
let previous = this.onFileChangeCallback ? this.db.readFile(oldPath)?.content : void 0, metadata = this.fileChangeMeta(oldPath), renamed = this.db.
|
|
8003
|
+
let previous = this.onFileChangeCallback ? this.db.readFile(oldPath)?.content : void 0, metadata = this.fileChangeMeta(oldPath), renamed = this.db.transaction(() => {
|
|
8004
|
+
let done = this.db.renameFile(oldPath, newPath);
|
|
8005
|
+
return done && this.rewriteFileRefs([{ op: "rename", from: oldPath, to: newPath }]), done;
|
|
8006
|
+
});
|
|
7551
8007
|
return renamed && (this.emitFileChange(oldPath, "deleted", void 0, previous, metadata), this.emitFileChange(newPath, "created", previous, void 0, this.fileChangeMeta(newPath))), renamed;
|
|
7552
8008
|
}
|
|
7553
8009
|
renameFolder(oldPrefix, newPrefix) {
|
|
@@ -7555,14 +8011,15 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7555
8011
|
path: file.path,
|
|
7556
8012
|
content: notify ? this.db.readFile(file.path)?.content : void 0,
|
|
7557
8013
|
metadata: notify ? this.getFileMeta(file.path) : null
|
|
7558
|
-
})), count = this.db.
|
|
7559
|
-
|
|
7560
|
-
|
|
8014
|
+
})), replacementPrefix = newPrefix.endsWith("/") ? newPrefix : `${newPrefix}/`, count = this.db.transaction(() => {
|
|
8015
|
+
let changed = this.db.renameFolder(oldPrefix, newPrefix);
|
|
8016
|
+
return changed > 0 && this.rewriteFileRefs(moved.map((file) => ({ op: "rename", from: file.path, to: replacementPrefix + file.path.slice(prefix.length) }))), changed;
|
|
8017
|
+
});
|
|
8018
|
+
if (count > 0)
|
|
7561
8019
|
for (let file of moved) {
|
|
7562
8020
|
let newPath = replacementPrefix + file.path.slice(prefix.length);
|
|
7563
8021
|
this.emitFileChange(file.path, "deleted", void 0, file.content, file.metadata), this.emitFileChange(newPath, "created", file.content, void 0, this.fileChangeMeta(newPath));
|
|
7564
8022
|
}
|
|
7565
|
-
}
|
|
7566
8023
|
return count;
|
|
7567
8024
|
}
|
|
7568
8025
|
// The sinks are per-agent, not per-loop: registering through a forLoop() view
|
|
@@ -7597,8 +8054,38 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7597
8054
|
} catch {
|
|
7598
8055
|
}
|
|
7599
8056
|
}
|
|
8057
|
+
/**
|
|
8058
|
+
* Keep file references (`file_ref` blocks) pointing at the right file: a
|
|
8059
|
+
* rename or move rewrites the path, a delete leaves a tombstone. Rewrites
|
|
8060
|
+
* every loop row that names the file, then every live session's copy. Only
|
|
8061
|
+
* files some row references cost a scan; the set of referenced paths is
|
|
8062
|
+
* built once per process and kept current by loop writes.
|
|
8063
|
+
*/
|
|
8064
|
+
rewriteFileRefs(changes) {
|
|
8065
|
+
if (changes.length === 0) return;
|
|
8066
|
+
let registry3 = this.db.getFileRefRegistry();
|
|
8067
|
+
try {
|
|
8068
|
+
if (!registry3.referenced) {
|
|
8069
|
+
let referenced = /* @__PURE__ */ new Set();
|
|
8070
|
+
for (let row of this.db.getLoopRowsWithFileRefs())
|
|
8071
|
+
for (let block of parseLoopContent(row.content_json))
|
|
8072
|
+
isFileRefBlock(block) && !block.file_ref.deleted && referenced.add(block.file_ref.path);
|
|
8073
|
+
registry3.referenced = referenced;
|
|
8074
|
+
}
|
|
8075
|
+
if (!registry3.affects(changes)) return;
|
|
8076
|
+
this.db.transaction(() => {
|
|
8077
|
+
for (let row of this.db.getLoopRowsWithFileRefs()) {
|
|
8078
|
+
let content = parseLoopContent(row.content_json), next = applyFileRefChanges(content, changes);
|
|
8079
|
+
next !== content && this.db.updateLoopContent(row.loop, row.seq, next);
|
|
8080
|
+
}
|
|
8081
|
+
}), registry3.noteChanges(changes);
|
|
8082
|
+
} catch (error) {
|
|
8083
|
+
console.error("[AdfWorkspace] Could not update file references:", error);
|
|
8084
|
+
}
|
|
8085
|
+
registry3.notifyHolders(changes);
|
|
8086
|
+
}
|
|
7600
8087
|
emitFileChange(path, operation, content, previousContent, metadata) {
|
|
7601
|
-
if (this.skillIndexer.notifyPath(path), !this.onFileChangeCallback) return;
|
|
8088
|
+
if (this.db.getFileRefRegistry().invalidate(path), this.skillIndexer.notifyPath(path), this.emitDataChange("files"), !this.onFileChangeCallback) return;
|
|
7602
8089
|
let mimeType = metadata?.mime_type ?? this.getMimeType(path);
|
|
7603
8090
|
this.onFileChangeCallback({
|
|
7604
8091
|
path,
|
|
@@ -7738,6 +8225,9 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7738
8225
|
setTaskExecutorManaged(id, value) {
|
|
7739
8226
|
this.db.setTaskExecutorManaged(id, value);
|
|
7740
8227
|
}
|
|
8228
|
+
setTaskApprovalMeta(id, approvalMeta) {
|
|
8229
|
+
this.db.setTaskApprovalMeta(id, approvalMeta);
|
|
8230
|
+
}
|
|
7741
8231
|
updateTaskArgs(id, args) {
|
|
7742
8232
|
this.db.updateTaskArgs(id, args);
|
|
7743
8233
|
}
|
|
@@ -7884,8 +8374,39 @@ var AdfWorkspace = class _AdfWorkspace {
|
|
|
7884
8374
|
transaction(fn) {
|
|
7885
8375
|
return this.db.transaction(fn);
|
|
7886
8376
|
}
|
|
8377
|
+
/** AdfDatabase.immediateTransaction: an atomic check-then-write against other connections to the file. */
|
|
8378
|
+
immediateTransaction(fn, busyTimeoutMs) {
|
|
8379
|
+
return this.db.immediateTransaction(fn, busyTimeoutMs);
|
|
8380
|
+
}
|
|
7887
8381
|
};
|
|
7888
8382
|
|
|
8383
|
+
// src/main/runtime/loop-usage.ts
|
|
8384
|
+
function summarizeLoopUsage(entries) {
|
|
8385
|
+
let totals = createUsageTotals(), byModel = /* @__PURE__ */ new Map(), usageRows = 0, estimatedRows = 0;
|
|
8386
|
+
for (let entry of entries) {
|
|
8387
|
+
if (!entry.tokens) continue;
|
|
8388
|
+
usageRows++, hasReportedUsage(entry.tokens) || estimatedRows++, addUsage(totals, entry.tokens);
|
|
8389
|
+
let model = entry.model ?? "unknown", bucket = byModel.get(model);
|
|
8390
|
+
bucket || (bucket = { model, rows: 0, ...createUsageTotals() }, byModel.set(model, bucket)), bucket.rows++, addUsage(bucket, entry.tokens);
|
|
8391
|
+
}
|
|
8392
|
+
return {
|
|
8393
|
+
loopRows: entries.length,
|
|
8394
|
+
usageRows,
|
|
8395
|
+
estimatedRows,
|
|
8396
|
+
totals,
|
|
8397
|
+
byModel: Array.from(byModel.values()).sort((a, b) => b.total - a.total)
|
|
8398
|
+
};
|
|
8399
|
+
}
|
|
8400
|
+
function createUsageTotals() {
|
|
8401
|
+
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
8402
|
+
}
|
|
8403
|
+
function addUsage(target, usage) {
|
|
8404
|
+
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;
|
|
8405
|
+
}
|
|
8406
|
+
function hasReportedUsage(t) {
|
|
8407
|
+
return t.estimated ? !1 : t.cache_read !== void 0 || t.cache_write !== void 0 || t.reasoning !== void 0 || t.cost_usd !== void 0;
|
|
8408
|
+
}
|
|
8409
|
+
|
|
7889
8410
|
// src/main/tools/shell/commands/status.ts
|
|
7890
8411
|
function staticArgValue3(arg) {
|
|
7891
8412
|
if (!arg) return null;
|
|
@@ -8062,7 +8583,7 @@ async function configTools(ctx) {
|
|
|
8062
8583
|
var statusHandler = {
|
|
8063
8584
|
name: "status",
|
|
8064
8585
|
summary: "Show agent status summary",
|
|
8065
|
-
helpText: "status State, token usage
|
|
8586
|
+
helpText: "status State, model, token usage in the current loop",
|
|
8066
8587
|
category: "identity",
|
|
8067
8588
|
resolvedTools: ["sys_get_config"],
|
|
8068
8589
|
async execute(ctx) {
|
|
@@ -8072,7 +8593,7 @@ var statusHandler = {
|
|
|
8072
8593
|
`Model: ${config.model?.provider ?? ""}/${config.model?.model_id ?? ""}`,
|
|
8073
8594
|
`Autonomy: ${config.autonomous ? "enabled" : "disabled"}`
|
|
8074
8595
|
];
|
|
8075
|
-
return
|
|
8596
|
+
return lines.push(`Tokens: ${tokenUsageLine(ctx)}`), ok(lines.join(`
|
|
8076
8597
|
`));
|
|
8077
8598
|
}
|
|
8078
8599
|
}, stateHandler = {
|
|
@@ -8231,6 +8752,17 @@ var statusHandler = {
|
|
|
8231
8752
|
falseHandler,
|
|
8232
8753
|
sleepHandler
|
|
8233
8754
|
];
|
|
8755
|
+
function tokenUsageLine(ctx) {
|
|
8756
|
+
let usage;
|
|
8757
|
+
try {
|
|
8758
|
+
usage = summarizeLoopUsage(ctx.workspace.getLoop());
|
|
8759
|
+
} catch {
|
|
8760
|
+
return "unavailable";
|
|
8761
|
+
}
|
|
8762
|
+
if (usage.usageRows === 0) return "none recorded in the current loop";
|
|
8763
|
+
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}`;
|
|
8764
|
+
return `${n(total)} total: ${n(input)} in${cached}, ${n(output)} out; ${calls} in the current loop`;
|
|
8765
|
+
}
|
|
8234
8766
|
|
|
8235
8767
|
// src/main/tools/shell/commands/meta.ts
|
|
8236
8768
|
var metaHandler = {
|
|
@@ -8426,7 +8958,7 @@ var CATEGORY_ORDER = [
|
|
|
8426
8958
|
resolvedTools: [],
|
|
8427
8959
|
async execute(ctx) {
|
|
8428
8960
|
if (ctx.args.length > 0) {
|
|
8429
|
-
let { getCommand: getCommand2 } = await import("./commands-
|
|
8961
|
+
let { getCommand: getCommand2 } = await import("./commands-KOMPXJZS.mjs"), handler = getCommand2(ctx.args[0]);
|
|
8430
8962
|
return handler ? ok(handler.helpText) : ok(`Unknown command: ${ctx.args[0]}`);
|
|
8431
8963
|
}
|
|
8432
8964
|
let commands = getAllCommands(), grouped = /* @__PURE__ */ new Map();
|
|
@@ -8495,10 +9027,13 @@ export {
|
|
|
8495
9027
|
verifyEd25519,
|
|
8496
9028
|
rawPublicKeyToSpki,
|
|
8497
9029
|
didToPublicKey,
|
|
9030
|
+
ADF_LATEST_SCHEMA_VERSION,
|
|
8498
9031
|
AdfDatabase,
|
|
8499
9032
|
withSource,
|
|
8500
9033
|
withLoop,
|
|
8501
9034
|
withTurn,
|
|
9035
|
+
currentTurnId,
|
|
9036
|
+
currentAgentId,
|
|
8502
9037
|
currentSourceOrUnknown,
|
|
8503
9038
|
init_execution_context,
|
|
8504
9039
|
SKILLS_REGISTRY_PATH,
|
|
@@ -8509,6 +9044,7 @@ export {
|
|
|
8509
9044
|
rawX25519PrivateToPkcs8,
|
|
8510
9045
|
rawX25519PublicToSpki,
|
|
8511
9046
|
extractRawX25519PublicKey,
|
|
9047
|
+
openKeySlot,
|
|
8512
9048
|
sealWithDek,
|
|
8513
9049
|
openWithDek,
|
|
8514
9050
|
envelopeFromAlgo,
|
|
@@ -8532,12 +9068,11 @@ export {
|
|
|
8532
9068
|
isCodeForbiddenIdentityWrite,
|
|
8533
9069
|
AdfWorkspace,
|
|
8534
9070
|
isTextMime,
|
|
8535
|
-
isVisionMime,
|
|
8536
|
-
isAudioInputMime,
|
|
8537
|
-
isVideoInputMime,
|
|
8538
9071
|
mimeToAudioFormat,
|
|
8539
9072
|
formatSize,
|
|
8540
9073
|
mimeToExt,
|
|
9074
|
+
summarizeLoopUsage,
|
|
9075
|
+
hasReportedUsage,
|
|
8541
9076
|
EXIT,
|
|
8542
9077
|
err,
|
|
8543
9078
|
shellReadFile,
|