@rulvar/core 1.52.0 → 1.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +261 -7
- package/dist/index.js +885 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createHash, getRandomValues, randomUUID } from "node:crypto";
|
|
2
|
-
import { appendFileSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join, resolve, sep } from "node:path";
|
|
2
|
+
import { appendFileSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import path, { dirname, join, resolve, sep } from "node:path";
|
|
4
4
|
import { Client } from "@modelcontextprotocol/sdk/client";
|
|
5
5
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
6
6
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7
7
|
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
8
8
|
import { ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
9
9
|
import { execFile } from "node:child_process";
|
|
10
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
10
|
+
import { mkdtemp, readFile, readdir, realpath, rm, stat } from "node:fs/promises";
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
12
|
import { promisify } from "node:util";
|
|
13
13
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -3297,6 +3297,459 @@ var GitWorktreeProvider = class {
|
|
|
3297
3297
|
}
|
|
3298
3298
|
};
|
|
3299
3299
|
//#endregion
|
|
3300
|
+
//#region src/tools/research.ts
|
|
3301
|
+
/**
|
|
3302
|
+
* The standard repository research toolset (RV-210 remainder): paginated
|
|
3303
|
+
* list/search/read tools over a confined directory root with STABLE
|
|
3304
|
+
* keyset cursors, plus an evidence collector that verifies citations at
|
|
3305
|
+
* collection time. Design contract:
|
|
3306
|
+
*
|
|
3307
|
+
* - Responses are CANONICAL: a page is a pure function of (root
|
|
3308
|
+
* filesystem state, logical window), never of how the window was
|
|
3309
|
+
* addressed, so reading the same page through a cursor and through
|
|
3310
|
+
* fresh arguments returns byte-identical results. That is what makes
|
|
3311
|
+
* the RV-210 `maxNoNewEvidenceCalls` guard measure duplicate-page
|
|
3312
|
+
* reads correctly, and the `maxRepeatedToolSignature` guard already
|
|
3313
|
+
* denies byte-identical repeat calls: deduplication is composition,
|
|
3314
|
+
* not a marker field.
|
|
3315
|
+
* - Cursors are keyset cursors (the last path / line, not an offset), so
|
|
3316
|
+
* a page boundary never shifts when unrelated entries appear or
|
|
3317
|
+
* disappear, and every cursor embeds the query identity: replaying a
|
|
3318
|
+
* cursor against different arguments is a typed error result.
|
|
3319
|
+
* - Ordering is deterministic byte order (UTF-16 code unit sort), never
|
|
3320
|
+
* locale collation.
|
|
3321
|
+
* - The root confines everything: relative paths only, `..` escapes and
|
|
3322
|
+
* symlink escapes are typed error results, symlinked directories are
|
|
3323
|
+
* never walked.
|
|
3324
|
+
* - User-level failures (bad path, binary file, oversized file, invalid
|
|
3325
|
+
* cursor, an unverifiable citation) are RETURNED `{ error }` values,
|
|
3326
|
+
* deterministic and visible to the model; only host misconfiguration
|
|
3327
|
+
* throws (ConfigError at construction).
|
|
3328
|
+
* - Tool results are journaled at execution time, so replay never
|
|
3329
|
+
* touches the filesystem; live pages read the live tree.
|
|
3330
|
+
*
|
|
3331
|
+
* Public docs: https://docs.rulvar.com/guide/tools
|
|
3332
|
+
*/
|
|
3333
|
+
const DEFAULT_PAGE_SIZE = 50;
|
|
3334
|
+
const DEFAULT_READ_PAGE_CHARS = 4e3;
|
|
3335
|
+
const DEFAULT_MAX_FILE_BYTES = 262144;
|
|
3336
|
+
const DEFAULT_MAX_SCANNED_FILES = 2e4;
|
|
3337
|
+
const ALWAYS_IGNORED = [".git", "node_modules"];
|
|
3338
|
+
const SEARCH_SNIPPET_CHARS = 200;
|
|
3339
|
+
const BINARY_SNIFF_BYTES = 8192;
|
|
3340
|
+
const LIST_SCHEMA = {
|
|
3341
|
+
type: "object",
|
|
3342
|
+
additionalProperties: false,
|
|
3343
|
+
properties: {
|
|
3344
|
+
dir: {
|
|
3345
|
+
type: "string",
|
|
3346
|
+
description: "Root-relative directory to list; default the root."
|
|
3347
|
+
},
|
|
3348
|
+
cursor: {
|
|
3349
|
+
type: "string",
|
|
3350
|
+
description: "Opaque cursor from a previous page."
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
};
|
|
3354
|
+
const SEARCH_SCHEMA = {
|
|
3355
|
+
type: "object",
|
|
3356
|
+
additionalProperties: false,
|
|
3357
|
+
required: ["query"],
|
|
3358
|
+
properties: {
|
|
3359
|
+
query: {
|
|
3360
|
+
type: "string",
|
|
3361
|
+
minLength: 1,
|
|
3362
|
+
description: "Literal substring to find."
|
|
3363
|
+
},
|
|
3364
|
+
dir: {
|
|
3365
|
+
type: "string",
|
|
3366
|
+
description: "Root-relative directory to search; default the root."
|
|
3367
|
+
},
|
|
3368
|
+
cursor: {
|
|
3369
|
+
type: "string",
|
|
3370
|
+
description: "Opaque cursor from a previous page."
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
};
|
|
3374
|
+
const READ_SCHEMA = {
|
|
3375
|
+
type: "object",
|
|
3376
|
+
additionalProperties: false,
|
|
3377
|
+
required: ["path"],
|
|
3378
|
+
properties: {
|
|
3379
|
+
path: {
|
|
3380
|
+
type: "string",
|
|
3381
|
+
minLength: 1,
|
|
3382
|
+
description: "Root-relative file path."
|
|
3383
|
+
},
|
|
3384
|
+
cursor: {
|
|
3385
|
+
type: "string",
|
|
3386
|
+
description: "Opaque cursor from a previous page."
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
};
|
|
3390
|
+
const RECORD_EVIDENCE_SCHEMA = {
|
|
3391
|
+
type: "object",
|
|
3392
|
+
additionalProperties: false,
|
|
3393
|
+
required: ["claim", "file"],
|
|
3394
|
+
properties: {
|
|
3395
|
+
claim: {
|
|
3396
|
+
type: "string",
|
|
3397
|
+
minLength: 1,
|
|
3398
|
+
description: "The claim this evidence supports."
|
|
3399
|
+
},
|
|
3400
|
+
file: {
|
|
3401
|
+
type: "string",
|
|
3402
|
+
minLength: 1,
|
|
3403
|
+
description: "Root-relative file the claim cites."
|
|
3404
|
+
},
|
|
3405
|
+
lines: {
|
|
3406
|
+
type: "string",
|
|
3407
|
+
description: "Cited line or range, 1-based: '12' or '12-40'."
|
|
3408
|
+
},
|
|
3409
|
+
quote: {
|
|
3410
|
+
type: "string",
|
|
3411
|
+
minLength: 1,
|
|
3412
|
+
description: "Verbatim quote; verified to appear in the file."
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
};
|
|
3416
|
+
const LIST_EVIDENCE_SCHEMA = {
|
|
3417
|
+
type: "object",
|
|
3418
|
+
additionalProperties: false,
|
|
3419
|
+
properties: { cursor: {
|
|
3420
|
+
type: "string",
|
|
3421
|
+
description: "Opaque cursor from a previous page."
|
|
3422
|
+
} }
|
|
3423
|
+
};
|
|
3424
|
+
function encodeCursor(payload) {
|
|
3425
|
+
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
3426
|
+
}
|
|
3427
|
+
function decodeCursor(raw) {
|
|
3428
|
+
try {
|
|
3429
|
+
return JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
|
|
3430
|
+
} catch {
|
|
3431
|
+
return;
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
function isBinary(buffer) {
|
|
3435
|
+
return buffer.subarray(0, BINARY_SNIFF_BYTES).includes(0);
|
|
3436
|
+
}
|
|
3437
|
+
/** Split into lines on LF; a trailing CR per line is presentation, not content. */
|
|
3438
|
+
function splitLines(text) {
|
|
3439
|
+
return text.split("\n").map((line) => line.endsWith("\r") ? line.slice(0, -1) : line);
|
|
3440
|
+
}
|
|
3441
|
+
function repositoryResearchToolset(options) {
|
|
3442
|
+
if (typeof options.root !== "string" || options.root.length === 0) throw new ConfigError("repositoryResearchToolset root must be a non-empty string");
|
|
3443
|
+
let realRoot;
|
|
3444
|
+
try {
|
|
3445
|
+
realRoot = realpathSync(path.resolve(options.root));
|
|
3446
|
+
} catch {
|
|
3447
|
+
throw new ConfigError(`repositoryResearchToolset root '${options.root}' does not exist`);
|
|
3448
|
+
}
|
|
3449
|
+
if (!statSync(realRoot).isDirectory()) throw new ConfigError(`repositoryResearchToolset root '${options.root}' is not a directory`);
|
|
3450
|
+
for (const [name, value] of [
|
|
3451
|
+
["pageSize", options.pageSize],
|
|
3452
|
+
["readPageChars", options.readPageChars],
|
|
3453
|
+
["maxFileBytes", options.maxFileBytes],
|
|
3454
|
+
["maxScannedFiles", options.maxScannedFiles]
|
|
3455
|
+
]) if (value !== void 0 && (!Number.isSafeInteger(value) || value < 1)) throw new ConfigError(`repositoryResearchToolset ${name} must be a positive integer; got ${String(value)}`);
|
|
3456
|
+
const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
3457
|
+
const readPageChars = options.readPageChars ?? DEFAULT_READ_PAGE_CHARS;
|
|
3458
|
+
const maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
3459
|
+
const maxScannedFiles = options.maxScannedFiles ?? DEFAULT_MAX_SCANNED_FILES;
|
|
3460
|
+
const includeHidden = options.includeHidden ?? false;
|
|
3461
|
+
const ignored = /* @__PURE__ */ new Set([...ALWAYS_IGNORED, ...options.ignore ?? []]);
|
|
3462
|
+
const evidence = [];
|
|
3463
|
+
/**
|
|
3464
|
+
* Resolves a root-relative POSIX path and confines it: absolute paths,
|
|
3465
|
+
* `..` escapes, and symlink escapes are error strings, never throws.
|
|
3466
|
+
* `mustExist` additionally resolves symlinks and re-checks containment
|
|
3467
|
+
* of the REAL path (the FileTranscriptStore traversal lesson).
|
|
3468
|
+
*/
|
|
3469
|
+
const resolveWithin = async (rel, kind) => {
|
|
3470
|
+
const normalizedInput = rel.replaceAll("\\", "/");
|
|
3471
|
+
if (path.posix.isAbsolute(normalizedInput) || path.isAbsolute(rel)) return { error: `path must be relative to the research root; got '${rel}'` };
|
|
3472
|
+
const normalized = path.posix.normalize(normalizedInput);
|
|
3473
|
+
if (normalized === ".." || normalized.startsWith("../")) return { error: `path escapes the research root: '${rel}'` };
|
|
3474
|
+
const cleaned = normalized === "." ? "" : normalized;
|
|
3475
|
+
if (kind === "file" && cleaned === "") return { error: "path must name a file inside the research root" };
|
|
3476
|
+
const abs = path.resolve(realRoot, cleaned);
|
|
3477
|
+
let real;
|
|
3478
|
+
try {
|
|
3479
|
+
real = await realpath(abs);
|
|
3480
|
+
} catch {
|
|
3481
|
+
return { error: `no such ${kind} under the research root: '${cleaned === "" ? "." : cleaned}'` };
|
|
3482
|
+
}
|
|
3483
|
+
if (real !== realRoot && !real.startsWith(realRoot + path.sep)) return { error: `path escapes the research root: '${rel}'` };
|
|
3484
|
+
try {
|
|
3485
|
+
const info = await stat(real);
|
|
3486
|
+
if (kind === "file" && !info.isFile()) return { error: `not a regular file: '${cleaned}'` };
|
|
3487
|
+
if (kind === "dir" && !info.isDirectory()) return { error: `not a directory: '${cleaned === "" ? "." : cleaned}'` };
|
|
3488
|
+
} catch {
|
|
3489
|
+
return { error: `no such ${kind} under the research root: '${cleaned === "" ? "." : cleaned}'` };
|
|
3490
|
+
}
|
|
3491
|
+
return {
|
|
3492
|
+
abs: real,
|
|
3493
|
+
rel: cleaned
|
|
3494
|
+
};
|
|
3495
|
+
};
|
|
3496
|
+
/**
|
|
3497
|
+
* Deterministic recursive walk: sorted entries, ignored and hidden
|
|
3498
|
+
* names skipped, symlinks never followed, regular files only. Returns
|
|
3499
|
+
* root-relative POSIX paths in byte order, or an error when the walk
|
|
3500
|
+
* exceeds maxScannedFiles.
|
|
3501
|
+
*/
|
|
3502
|
+
const walkFiles = async (absDir, relDir) => {
|
|
3503
|
+
const files = [];
|
|
3504
|
+
let visited = 0;
|
|
3505
|
+
const recurse = async (dirAbs, dirRel) => {
|
|
3506
|
+
let entries;
|
|
3507
|
+
try {
|
|
3508
|
+
entries = await readdir(dirAbs, { withFileTypes: true });
|
|
3509
|
+
} catch {
|
|
3510
|
+
return `directory disappeared during the walk: '${dirRel === "" ? "." : dirRel}'`;
|
|
3511
|
+
}
|
|
3512
|
+
const sorted = [...entries].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
3513
|
+
for (const entry of sorted) {
|
|
3514
|
+
const name = entry.name;
|
|
3515
|
+
if (ignored.has(name)) continue;
|
|
3516
|
+
if (!includeHidden && name.startsWith(".")) continue;
|
|
3517
|
+
const childRel = dirRel === "" ? name : `${dirRel}/${name}`;
|
|
3518
|
+
if (entry.isDirectory()) {
|
|
3519
|
+
const failure = await recurse(path.join(dirAbs, name), childRel);
|
|
3520
|
+
if (failure !== void 0) return failure;
|
|
3521
|
+
continue;
|
|
3522
|
+
}
|
|
3523
|
+
if (!entry.isFile()) continue;
|
|
3524
|
+
visited += 1;
|
|
3525
|
+
if (visited > maxScannedFiles) return `the walk exceeded maxScannedFiles (${String(maxScannedFiles)}); narrow dir or raise the limit`;
|
|
3526
|
+
files.push(childRel);
|
|
3527
|
+
}
|
|
3528
|
+
};
|
|
3529
|
+
const failure = await recurse(absDir, relDir);
|
|
3530
|
+
if (failure !== void 0) return { error: failure };
|
|
3531
|
+
return { files };
|
|
3532
|
+
};
|
|
3533
|
+
const loadTextFile = async (abs, rel) => {
|
|
3534
|
+
let info;
|
|
3535
|
+
try {
|
|
3536
|
+
info = await stat(abs);
|
|
3537
|
+
} catch {
|
|
3538
|
+
return { error: `no such file under the research root: '${rel}'` };
|
|
3539
|
+
}
|
|
3540
|
+
if (info.size > maxFileBytes) return { error: `file '${rel}' is ${String(info.size)} bytes, over the maxFileBytes limit (${String(maxFileBytes)})` };
|
|
3541
|
+
const buffer = await readFile(abs);
|
|
3542
|
+
if (isBinary(buffer)) return { error: `file '${rel}' is binary` };
|
|
3543
|
+
return { text: buffer.toString("utf8") };
|
|
3544
|
+
};
|
|
3545
|
+
return {
|
|
3546
|
+
tools: [
|
|
3547
|
+
tool({
|
|
3548
|
+
name: "list_files",
|
|
3549
|
+
description: "List files under a directory of the research root, recursively, in deterministic byte order, one page at a time. Returns { files, totalFiles, nextCursor? }; pass cursor to continue where the last page ended (the cursor is stable: unrelated changes never shift the boundary).",
|
|
3550
|
+
parameters: LIST_SCHEMA,
|
|
3551
|
+
risk: "read",
|
|
3552
|
+
execute: async (input) => {
|
|
3553
|
+
const params = input;
|
|
3554
|
+
let dir = params.dir ?? "";
|
|
3555
|
+
let after;
|
|
3556
|
+
if (params.cursor !== void 0) {
|
|
3557
|
+
const payload = decodeCursor(params.cursor);
|
|
3558
|
+
if (payload === void 0 || payload.t !== "list" || typeof payload.dir !== "string" || typeof payload.after !== "string" || params.dir !== void 0 && params.dir !== payload.dir) return { error: "invalid cursor: pass the nextCursor of a previous list_files page" };
|
|
3559
|
+
dir = payload.dir;
|
|
3560
|
+
after = payload.after;
|
|
3561
|
+
}
|
|
3562
|
+
const resolved = await resolveWithin(dir, "dir");
|
|
3563
|
+
if ("error" in resolved) return resolved;
|
|
3564
|
+
const walked = await walkFiles(resolved.abs, resolved.rel);
|
|
3565
|
+
if ("error" in walked) return walked;
|
|
3566
|
+
const remaining = after === void 0 ? walked.files : walked.files.filter((file) => file > after);
|
|
3567
|
+
const page = remaining.slice(0, pageSize);
|
|
3568
|
+
const more = remaining.length > pageSize;
|
|
3569
|
+
return {
|
|
3570
|
+
files: page,
|
|
3571
|
+
totalFiles: walked.files.length,
|
|
3572
|
+
...more ? { nextCursor: encodeCursor({
|
|
3573
|
+
t: "list",
|
|
3574
|
+
dir: resolved.rel,
|
|
3575
|
+
after: page[page.length - 1]
|
|
3576
|
+
}) } : {}
|
|
3577
|
+
};
|
|
3578
|
+
}
|
|
3579
|
+
}),
|
|
3580
|
+
tool({
|
|
3581
|
+
name: "search_files",
|
|
3582
|
+
description: "Search files under the research root for a literal substring (case-sensitive, never a regex), one page of matches at a time in deterministic (path, line) order. Returns { matches: [{ file, line, text }], filesScanned, filesSkipped, nextCursor? }; binary and oversized files are skipped and counted.",
|
|
3583
|
+
parameters: SEARCH_SCHEMA,
|
|
3584
|
+
risk: "read",
|
|
3585
|
+
execute: async (input) => {
|
|
3586
|
+
const params = input;
|
|
3587
|
+
let dir = params.dir ?? "";
|
|
3588
|
+
let query = params.query;
|
|
3589
|
+
let after;
|
|
3590
|
+
if (params.cursor !== void 0) {
|
|
3591
|
+
const payload = decodeCursor(params.cursor);
|
|
3592
|
+
if (payload === void 0 || payload.t !== "search" || typeof payload.query !== "string" || typeof payload.dir !== "string" || typeof payload.file !== "string" || typeof payload.line !== "number" || payload.query !== params.query || params.dir !== void 0 && params.dir !== payload.dir) return { error: "invalid cursor: pass the nextCursor of a previous search_files page with the same query and dir" };
|
|
3593
|
+
dir = payload.dir;
|
|
3594
|
+
query = payload.query;
|
|
3595
|
+
after = {
|
|
3596
|
+
file: payload.file,
|
|
3597
|
+
line: payload.line
|
|
3598
|
+
};
|
|
3599
|
+
}
|
|
3600
|
+
if (query.length === 0) return { error: "query must be a non-empty literal substring" };
|
|
3601
|
+
const resolved = await resolveWithin(dir, "dir");
|
|
3602
|
+
if ("error" in resolved) return resolved;
|
|
3603
|
+
const walked = await walkFiles(resolved.abs, resolved.rel);
|
|
3604
|
+
if ("error" in walked) return walked;
|
|
3605
|
+
const matches = [];
|
|
3606
|
+
let filesScanned = 0;
|
|
3607
|
+
let filesSkipped = 0;
|
|
3608
|
+
for (const file of walked.files) {
|
|
3609
|
+
const loaded = await loadTextFile(path.join(realRoot, file), file);
|
|
3610
|
+
if ("error" in loaded) {
|
|
3611
|
+
filesSkipped += 1;
|
|
3612
|
+
continue;
|
|
3613
|
+
}
|
|
3614
|
+
filesScanned += 1;
|
|
3615
|
+
const lines = splitLines(loaded.text);
|
|
3616
|
+
for (let index = 0; index < lines.length; index += 1) if (lines[index].includes(query)) matches.push({
|
|
3617
|
+
file,
|
|
3618
|
+
line: index + 1,
|
|
3619
|
+
text: lines[index].trim().slice(0, SEARCH_SNIPPET_CHARS)
|
|
3620
|
+
});
|
|
3621
|
+
}
|
|
3622
|
+
const remaining = after === void 0 ? matches : matches.filter((match) => match.file > after.file || match.file === after.file && match.line > after.line);
|
|
3623
|
+
const page = remaining.slice(0, pageSize);
|
|
3624
|
+
const more = remaining.length > pageSize;
|
|
3625
|
+
const last = page[page.length - 1];
|
|
3626
|
+
return {
|
|
3627
|
+
matches: page,
|
|
3628
|
+
filesScanned,
|
|
3629
|
+
filesSkipped,
|
|
3630
|
+
...more && last !== void 0 ? { nextCursor: encodeCursor({
|
|
3631
|
+
t: "search",
|
|
3632
|
+
query,
|
|
3633
|
+
dir: resolved.rel,
|
|
3634
|
+
file: last.file,
|
|
3635
|
+
line: last.line
|
|
3636
|
+
}) } : {}
|
|
3637
|
+
};
|
|
3638
|
+
}
|
|
3639
|
+
}),
|
|
3640
|
+
tool({
|
|
3641
|
+
name: "read_file",
|
|
3642
|
+
description: "Read a file of the research root as numbered lines, one page at a time (whole lines up to the page character budget). Returns { path, totalLines, fromLine, toLine, content, nextCursor? }; the same page reads byte-identically however it is addressed, so duplicate reads are visible to the exploration guards.",
|
|
3643
|
+
parameters: READ_SCHEMA,
|
|
3644
|
+
risk: "read",
|
|
3645
|
+
execute: async (input) => {
|
|
3646
|
+
const params = input;
|
|
3647
|
+
let rel = params.path;
|
|
3648
|
+
let fromLine = 1;
|
|
3649
|
+
if (params.cursor !== void 0) {
|
|
3650
|
+
const payload = decodeCursor(params.cursor);
|
|
3651
|
+
if (payload === void 0 || payload.t !== "read" || typeof payload.path !== "string" || typeof payload.after !== "number" || payload.path !== params.path) return { error: "invalid cursor: pass the nextCursor of a previous read_file page for the same path" };
|
|
3652
|
+
rel = payload.path;
|
|
3653
|
+
fromLine = payload.after + 1;
|
|
3654
|
+
}
|
|
3655
|
+
const resolved = await resolveWithin(rel, "file");
|
|
3656
|
+
if ("error" in resolved) return resolved;
|
|
3657
|
+
const loaded = await loadTextFile(resolved.abs, resolved.rel);
|
|
3658
|
+
if ("error" in loaded) return loaded;
|
|
3659
|
+
const lines = splitLines(loaded.text);
|
|
3660
|
+
const totalLines = lines.length;
|
|
3661
|
+
if (fromLine > totalLines) return { error: `fromLine ${String(fromLine)} is past the end of '${resolved.rel}' (${String(totalLines)} lines)` };
|
|
3662
|
+
const rendered = [];
|
|
3663
|
+
let used = 0;
|
|
3664
|
+
let toLine = fromLine - 1;
|
|
3665
|
+
for (let index = fromLine - 1; index < totalLines; index += 1) {
|
|
3666
|
+
const row = `${String(index + 1)}: ${lines[index]}`;
|
|
3667
|
+
if (rendered.length > 0 && used + 1 + row.length > readPageChars) break;
|
|
3668
|
+
rendered.push(row);
|
|
3669
|
+
used += (rendered.length > 1 ? 1 : 0) + row.length;
|
|
3670
|
+
toLine = index + 1;
|
|
3671
|
+
}
|
|
3672
|
+
const more = toLine < totalLines;
|
|
3673
|
+
return {
|
|
3674
|
+
path: resolved.rel,
|
|
3675
|
+
totalLines,
|
|
3676
|
+
fromLine,
|
|
3677
|
+
toLine,
|
|
3678
|
+
content: rendered.join("\n"),
|
|
3679
|
+
...more ? { nextCursor: encodeCursor({
|
|
3680
|
+
t: "read",
|
|
3681
|
+
path: resolved.rel,
|
|
3682
|
+
after: toLine
|
|
3683
|
+
}) } : {}
|
|
3684
|
+
};
|
|
3685
|
+
}
|
|
3686
|
+
}),
|
|
3687
|
+
tool({
|
|
3688
|
+
name: "record_evidence",
|
|
3689
|
+
description: "Record one evidence entry supporting a claim. The citation is VERIFIED at record time: the file must exist under the research root, lines must be a valid 1-based line or range inside it ('12' or '12-40'), and quote (when given) must appear verbatim in the file. Returns { recorded, duplicate, totalEvidence }.",
|
|
3690
|
+
parameters: RECORD_EVIDENCE_SCHEMA,
|
|
3691
|
+
risk: "read",
|
|
3692
|
+
execute: async (input) => {
|
|
3693
|
+
const params = input;
|
|
3694
|
+
if (params.claim.trim().length === 0) return { error: "claim must be a non-empty string" };
|
|
3695
|
+
const resolved = await resolveWithin(params.file, "file");
|
|
3696
|
+
if ("error" in resolved) return resolved;
|
|
3697
|
+
const loaded = await loadTextFile(resolved.abs, resolved.rel);
|
|
3698
|
+
if ("error" in loaded) return loaded;
|
|
3699
|
+
const lines = splitLines(loaded.text);
|
|
3700
|
+
if (params.lines !== void 0) {
|
|
3701
|
+
const match = /^(\d+)(?:-(\d+))?$/u.exec(params.lines);
|
|
3702
|
+
if (match === null) return { error: "lines must be '12' or '12-40' (1-based)" };
|
|
3703
|
+
const from = Number(match[1]);
|
|
3704
|
+
const to = match[2] === void 0 ? from : Number(match[2]);
|
|
3705
|
+
if (from < 1 || to < from || to > lines.length) return { error: `lines '${params.lines}' is outside '${resolved.rel}' (${String(lines.length)} lines)` };
|
|
3706
|
+
}
|
|
3707
|
+
if (params.quote !== void 0 && !loaded.text.includes(params.quote)) return { error: `quote not found verbatim in '${resolved.rel}'; cite what the file actually says` };
|
|
3708
|
+
const entry = {
|
|
3709
|
+
claim: params.claim,
|
|
3710
|
+
file: resolved.rel,
|
|
3711
|
+
...params.lines === void 0 ? {} : { lines: params.lines },
|
|
3712
|
+
...params.quote === void 0 ? {} : { quote: params.quote }
|
|
3713
|
+
};
|
|
3714
|
+
const duplicate = evidence.some((existing) => existing.claim === entry.claim && existing.file === entry.file && existing.lines === entry.lines && existing.quote === entry.quote);
|
|
3715
|
+
if (!duplicate) evidence.push(entry);
|
|
3716
|
+
return {
|
|
3717
|
+
recorded: !duplicate,
|
|
3718
|
+
duplicate,
|
|
3719
|
+
totalEvidence: evidence.length
|
|
3720
|
+
};
|
|
3721
|
+
}
|
|
3722
|
+
}),
|
|
3723
|
+
tool({
|
|
3724
|
+
name: "list_evidence",
|
|
3725
|
+
description: "List the evidence recorded so far, one page at a time in record order. Returns { evidence, totalEvidence, nextCursor? }.",
|
|
3726
|
+
parameters: LIST_EVIDENCE_SCHEMA,
|
|
3727
|
+
risk: "read",
|
|
3728
|
+
execute: (input) => {
|
|
3729
|
+
const params = input;
|
|
3730
|
+
let from = 0;
|
|
3731
|
+
if (params.cursor !== void 0) {
|
|
3732
|
+
const payload = decodeCursor(params.cursor);
|
|
3733
|
+
if (payload === void 0 || payload.t !== "evidence" || typeof payload.after !== "number") return Promise.resolve({ error: "invalid cursor: pass the nextCursor of a previous list_evidence page" });
|
|
3734
|
+
from = payload.after;
|
|
3735
|
+
}
|
|
3736
|
+
const page = evidence.slice(from, from + pageSize);
|
|
3737
|
+
const more = from + pageSize < evidence.length;
|
|
3738
|
+
return Promise.resolve({
|
|
3739
|
+
evidence: page,
|
|
3740
|
+
totalEvidence: evidence.length,
|
|
3741
|
+
...more ? { nextCursor: encodeCursor({
|
|
3742
|
+
t: "evidence",
|
|
3743
|
+
after: from + pageSize
|
|
3744
|
+
}) } : {}
|
|
3745
|
+
});
|
|
3746
|
+
}
|
|
3747
|
+
})
|
|
3748
|
+
],
|
|
3749
|
+
evidence: () => evidence.map((entry) => ({ ...entry }))
|
|
3750
|
+
};
|
|
3751
|
+
}
|
|
3752
|
+
//#endregion
|
|
3300
3753
|
//#region src/journal/identity.ts
|
|
3301
3754
|
/**
|
|
3302
3755
|
* Content-addressed entry identity (M1-T04): IdentityInput records per
|
|
@@ -13265,6 +13718,52 @@ async function executeWorkflow(internals, wf, args) {
|
|
|
13265
13718
|
}
|
|
13266
13719
|
}
|
|
13267
13720
|
//#endregion
|
|
13721
|
+
//#region src/orchestrator/claims.ts
|
|
13722
|
+
/** The conservative matching key: trim plus inner-whitespace collapse. */
|
|
13723
|
+
function claimKey(line) {
|
|
13724
|
+
return line.trim().replace(/\s+/gu, " ");
|
|
13725
|
+
}
|
|
13726
|
+
/**
|
|
13727
|
+
* Removes later occurrences of repeated claim lines across the rows and
|
|
13728
|
+
* indexes each repeated claim with its reporters. Deterministic: output
|
|
13729
|
+
* depends only on the input order and bytes.
|
|
13730
|
+
*/
|
|
13731
|
+
function dedupeRepeatedClaims(rows) {
|
|
13732
|
+
const seen = /* @__PURE__ */ new Map();
|
|
13733
|
+
const order = [];
|
|
13734
|
+
return {
|
|
13735
|
+
rows: rows.map((row) => {
|
|
13736
|
+
const kept = [];
|
|
13737
|
+
for (const line of row.text.split("\n")) {
|
|
13738
|
+
const key = claimKey(line);
|
|
13739
|
+
if (key === "") {
|
|
13740
|
+
kept.push(line);
|
|
13741
|
+
continue;
|
|
13742
|
+
}
|
|
13743
|
+
const prior = seen.get(key);
|
|
13744
|
+
if (prior === void 0) {
|
|
13745
|
+
const entry = {
|
|
13746
|
+
claim: line,
|
|
13747
|
+
nodeIds: [row.nodeId],
|
|
13748
|
+
count: 1
|
|
13749
|
+
};
|
|
13750
|
+
seen.set(key, entry);
|
|
13751
|
+
order.push(entry);
|
|
13752
|
+
kept.push(line);
|
|
13753
|
+
continue;
|
|
13754
|
+
}
|
|
13755
|
+
prior.count += 1;
|
|
13756
|
+
if (!prior.nodeIds.includes(row.nodeId)) prior.nodeIds.push(row.nodeId);
|
|
13757
|
+
}
|
|
13758
|
+
return {
|
|
13759
|
+
nodeId: row.nodeId,
|
|
13760
|
+
text: kept.join("\n")
|
|
13761
|
+
};
|
|
13762
|
+
}),
|
|
13763
|
+
repeated: order.filter((entry) => entry.count > 1)
|
|
13764
|
+
};
|
|
13765
|
+
}
|
|
13766
|
+
//#endregion
|
|
13268
13767
|
//#region src/orchestrator/orchestrate.ts
|
|
13269
13768
|
/**
|
|
13270
13769
|
* The mode (c) dynamic orchestrator (M6-T07/T08).
|
|
@@ -13286,6 +13785,17 @@ async function executeWorkflow(internals, wf, args) {
|
|
|
13286
13785
|
*/
|
|
13287
13786
|
/** How many rejected finishes are repaired by default: the plan's repair once. */
|
|
13288
13787
|
const DEFAULT_FINISH_MAX_REPAIRS = 1;
|
|
13788
|
+
/**
|
|
13789
|
+
* Default maxTurns of the synthesize invocation (RV-211): the finish
|
|
13790
|
+
* call plus headroom for one validator repair exchange.
|
|
13791
|
+
*/
|
|
13792
|
+
const DEFAULT_SYNTHESIS_MAX_TURNS = 4;
|
|
13793
|
+
/**
|
|
13794
|
+
* Default maxTurns of ONE incremental synthesis note (RV-211 remainder):
|
|
13795
|
+
* a note summarizes a single settled child into a bounded finish call,
|
|
13796
|
+
* so it needs less headroom than the full synthesis invocation.
|
|
13797
|
+
*/
|
|
13798
|
+
const DEFAULT_SYNTHESIS_NOTE_MAX_TURNS = 2;
|
|
13289
13799
|
const ORCHESTRATE_WORKFLOW_NAME = "rulvar-orchestrate";
|
|
13290
13800
|
/**
|
|
13291
13801
|
* One page of a string, for the child result evidence tools: maxChars is
|
|
@@ -13343,6 +13853,23 @@ function validateOrchestrateOptions(opts) {
|
|
|
13343
13853
|
}
|
|
13344
13854
|
if (fv.maxRepairs !== void 0) requireNonNegativeInteger(fv.maxRepairs, "orchestrate finishValidation.maxRepairs");
|
|
13345
13855
|
}
|
|
13856
|
+
if (opts.synthesis !== void 0) {
|
|
13857
|
+
const synthesis = opts.synthesis;
|
|
13858
|
+
if (synthesis.mode !== void 0 && synthesis.mode !== "single" && synthesis.mode !== "incremental") throw new ConfigError("orchestrate synthesis.mode must be 'single' or 'incremental'; got " + JSON.stringify(synthesis.mode));
|
|
13859
|
+
if (synthesis.mode === "incremental" && opts.finishValidation !== void 0) throw new ConfigError("orchestrate synthesis.mode 'incremental' reconciles deterministically and has no model-composed final finish for finishValidation to bind; configure validators with mode 'single', or drop them");
|
|
13860
|
+
if (synthesis.dedupeClaims !== void 0 && typeof synthesis.dedupeClaims !== "boolean") throw new ConfigError("orchestrate synthesis.dedupeClaims must be a boolean; got " + typeof synthesis.dedupeClaims);
|
|
13861
|
+
if (synthesis.noteLimits !== void 0) validateUsageLimits(synthesis.noteLimits, "orchestrate synthesis.noteLimits");
|
|
13862
|
+
if (synthesis.effort !== void 0 && ![
|
|
13863
|
+
"low",
|
|
13864
|
+
"medium",
|
|
13865
|
+
"high",
|
|
13866
|
+
"xhigh",
|
|
13867
|
+
"max"
|
|
13868
|
+
].includes(synthesis.effort)) throw new ConfigError(`orchestrate synthesis.effort must be one of 'low' | 'medium' | 'high' | 'xhigh' | 'max'; got ${JSON.stringify(synthesis.effort)}`);
|
|
13869
|
+
if (synthesis.limits !== void 0) validateUsageLimits(synthesis.limits, "orchestrate synthesis.limits");
|
|
13870
|
+
if (synthesis.instructions !== void 0 && typeof synthesis.instructions !== "string") throw new ConfigError(`orchestrate synthesis.instructions must be a string; got ${typeof synthesis.instructions}`);
|
|
13871
|
+
if (synthesis.estCost !== void 0) requireNonNegativeNumber(synthesis.estCost, "orchestrate synthesis.estCost");
|
|
13872
|
+
}
|
|
13346
13873
|
const spec = opts.budget;
|
|
13347
13874
|
if (spec === void 0) return;
|
|
13348
13875
|
if (spec.capUsd !== void 0) requireNonNegativeNumber(spec.capUsd, "orchestrate budget.capUsd");
|
|
@@ -13533,6 +14060,22 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
13533
14060
|
const recoveryDone = new Promise((resolve) => {
|
|
13534
14061
|
releaseRecovery = resolve;
|
|
13535
14062
|
});
|
|
14063
|
+
/**
|
|
14064
|
+
* Incremental synthesis notes (RV-211 remainder): one bounded
|
|
14065
|
+
* synthesize-role invocation per settled child, keyed by nodeId so a
|
|
14066
|
+
* note can never double-dispatch. The settle hook fires the note the
|
|
14067
|
+
* moment its child settles (overlapping the still-running fan-out);
|
|
14068
|
+
* the deterministic reconciliation is the completeness backstop and
|
|
14069
|
+
* dispatches any note the hook missed. The dispatcher installs right
|
|
14070
|
+
* before the coordination loop because it closes over runtime pieces
|
|
14071
|
+
* built below; these bindings are declared HERE, before
|
|
14072
|
+
* dispatchChild, so a recovered child's settle hook (which can fire
|
|
14073
|
+
* during the recovery scan) never touches a binding in its temporal
|
|
14074
|
+
* dead zone.
|
|
14075
|
+
*/
|
|
14076
|
+
const synthesisNotes = /* @__PURE__ */ new Map();
|
|
14077
|
+
let synthesisNoteDispatcher;
|
|
14078
|
+
let synthesisSettleFrozen = false;
|
|
13536
14079
|
let activityChain = Promise.resolve();
|
|
13537
14080
|
const childScopeOf = () => {
|
|
13538
14081
|
if (orchSeq === void 0) throw new ConfigError("orchestrator dispatch seq unknown before the loop started");
|
|
@@ -13618,6 +14161,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
13618
14161
|
};
|
|
13619
14162
|
settledResult.then(async (settled) => {
|
|
13620
14163
|
record.settled = settled;
|
|
14164
|
+
if (!synthesisSettleFrozen) synthesisNoteDispatcher?.(record);
|
|
13621
14165
|
await runExtensionActivity();
|
|
13622
14166
|
for (const listener of [...settleListeners]) listener();
|
|
13623
14167
|
});
|
|
@@ -14330,7 +14874,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
14330
14874
|
},
|
|
14331
14875
|
[kTerminalTool]: {
|
|
14332
14876
|
name: FINISH_TOOL_NAME,
|
|
14333
|
-
...validationSpec === void 0 ? {} : { validate: validateFinish }
|
|
14877
|
+
...validationSpec === void 0 || opts?.synthesis !== void 0 ? {} : { validate: validateFinish }
|
|
14334
14878
|
},
|
|
14335
14879
|
...(() => {
|
|
14336
14880
|
const priorCancelledRoot = internals.replayer.snapshot().filter((entry) => entry.kind === "agent" && entry.scope === callingState.scope && entry.status === "cancelled" && entry.checkpointRef !== void 0).at(-1);
|
|
@@ -14407,6 +14951,262 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
14407
14951
|
};
|
|
14408
14952
|
};
|
|
14409
14953
|
/**
|
|
14954
|
+
* One incremental synthesis note (RV-211 remainder): a FRESH agent
|
|
14955
|
+
* entry with role 'synthesize' on the finish-only toolset whose
|
|
14956
|
+
* prompt derives deterministically from the goal and the ONE settled
|
|
14957
|
+
* child's digest, so a resume replays it by identity with zero paid
|
|
14958
|
+
* calls. The invocation itself never throws out of here: an infra
|
|
14959
|
+
* failure settles as a synthesized error result and the
|
|
14960
|
+
* reconciliation falls back to the raw digest summary.
|
|
14961
|
+
*/
|
|
14962
|
+
const runSynthesisNote = async (record) => {
|
|
14963
|
+
const spec = opts?.synthesis;
|
|
14964
|
+
const finishOnly = buildOrchestratorTools(orchestratorRuntime, fullCardText).filter((tool) => tool.name === FINISH_TOOL_NAME);
|
|
14965
|
+
const digest = digestOf(record, record.settled);
|
|
14966
|
+
const prompt = [
|
|
14967
|
+
"You are an incremental synthesis note of an orchestrated run. Digest the SINGLE settled child below into a self-contained note for the final deterministic reconciliation by calling finish({ result }) EXACTLY once, where result is a STRING. Preserve concrete evidence and citations; do not invent findings. No other tool exists.",
|
|
14968
|
+
...spec.instructions === void 0 ? [] : [spec.instructions],
|
|
14969
|
+
`GOAL: ${goal}`,
|
|
14970
|
+
`CHILD: ${JSON.stringify(digest)}`
|
|
14971
|
+
].join("\n");
|
|
14972
|
+
const noteState = { ...callingState };
|
|
14973
|
+
if (orchestratorAccount !== void 0) noteState.budgetScope = orchestratorAccount;
|
|
14974
|
+
const noteOpts = {
|
|
14975
|
+
role: "synthesize",
|
|
14976
|
+
result: "full",
|
|
14977
|
+
tools: finishOnly,
|
|
14978
|
+
limits: spec.noteLimits ?? { maxTurns: 2 },
|
|
14979
|
+
...spec.model === void 0 ? {} : { model: spec.model },
|
|
14980
|
+
...spec.effort === void 0 ? {} : { effort: spec.effort },
|
|
14981
|
+
...spec.estCost === void 0 ? {} : { estCost: spec.estCost },
|
|
14982
|
+
[kTerminalTool]: { name: FINISH_TOOL_NAME }
|
|
14983
|
+
};
|
|
14984
|
+
return runtime.runInScope(noteState, () => ctx.agent(prompt, noteOpts));
|
|
14985
|
+
};
|
|
14986
|
+
/**
|
|
14987
|
+
* Note dispatch is idempotent per child: the settle hook and the
|
|
14988
|
+
* reconciliation both come through here, and the map guarantees one
|
|
14989
|
+
* dispatch per nodeId (a concurrent identical dispatch would mint a
|
|
14990
|
+
* second occurrence and PAY twice: the v1.32.0 lesson).
|
|
14991
|
+
*/
|
|
14992
|
+
const ensureSynthesisNote = (record) => {
|
|
14993
|
+
const existing = synthesisNotes.get(record.nodeId);
|
|
14994
|
+
if (existing !== void 0) return existing;
|
|
14995
|
+
const note = runSynthesisNote(record).catch((thrown) => ({
|
|
14996
|
+
status: "error",
|
|
14997
|
+
output: null,
|
|
14998
|
+
usage: {
|
|
14999
|
+
inputTokens: 0,
|
|
15000
|
+
outputTokens: 0,
|
|
15001
|
+
cacheReadTokens: 0,
|
|
15002
|
+
cacheWriteTokens: 0
|
|
15003
|
+
},
|
|
15004
|
+
costUsd: 0,
|
|
15005
|
+
turns: 0,
|
|
15006
|
+
servedBy: "unknown:unknown",
|
|
15007
|
+
transcriptRef: "",
|
|
15008
|
+
errorMessage: thrown instanceof Error ? thrown.message : String(thrown)
|
|
15009
|
+
}));
|
|
15010
|
+
synthesisNotes.set(record.nodeId, note);
|
|
15011
|
+
return note;
|
|
15012
|
+
};
|
|
15013
|
+
if (opts?.synthesis?.mode === "incremental" && capDecisionRef === void 0) synthesisNoteDispatcher = ensureSynthesisNote;
|
|
15014
|
+
/**
|
|
15015
|
+
* The deterministic reconciliation of 'incremental' synthesis: the
|
|
15016
|
+
* final result is a PURE fold of the journaled draft and the note
|
|
15017
|
+
* results in spawn order, never another model call. A note that died
|
|
15018
|
+
* falls back to the child's raw digest summary under a journaled
|
|
15019
|
+
* per-child decision and a warn log. With dedupeClaims, repeated
|
|
15020
|
+
* claim lines keep their first occurrence and the envelope carries
|
|
15021
|
+
* the repeatedClaims index.
|
|
15022
|
+
*/
|
|
15023
|
+
const reconcileIncremental = async (draft, spec) => {
|
|
15024
|
+
synthesisSettleFrozen = true;
|
|
15025
|
+
const settledRecords = [...records.values()].filter((record) => record.settled !== void 0).sort((a, b) => a.spawnOrdinal - b.spawnOrdinal);
|
|
15026
|
+
const sections = [];
|
|
15027
|
+
for (const record of settledRecords) {
|
|
15028
|
+
const settled = record.settled;
|
|
15029
|
+
const note = await ensureSynthesisNote(record);
|
|
15030
|
+
let noteText;
|
|
15031
|
+
if (note.status === "ok") noteText = typeof note.output === "string" ? note.output : JSON.stringify(note.output ?? null);
|
|
15032
|
+
else {
|
|
15033
|
+
const fallbackKey = deriverV2.deriveKey({
|
|
15034
|
+
kind: "orchestrator-synthesis-note-fallback",
|
|
15035
|
+
nodeId: record.nodeId
|
|
15036
|
+
});
|
|
15037
|
+
if (!internals.replayer.snapshot().some((entry) => entry.kind === "decision" && entry.key === fallbackKey)) await internals.replayer.appendSinglePhase({
|
|
15038
|
+
scope: callingState.scope,
|
|
15039
|
+
key: fallbackKey,
|
|
15040
|
+
kind: "decision",
|
|
15041
|
+
status: "ok",
|
|
15042
|
+
spanId: internals.spans.mint(callingState.spanId),
|
|
15043
|
+
site: "orchestrator-synthesis",
|
|
15044
|
+
value: {
|
|
15045
|
+
decisionType: "orchestrator_synthesis_note_fallback",
|
|
15046
|
+
nodeId: record.nodeId,
|
|
15047
|
+
status: note.status,
|
|
15048
|
+
turnsUsed: note.turns
|
|
15049
|
+
}
|
|
15050
|
+
});
|
|
15051
|
+
internals.events.emit({
|
|
15052
|
+
type: "log",
|
|
15053
|
+
level: "warn",
|
|
15054
|
+
msg: `the synthesis note for child '${record.nodeId}' terminated with status '${note.status}'; falling back to the raw digest summary (journaled decision 'orchestrator_synthesis_note_fallback')`
|
|
15055
|
+
}, callingState.spanId);
|
|
15056
|
+
noteText = digestOf(record, settled).outputSummary;
|
|
15057
|
+
}
|
|
15058
|
+
sections.push({
|
|
15059
|
+
nodeId: record.nodeId,
|
|
15060
|
+
logicalTaskId: record.logicalTaskId,
|
|
15061
|
+
status: settled.status,
|
|
15062
|
+
noteStatus: note.status,
|
|
15063
|
+
note: noteText
|
|
15064
|
+
});
|
|
15065
|
+
}
|
|
15066
|
+
let repeatedClaims;
|
|
15067
|
+
if (spec.dedupeClaims === true) {
|
|
15068
|
+
const deduped = dedupeRepeatedClaims(sections.map((section) => ({
|
|
15069
|
+
nodeId: section.nodeId,
|
|
15070
|
+
text: section.note
|
|
15071
|
+
})));
|
|
15072
|
+
const textByNode = new Map(deduped.rows.map((row) => [row.nodeId, row.text]));
|
|
15073
|
+
for (const section of sections) section.note = textByNode.get(section.nodeId) ?? section.note;
|
|
15074
|
+
repeatedClaims = deduped.repeated;
|
|
15075
|
+
}
|
|
15076
|
+
const draftJson = JSON.stringify(draft ?? null);
|
|
15077
|
+
internals.events.emit({
|
|
15078
|
+
type: "log",
|
|
15079
|
+
level: "debug",
|
|
15080
|
+
msg: "orchestrator synthesis reconciliation",
|
|
15081
|
+
data: {
|
|
15082
|
+
children: sections.length,
|
|
15083
|
+
draftChars: draftJson.length,
|
|
15084
|
+
notesChars: sections.reduce((sum, section) => sum + section.note.length, 0),
|
|
15085
|
+
perChild: sections.map((section) => ({
|
|
15086
|
+
nodeId: section.nodeId,
|
|
15087
|
+
chars: section.note.length
|
|
15088
|
+
})),
|
|
15089
|
+
...repeatedClaims === void 0 ? {} : { repeatedClaims: repeatedClaims.length }
|
|
15090
|
+
}
|
|
15091
|
+
}, callingState.spanId);
|
|
15092
|
+
return {
|
|
15093
|
+
synthesis: "incremental",
|
|
15094
|
+
draft,
|
|
15095
|
+
sections,
|
|
15096
|
+
...repeatedClaims === void 0 ? {} : { repeatedClaims }
|
|
15097
|
+
};
|
|
15098
|
+
};
|
|
15099
|
+
/**
|
|
15100
|
+
* The post-fan-in synthesis invocation (RV-211): a FRESH agent entry
|
|
15101
|
+
* with role 'synthesize' on the finish-only toolset (a distinct
|
|
15102
|
+
* toolsetHash, the reserved-finalizer precedent), its prompt derived
|
|
15103
|
+
* deterministically from the goal, the journaled coordination draft,
|
|
15104
|
+
* and the settled child digest, so a resume replays it by identity
|
|
15105
|
+
* with zero paid calls. Runs strictly AFTER the acceptance verdict
|
|
15106
|
+
* (a rejected run never pays for synthesis; in 'incremental' mode
|
|
15107
|
+
* the per-child notes are paid DURING the run, so only the
|
|
15108
|
+
* reconciliation itself is deferred) and owns the finish validators
|
|
15109
|
+
* when they are configured. Failure posture: with validators the run
|
|
15110
|
+
* fails typed (the validated path is mandatory); without them the
|
|
15111
|
+
* run falls back to the draft under a journaled decision and a warn
|
|
15112
|
+
* log, never silently.
|
|
15113
|
+
*/
|
|
15114
|
+
const runSynthesis = async (draft) => {
|
|
15115
|
+
const spec = opts?.synthesis;
|
|
15116
|
+
if (spec === void 0) return draft;
|
|
15117
|
+
await recoveryDone;
|
|
15118
|
+
if (spec.mode === "incremental") return await reconcileIncremental(draft, spec);
|
|
15119
|
+
const finishOnly = buildOrchestratorTools(orchestratorRuntime, fullCardText).filter((tool) => tool.name === FINISH_TOOL_NAME);
|
|
15120
|
+
const settledDigests = [...records.values()].filter((record) => record.settled !== void 0).sort((a, b) => a.spawnOrdinal - b.spawnOrdinal).map((record) => digestOf(record, record.settled));
|
|
15121
|
+
let digestRows = settledDigests;
|
|
15122
|
+
let repeatedClaims;
|
|
15123
|
+
if (spec.dedupeClaims === true) {
|
|
15124
|
+
const deduped = dedupeRepeatedClaims(settledDigests.map((row) => ({
|
|
15125
|
+
nodeId: row.nodeId,
|
|
15126
|
+
text: row.outputSummary
|
|
15127
|
+
})));
|
|
15128
|
+
const textByNode = new Map(deduped.rows.map((row) => [row.nodeId, row.text]));
|
|
15129
|
+
digestRows = settledDigests.map((row) => ({
|
|
15130
|
+
...row,
|
|
15131
|
+
outputSummary: textByNode.get(row.nodeId) ?? row.outputSummary
|
|
15132
|
+
}));
|
|
15133
|
+
repeatedClaims = deduped.repeated;
|
|
15134
|
+
}
|
|
15135
|
+
const draftJson = JSON.stringify(draft ?? null);
|
|
15136
|
+
const digestJson = JSON.stringify(digestRows);
|
|
15137
|
+
const prompt = [
|
|
15138
|
+
"You are the synthesis invocation of an orchestrated run. Compose the FINAL result of the run from the goal, the coordination draft, and the settled child evidence below by calling finish({ result }) EXACTLY once. Preserve the evidence and citations the draft relies on; do not invent findings. No other tool exists.",
|
|
15139
|
+
...repeatedClaims === void 0 ? [] : ["Repeated claims across children were deduplicated before this prompt: only the first occurrence of each repeated line remains in the digest, and the REPEATED CLAIMS index below lists each one with its reporters."],
|
|
15140
|
+
...spec.instructions === void 0 ? [] : [spec.instructions],
|
|
15141
|
+
...finishValidationPromptLines(validationSpec),
|
|
15142
|
+
`GOAL: ${goal}`,
|
|
15143
|
+
`DRAFT: ${draftJson}`,
|
|
15144
|
+
`DIGEST: ${digestJson}`,
|
|
15145
|
+
...repeatedClaims === void 0 ? [] : [`REPEATED CLAIMS: ${JSON.stringify(repeatedClaims)}`]
|
|
15146
|
+
].join("\n");
|
|
15147
|
+
internals.events.emit({
|
|
15148
|
+
type: "log",
|
|
15149
|
+
level: "debug",
|
|
15150
|
+
msg: "orchestrator synthesis context",
|
|
15151
|
+
data: {
|
|
15152
|
+
children: settledDigests.length,
|
|
15153
|
+
draftChars: draftJson.length,
|
|
15154
|
+
digestChars: digestJson.length,
|
|
15155
|
+
promptChars: prompt.length,
|
|
15156
|
+
perChild: digestRows.map((entry) => ({
|
|
15157
|
+
nodeId: entry.nodeId,
|
|
15158
|
+
chars: JSON.stringify(entry).length
|
|
15159
|
+
})),
|
|
15160
|
+
...repeatedClaims === void 0 ? {} : { repeatedClaims: repeatedClaims.length }
|
|
15161
|
+
}
|
|
15162
|
+
}, callingState.spanId);
|
|
15163
|
+
const synthesisState = { ...callingState };
|
|
15164
|
+
if (orchestratorAccount !== void 0) synthesisState.budgetScope = orchestratorAccount;
|
|
15165
|
+
const synthesisBreak = validationSpec === void 0 ? void 0 : validationAbort.signal;
|
|
15166
|
+
if (synthesisBreak !== void 0) synthesisState.signal = callingState.signal === void 0 ? synthesisBreak : AbortSignal.any([callingState.signal, synthesisBreak]);
|
|
15167
|
+
const synthesisOpts = {
|
|
15168
|
+
role: "synthesize",
|
|
15169
|
+
result: "full",
|
|
15170
|
+
tools: finishOnly,
|
|
15171
|
+
limits: spec.limits ?? { maxTurns: 4 },
|
|
15172
|
+
...spec.model === void 0 ? {} : { model: spec.model },
|
|
15173
|
+
...spec.effort === void 0 ? {} : { effort: spec.effort },
|
|
15174
|
+
...spec.estCost === void 0 ? {} : { estCost: spec.estCost },
|
|
15175
|
+
[kTerminalTool]: {
|
|
15176
|
+
name: FINISH_TOOL_NAME,
|
|
15177
|
+
...validationSpec === void 0 ? {} : { validate: validateFinish }
|
|
15178
|
+
}
|
|
15179
|
+
};
|
|
15180
|
+
const synthesized = await runtime.runInScope(synthesisState, () => ctx.agent(prompt, synthesisOpts));
|
|
15181
|
+
if (validationTermination !== void 0) throw validationTermination;
|
|
15182
|
+
if (synthesized.status === "ok") return synthesized.output;
|
|
15183
|
+
if (validationSpec !== void 0) throw new FailRunError(`the synthesis invocation terminated with status '${synthesized.status}'` + (synthesized.errorMessage === void 0 ? "" : `: ${synthesized.errorMessage}`) + "; finish validators are configured, so the unvalidated draft cannot stand", { data: {
|
|
15184
|
+
source: "orchestrator_synthesis",
|
|
15185
|
+
status: synthesized.status,
|
|
15186
|
+
turnsUsed: synthesized.turns
|
|
15187
|
+
} });
|
|
15188
|
+
const fallbackKey = deriverV2.deriveKey({ kind: "orchestrator-synthesis-fallback" });
|
|
15189
|
+
if (!internals.replayer.snapshot().some((entry) => entry.kind === "decision" && entry.key === fallbackKey)) await internals.replayer.appendSinglePhase({
|
|
15190
|
+
scope: callingState.scope,
|
|
15191
|
+
key: fallbackKey,
|
|
15192
|
+
kind: "decision",
|
|
15193
|
+
status: "ok",
|
|
15194
|
+
spanId: internals.spans.mint(callingState.spanId),
|
|
15195
|
+
site: "orchestrator-synthesis",
|
|
15196
|
+
value: {
|
|
15197
|
+
decisionType: "orchestrator_synthesis_fallback",
|
|
15198
|
+
status: synthesized.status,
|
|
15199
|
+
turnsUsed: synthesized.turns
|
|
15200
|
+
}
|
|
15201
|
+
});
|
|
15202
|
+
internals.events.emit({
|
|
15203
|
+
type: "log",
|
|
15204
|
+
level: "warn",
|
|
15205
|
+
msg: `the synthesis invocation terminated with status '${synthesized.status}'; falling back to the coordination draft (journaled decision 'orchestrator_synthesis_fallback')`
|
|
15206
|
+
}, callingState.spanId);
|
|
15207
|
+
return draft;
|
|
15208
|
+
};
|
|
15209
|
+
/**
|
|
14410
15210
|
* The settle at the cap: the JOURNALED cap decision drives the policy
|
|
14411
15211
|
* branch (its `fallback` field froze budget.atCap when the cap
|
|
14412
15212
|
* tripped), so a crash between the decision and its effect rolls the
|
|
@@ -14440,7 +15240,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
14440
15240
|
if (validationTermination !== void 0) throw validationTermination;
|
|
14441
15241
|
if (orchestratorAccount !== void 0) internals.cost.orchestrator.spentUsd = internals.budget.accountView(orchestratorAccount)?.spentUsd ?? 0;
|
|
14442
15242
|
if (result.status !== "ok") throw new ConfigError(`the orchestrator agent terminated with status '${result.status}'` + (result.errorMessage === void 0 ? "" : `: ${result.errorMessage}`));
|
|
14443
|
-
if (opts?.acceptance === void 0) return result.output;
|
|
15243
|
+
if (opts?.acceptance === void 0) return await runSynthesis(result.output);
|
|
14444
15244
|
const acceptanceKey = "acceptance";
|
|
14445
15245
|
const priorAcceptance = internals.replayer.snapshot().find((entry) => entry.kind === "decision" && entry.scope === callingState.scope && entry.key === acceptanceKey);
|
|
14446
15246
|
let decision;
|
|
@@ -14478,13 +15278,14 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
14478
15278
|
const required = decision.childPolicy === "all-ok" ? "every child ok" : `at least ${String(decision.childPolicy.minSuccessful)} children ok`;
|
|
14479
15279
|
throw new FailRunError(`the orchestrator acceptance policy rejected the finish: ${String(decision.childStatusCounts.ok ?? 0)} children settled 'ok' but the policy requires ${required}; degraded: ${decision.degradedReasons.join("; ")}`, { data: {
|
|
14480
15280
|
source: "orchestrator_acceptance",
|
|
15281
|
+
completion: "rejected",
|
|
14481
15282
|
childPolicy: decision.childPolicy,
|
|
14482
15283
|
childStatusCounts: decision.childStatusCounts,
|
|
14483
15284
|
degradedReasons: decision.degradedReasons
|
|
14484
15285
|
} });
|
|
14485
15286
|
}
|
|
14486
15287
|
return {
|
|
14487
|
-
result: result.output,
|
|
15288
|
+
result: await runSynthesis(result.output),
|
|
14488
15289
|
completion: decision.completion,
|
|
14489
15290
|
childStatusCounts: decision.childStatusCounts,
|
|
14490
15291
|
degradedReasons: decision.degradedReasons
|
|
@@ -14806,6 +15607,54 @@ function reduceInvocationTable(events) {
|
|
|
14806
15607
|
totalCostUsd
|
|
14807
15608
|
};
|
|
14808
15609
|
}
|
|
15610
|
+
function reduceCriticalPath(events) {
|
|
15611
|
+
let runStart;
|
|
15612
|
+
let runEnd;
|
|
15613
|
+
const startBySpan = /* @__PURE__ */ new Map();
|
|
15614
|
+
let lastWorkerEnd;
|
|
15615
|
+
let workerSpans = 0;
|
|
15616
|
+
let synthesisMs = 0;
|
|
15617
|
+
for (const event of events) {
|
|
15618
|
+
const at = Date.parse(event.ts);
|
|
15619
|
+
if (!Number.isFinite(at)) continue;
|
|
15620
|
+
switch (event.type) {
|
|
15621
|
+
case "run:start":
|
|
15622
|
+
runStart ??= at;
|
|
15623
|
+
break;
|
|
15624
|
+
case "run:end":
|
|
15625
|
+
runEnd = at;
|
|
15626
|
+
break;
|
|
15627
|
+
case "agent:start":
|
|
15628
|
+
startBySpan.set(event.spanId, {
|
|
15629
|
+
role: event.role,
|
|
15630
|
+
at
|
|
15631
|
+
});
|
|
15632
|
+
break;
|
|
15633
|
+
case "agent:end": {
|
|
15634
|
+
const started = startBySpan.get(event.spanId);
|
|
15635
|
+
if (started === void 0) break;
|
|
15636
|
+
if (started.role === "synthesize") synthesisMs += Math.max(0, at - started.at);
|
|
15637
|
+
else if (started.role !== "orchestrate") {
|
|
15638
|
+
workerSpans += 1;
|
|
15639
|
+
lastWorkerEnd = lastWorkerEnd === void 0 ? at : Math.max(lastWorkerEnd, at);
|
|
15640
|
+
}
|
|
15641
|
+
break;
|
|
15642
|
+
}
|
|
15643
|
+
default: break;
|
|
15644
|
+
}
|
|
15645
|
+
}
|
|
15646
|
+
const path = {
|
|
15647
|
+
synthesisMs,
|
|
15648
|
+
workerSpans
|
|
15649
|
+
};
|
|
15650
|
+
if (runStart !== void 0 && runEnd !== void 0) path.runWallMs = Math.max(0, runEnd - runStart);
|
|
15651
|
+
if (runEnd !== void 0 && lastWorkerEnd !== void 0) path.postFanInMs = Math.max(0, runEnd - lastWorkerEnd);
|
|
15652
|
+
if (path.runWallMs !== void 0 && path.runWallMs > 0) {
|
|
15653
|
+
if (path.postFanInMs !== void 0) path.postFanInShare = path.postFanInMs / path.runWallMs;
|
|
15654
|
+
path.synthesisShare = synthesisMs / path.runWallMs;
|
|
15655
|
+
}
|
|
15656
|
+
return path;
|
|
15657
|
+
}
|
|
14809
15658
|
//#endregion
|
|
14810
15659
|
//#region src/l0/run-id.ts
|
|
14811
15660
|
/**
|
|
@@ -15087,6 +15936,32 @@ function workflowSourceRef(runId) {
|
|
|
15087
15936
|
return `${runId}/workflow-source`;
|
|
15088
15937
|
}
|
|
15089
15938
|
/**
|
|
15939
|
+
* The completion envelope contract (RV-207 tail): a workflow reports
|
|
15940
|
+
* SEMANTIC completion by returning an object result carrying a
|
|
15941
|
+
* `completion` literal (and optionally `childStatusCounts`), or by
|
|
15942
|
+
* throwing a typed RulvarError whose `data` carries them; the engine
|
|
15943
|
+
* lifts the validated fields onto the `run:end` event so telemetry
|
|
15944
|
+
* consumers read completeness without parsing workflow-specific result
|
|
15945
|
+
* shapes. The orchestrator acceptance path emits this envelope. Pure
|
|
15946
|
+
* shape validation: anything malformed is silently absent (the event is
|
|
15947
|
+
* telemetry, never authority), and an invalid counts record drops the
|
|
15948
|
+
* counts while keeping a valid completion.
|
|
15949
|
+
*/
|
|
15950
|
+
function liftRunCompletion(candidate) {
|
|
15951
|
+
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) return;
|
|
15952
|
+
const completion = candidate.completion;
|
|
15953
|
+
if (completion !== "complete" && completion !== "partial" && completion !== "rejected") return;
|
|
15954
|
+
const counts = candidate.childStatusCounts;
|
|
15955
|
+
if (typeof counts === "object" && counts !== null && !Array.isArray(counts)) {
|
|
15956
|
+
const entries = Object.entries(counts);
|
|
15957
|
+
if (entries.every(([, value]) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) return {
|
|
15958
|
+
completion,
|
|
15959
|
+
childStatusCounts: Object.fromEntries(entries)
|
|
15960
|
+
};
|
|
15961
|
+
}
|
|
15962
|
+
return { completion };
|
|
15963
|
+
}
|
|
15964
|
+
/**
|
|
15090
15965
|
* sha256 hex over the JCS canonical serialization of a run's args: the
|
|
15091
15966
|
* value the engine records as `RunMeta.argsHash` at genesis, exposed so
|
|
15092
15967
|
* hosts can verify re-supplied resume args against the recorded hash
|
|
@@ -15457,11 +16332,13 @@ function createEngine(options) {
|
|
|
15457
16332
|
}
|
|
15458
16333
|
}
|
|
15459
16334
|
await putMeta(status).catch(() => void 0);
|
|
16335
|
+
const lifted = liftRunCompletion(status === "ok" || status === "exhausted" ? outcome.value : status === "error" ? wireError?.data : void 0);
|
|
15460
16336
|
bus.emit({
|
|
15461
16337
|
type: "run:end",
|
|
15462
16338
|
status,
|
|
15463
16339
|
totalUsd: ledger.usd,
|
|
15464
|
-
...outcome.cost.usageApprox === true ? { usageApprox: true } : {}
|
|
16340
|
+
...outcome.cost.usageApprox === true ? { usageApprox: true } : {},
|
|
16341
|
+
...lifted === void 0 ? {} : lifted
|
|
15465
16342
|
}, rootSpanId);
|
|
15466
16343
|
bus.end();
|
|
15467
16344
|
resumeCtx?.previewResolve({
|
|
@@ -15933,4 +16810,4 @@ function createSandboxBridge(ctx, options) {
|
|
|
15933
16810
|
};
|
|
15934
16811
|
}
|
|
15935
16812
|
//#endregion
|
|
15936
|
-
export { AWAIT_SCHEMA, AdmissionController, AdmissionRejectedError, AgentCallError, BUDGET_ABORT_REASON, BudgetExhaustedError, CANCEL_AGENT_SCHEMA, CHECKPOINT_FORMAT_V1, CLAIM_STATEMENT_MAX_CHARS, CLAIM_TTL_DAYS, COMPACTION_SUMMARY_PREFIX, CURRENT_HASH_VERSION, ConfigError, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_DEPTH, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PINNED_WORKTREES, DEFAULT_MAX_REVISIONS_PER_RUN, DEFAULT_MAX_TOTAL_SPAWNS, DEFAULT_MAX_TURNS, DEFAULT_MODEL_RETRY_ATTEMPTS, DEFAULT_NO_PROGRESS_TURNS, DEFAULT_PER_RUN_CONCURRENCY, DEFAULT_RETRY_POLICY, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DedupIndex, DeterminismError, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EscalationDecisionAbortedError, EventBus, ExternalRegistry, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_TOOL_NAME, FailRunError, FileModelKnowledgeStore, FileTranscriptStore, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, GitWorktreeProvider, INBOX_PROPOSAL_TTL_DAYS, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, JournalCompatibilityError, JournalMatcher, JournalMissError, JournalOrderViolation, JsonlFileStore, KB_ACTIVE_CLAIMS_CAP, KB_CARD_RENDER_BUDGET_CHARS, KeyedLimiter, KnowledgeCasError, LARGE_VALUE_WARN_BYTES, LEGACY_LTID_PREFIX, LEGACY_SIGNATURE_INPUTS, LINEAGE_SIG_VERSION, LeaseHeldError, LineageIndex, MASKED_SECRET, MAX_CHILD_RESULT_PAGE_CHARS, MAX_DEPTH_CEILING, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, ParallelSiteCounter, PlanInvariantError, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SANDBOX_AGENT_OPT_KEYS, SPAWN_AGENT_SCHEMA, SandboxError, ScriptRejected, Semaphore, SpanRegistry, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, atCompactionThreshold, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, compilePermissionChain, compilePermissionPreset, compileVerifiedLayer, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidencePreservedValidator, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, identityJcs, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, lexShellCommand, liftRetainedParts, lineageWeightOf, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, readRunMeta, readTerminationInit, reconcileRunMeta, reduceInvocationTable, registryKeyRing, remeasureQueue, replayDisposition, requiredFieldsValidator, requiredSectionsValidator, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, sanitizeTerminalText, sanitizeTokenCount, sanitizeUsage, sanitizeUsageDelta, scanJournalCompatibility, schemaHash, schemaHashOfSpec, selectStructuredOutputTier, shouldCompact, snapshotUsage, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, usageViolations, validateEditorialCommit, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateUsageLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
|
|
16813
|
+
export { AWAIT_SCHEMA, AdmissionController, AdmissionRejectedError, AgentCallError, BUDGET_ABORT_REASON, BudgetExhaustedError, CANCEL_AGENT_SCHEMA, CHECKPOINT_FORMAT_V1, CLAIM_STATEMENT_MAX_CHARS, CLAIM_TTL_DAYS, COMPACTION_SUMMARY_PREFIX, CURRENT_HASH_VERSION, ConfigError, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_DEPTH, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PINNED_WORKTREES, DEFAULT_MAX_REVISIONS_PER_RUN, DEFAULT_MAX_TOTAL_SPAWNS, DEFAULT_MAX_TURNS, DEFAULT_MODEL_RETRY_ATTEMPTS, DEFAULT_NO_PROGRESS_TURNS, DEFAULT_PER_RUN_CONCURRENCY, DEFAULT_RETRY_POLICY, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_SYNTHESIS_MAX_TURNS, DEFAULT_SYNTHESIS_NOTE_MAX_TURNS, DedupIndex, DeterminismError, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EscalationDecisionAbortedError, EventBus, ExternalRegistry, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_TOOL_NAME, FailRunError, FileModelKnowledgeStore, FileTranscriptStore, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, GitWorktreeProvider, INBOX_PROPOSAL_TTL_DAYS, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, JournalCompatibilityError, JournalMatcher, JournalMissError, JournalOrderViolation, JsonlFileStore, KB_ACTIVE_CLAIMS_CAP, KB_CARD_RENDER_BUDGET_CHARS, KeyedLimiter, KnowledgeCasError, LARGE_VALUE_WARN_BYTES, LEGACY_LTID_PREFIX, LEGACY_SIGNATURE_INPUTS, LINEAGE_SIG_VERSION, LeaseHeldError, LineageIndex, MASKED_SECRET, MAX_CHILD_RESULT_PAGE_CHARS, MAX_DEPTH_CEILING, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, ParallelSiteCounter, PlanInvariantError, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SANDBOX_AGENT_OPT_KEYS, SPAWN_AGENT_SCHEMA, SandboxError, ScriptRejected, Semaphore, SpanRegistry, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, atCompactionThreshold, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, compilePermissionChain, compilePermissionPreset, compileVerifiedLayer, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, dedupeRepeatedClaims, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidencePreservedValidator, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, identityJcs, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, lexShellCommand, liftRetainedParts, lineageWeightOf, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, readRunMeta, readTerminationInit, reconcileRunMeta, reduceCriticalPath, reduceInvocationTable, registryKeyRing, remeasureQueue, replayDisposition, repositoryResearchToolset, requiredFieldsValidator, requiredSectionsValidator, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, sanitizeTerminalText, sanitizeTokenCount, sanitizeUsage, sanitizeUsageDelta, scanJournalCompatibility, schemaHash, schemaHashOfSpec, selectStructuredOutputTier, shouldCompact, snapshotUsage, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, usageViolations, validateEditorialCommit, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateUsageLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
|