perchai-cli 2.4.33 → 2.4.35
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/perch.mjs +567 -92
- package/package.json +1 -1
package/dist/perch.mjs
CHANGED
|
@@ -76218,6 +76218,7 @@ function getToolDisplayName(toolName) {
|
|
|
76218
76218
|
var NON_MODULE_TOOL_OWNERS, TOOL_RISK, TOOL_DISPLAY_NAMES;
|
|
76219
76219
|
var init_catalog = __esm({
|
|
76220
76220
|
"features/perchTerminal/runtime/toolSystem/catalog.ts"() {
|
|
76221
|
+
"use strict";
|
|
76221
76222
|
init_toolNames();
|
|
76222
76223
|
NON_MODULE_TOOL_OWNERS = {
|
|
76223
76224
|
[TOOL_NAMES.listSources]: "lane",
|
|
@@ -134940,9 +134941,29 @@ function validateArgs(name, args) {
|
|
|
134940
134941
|
if (args.reason !== void 0 && typeof args.reason !== "string")
|
|
134941
134942
|
return "task_stop.reason must be a string.";
|
|
134942
134943
|
return null;
|
|
134943
|
-
case TOOL_NAMES.spawnWorker:
|
|
134944
|
-
|
|
134945
|
-
|
|
134944
|
+
case TOOL_NAMES.spawnWorker: {
|
|
134945
|
+
const hasWorkerId = typeof args.workerId === "string" && args.workerId.trim().length > 0;
|
|
134946
|
+
const customRaw = args.custom;
|
|
134947
|
+
if (!hasWorkerId && customRaw === void 0) {
|
|
134948
|
+
return "spawn_worker requires workerId (registered worker) or custom { displayName, allowedTools, ... } (ad-hoc worker).";
|
|
134949
|
+
}
|
|
134950
|
+
if (customRaw !== void 0) {
|
|
134951
|
+
if (!customRaw || typeof customRaw !== "object" || Array.isArray(customRaw)) {
|
|
134952
|
+
return "spawn_worker.custom must be an object.";
|
|
134953
|
+
}
|
|
134954
|
+
const custom = customRaw;
|
|
134955
|
+
if (typeof custom.displayName !== "string" || !custom.displayName.trim()) {
|
|
134956
|
+
return "spawn_worker.custom.displayName must be a non-empty string.";
|
|
134957
|
+
}
|
|
134958
|
+
if (custom.allowedTools !== void 0 && (!Array.isArray(custom.allowedTools) || !custom.allowedTools.every((tool) => typeof tool === "string"))) {
|
|
134959
|
+
return "spawn_worker.custom.allowedTools must be an array of tool name strings.";
|
|
134960
|
+
}
|
|
134961
|
+
if (custom.writesWorkspace !== void 0 && typeof custom.writesWorkspace !== "boolean") {
|
|
134962
|
+
return "spawn_worker.custom.writesWorkspace must be a boolean.";
|
|
134963
|
+
}
|
|
134964
|
+
if (custom.outputContract !== void 0 && typeof custom.outputContract !== "string") {
|
|
134965
|
+
return "spawn_worker.custom.outputContract must be a string.";
|
|
134966
|
+
}
|
|
134946
134967
|
}
|
|
134947
134968
|
if (typeof args.objective !== "string" || !args.objective.trim())
|
|
134948
134969
|
return "spawn_worker.objective must be a non-empty string.";
|
|
@@ -134962,6 +134983,7 @@ function validateArgs(name, args) {
|
|
|
134962
134983
|
if (args.maxIterations !== void 0 && !isPositiveNumber(args.maxIterations))
|
|
134963
134984
|
return "spawn_worker.maxIterations must be a positive number.";
|
|
134964
134985
|
return null;
|
|
134986
|
+
}
|
|
134965
134987
|
case TOOL_NAMES.dispatchAgent: {
|
|
134966
134988
|
if (Array.isArray(args.tasks)) {
|
|
134967
134989
|
if (args.tasks.length === 0)
|
|
@@ -135928,13 +135950,38 @@ function getDesktopToolDefinitions() {
|
|
|
135928
135950
|
type: "function",
|
|
135929
135951
|
function: {
|
|
135930
135952
|
name: TOOL_NAMES.spawnWorker,
|
|
135931
|
-
description: "Spawn one bounded specialist sub-agent. Use simple capability-shaped workers when possible: source_reader for scoped local-file evidence, math_checker for exact sandbox totals/reconciliation, contradiction_verifier for conflict checks, financial_writer/general_writer for narrative from provided facts. Best for independent work where the main loop only needs the conclusion. Do not use context-only managed analysts as raw-folder readers; give them pre-read JSON. The main loop remains owner of decisions, writing, and real actions. Prefer dispatch_agent for parallel batches. Do not repeat source_reader after it returns a DONE/PARTIAL manifest; advance to math_checker or verification.",
|
|
135953
|
+
description: "Spawn one bounded specialist sub-agent. Use simple capability-shaped workers when possible: source_reader for scoped local-file evidence, math_checker for exact sandbox totals/reconciliation, contradiction_verifier for conflict checks, financial_writer/general_writer for narrative from provided facts. When no registered worker fits, pass custom{} instead of workerId to spawn a one-off ad-hoc worker with its own name and minimal tool scope. Best for independent work where the main loop only needs the conclusion. Do not use context-only managed analysts as raw-folder readers; give them pre-read JSON. The main loop remains owner of decisions, writing, and real actions. Prefer dispatch_agent for parallel batches. Do not repeat source_reader after it returns a DONE/PARTIAL manifest; advance to math_checker or verification.",
|
|
135932
135954
|
parameters: {
|
|
135933
135955
|
type: "object",
|
|
135934
135956
|
properties: {
|
|
135935
135957
|
workerId: {
|
|
135936
135958
|
type: "string",
|
|
135937
|
-
description: "Registered worker or role id. Simple roles: source_reader, math_checker, contradiction_verifier, financial_writer, general_writer. Quill roles: legal_source_scout, legal_case_scout, source_verifier, doc_writer, email_sender, calendar_scheduler."
|
|
135959
|
+
description: "Registered worker or role id. Simple roles: source_reader, math_checker, contradiction_verifier, financial_writer, general_writer. Quill roles: legal_source_scout, legal_case_scout, source_verifier, doc_writer, email_sender, calendar_scheduler. Omit when passing custom."
|
|
135960
|
+
},
|
|
135961
|
+
custom: {
|
|
135962
|
+
type: "object",
|
|
135963
|
+
description: "Inline ad-hoc worker spec, used INSTEAD of workerId when no registered worker fits. The worker is run-scoped: registered for this spawn, unregistered after. Orchestration tools are never granted; write tools require writesWorkspace=true.",
|
|
135964
|
+
properties: {
|
|
135965
|
+
displayName: {
|
|
135966
|
+
type: "string",
|
|
135967
|
+
description: 'Short task-specific role name, e.g. "Changelog Summarizer".'
|
|
135968
|
+
},
|
|
135969
|
+
allowedTools: {
|
|
135970
|
+
type: "array",
|
|
135971
|
+
items: { type: "string" },
|
|
135972
|
+
description: "Minimal tool names this worker needs. Unknown/forbidden tools are dropped."
|
|
135973
|
+
},
|
|
135974
|
+
writesWorkspace: {
|
|
135975
|
+
type: "boolean",
|
|
135976
|
+
description: "True only if the worker must edit files inside the workspace root."
|
|
135977
|
+
},
|
|
135978
|
+
outputContract: {
|
|
135979
|
+
type: "string",
|
|
135980
|
+
description: "Short contract for the worker's structured output."
|
|
135981
|
+
}
|
|
135982
|
+
},
|
|
135983
|
+
required: ["displayName"],
|
|
135984
|
+
additionalProperties: false
|
|
135938
135985
|
},
|
|
135939
135986
|
objective: {
|
|
135940
135987
|
type: "string",
|
|
@@ -135963,7 +136010,7 @@ function getDesktopToolDefinitions() {
|
|
|
135963
136010
|
description: "Optional child loop cap. Defaults to the worker manifest cap."
|
|
135964
136011
|
}
|
|
135965
136012
|
},
|
|
135966
|
-
required: ["
|
|
136013
|
+
required: ["objective"],
|
|
135967
136014
|
additionalProperties: false
|
|
135968
136015
|
}
|
|
135969
136016
|
}
|
|
@@ -205266,7 +205313,159 @@ var init_sendWorkerMessage2 = __esm({
|
|
|
205266
205313
|
}
|
|
205267
205314
|
});
|
|
205268
205315
|
|
|
205316
|
+
// features/perchTerminal/runtime/workers/adhocManifest.ts
|
|
205317
|
+
function buildAdhocWorkerManifest(spec) {
|
|
205318
|
+
const displayName = spec.displayName.replace(/\s+/g, " ").trim().slice(0, MAX_NAME_CHARS) || "Ad-hoc Worker";
|
|
205319
|
+
const writesWorkspace = spec.writesWorkspace === true;
|
|
205320
|
+
const outputContract = (spec.outputContract ?? "").replace(/\s+/g, " ").trim().slice(0, MAX_CONTRACT_CHARS) || DEFAULT_OUTPUT_CONTRACT;
|
|
205321
|
+
const requested = [...new Set(spec.allowedTools ?? [])].filter(
|
|
205322
|
+
(tool) => typeof tool === "string" && tool.trim().length > 0
|
|
205323
|
+
);
|
|
205324
|
+
const droppedTools = [];
|
|
205325
|
+
const allowedTools = requested.filter((tool) => {
|
|
205326
|
+
if (ADHOC_FORBIDDEN_TOOLS.includes(tool)) {
|
|
205327
|
+
droppedTools.push(tool);
|
|
205328
|
+
return false;
|
|
205329
|
+
}
|
|
205330
|
+
if (!writesWorkspace && ADHOC_WRITE_TOOLS.has(tool)) {
|
|
205331
|
+
droppedTools.push(tool);
|
|
205332
|
+
return false;
|
|
205333
|
+
}
|
|
205334
|
+
return true;
|
|
205335
|
+
});
|
|
205336
|
+
const maxIterations = clampInt(spec.maxIterations, 1, ADHOC_MAX_ITERATIONS, 6);
|
|
205337
|
+
const workerId = uniqueAdhocWorkerId(displayName);
|
|
205338
|
+
const manifest = {
|
|
205339
|
+
workerId,
|
|
205340
|
+
name: displayName,
|
|
205341
|
+
description: `Ad-hoc worker (run-scoped): ${displayName}.`,
|
|
205342
|
+
lane: writesWorkspace ? "code_worker" : "fast_worker",
|
|
205343
|
+
systemPrompt: [
|
|
205344
|
+
`You are ${displayName}, one bounded ad-hoc worker spawned for a single objective.`,
|
|
205345
|
+
"Work only on the objective you are given. Never delegate, never spawn other workers, never start suites.",
|
|
205346
|
+
`Respect the output contract exactly: ${outputContract}.`,
|
|
205347
|
+
writesWorkspace ? "You may edit files, but only inside the active workspace root, and only edits the objective requires." : "You are read-only: never write, move, or delete files."
|
|
205348
|
+
].join("\n"),
|
|
205349
|
+
allowedTools,
|
|
205350
|
+
maxIterations,
|
|
205351
|
+
callableAgents: [],
|
|
205352
|
+
outputContract
|
|
205353
|
+
};
|
|
205354
|
+
return { manifest, droppedTools };
|
|
205355
|
+
}
|
|
205356
|
+
function uniqueAdhocWorkerId(displayName) {
|
|
205357
|
+
const slug = displayName.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 24) || "worker";
|
|
205358
|
+
const base = `adhoc_${Date.now().toString(36)}_${slug}`;
|
|
205359
|
+
let candidate = base;
|
|
205360
|
+
let bump = 0;
|
|
205361
|
+
while (getWorkerManifest(candidate)) {
|
|
205362
|
+
bump += 1;
|
|
205363
|
+
candidate = `${base}_${bump}`;
|
|
205364
|
+
}
|
|
205365
|
+
return candidate;
|
|
205366
|
+
}
|
|
205367
|
+
function clampInt(value, min2, max2, fallback) {
|
|
205368
|
+
const num = typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
205369
|
+
return Math.min(Math.max(num, min2), max2);
|
|
205370
|
+
}
|
|
205371
|
+
var ADHOC_FORBIDDEN_TOOLS, ADHOC_WRITE_TOOLS, ADHOC_MAX_ITERATIONS, MAX_NAME_CHARS, MAX_CONTRACT_CHARS, DEFAULT_OUTPUT_CONTRACT;
|
|
205372
|
+
var init_adhocManifest = __esm({
|
|
205373
|
+
"features/perchTerminal/runtime/workers/adhocManifest.ts"() {
|
|
205374
|
+
"use strict";
|
|
205375
|
+
init_toolNames();
|
|
205376
|
+
init_registry();
|
|
205377
|
+
init_types();
|
|
205378
|
+
ADHOC_FORBIDDEN_TOOLS = [
|
|
205379
|
+
...WORKER_DISALLOWED_TOOLS,
|
|
205380
|
+
TOOL_NAMES.dispatchAgent,
|
|
205381
|
+
TOOL_NAMES.sendWorkerMessage,
|
|
205382
|
+
TOOL_NAMES.taskStop,
|
|
205383
|
+
TOOL_NAMES.runSuite,
|
|
205384
|
+
TOOL_NAMES.listSuiteCatalog,
|
|
205385
|
+
TOOL_NAMES.proposeSuitePlan,
|
|
205386
|
+
TOOL_NAMES.executeSuitePlan,
|
|
205387
|
+
TOOL_NAMES.proposeWork,
|
|
205388
|
+
TOOL_NAMES.executeWork,
|
|
205389
|
+
TOOL_NAMES.proposePlan
|
|
205390
|
+
];
|
|
205391
|
+
ADHOC_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
205392
|
+
TOOL_NAMES.writeLocalFile,
|
|
205393
|
+
TOOL_NAMES.editLocalFile,
|
|
205394
|
+
TOOL_NAMES.deleteLocalFile,
|
|
205395
|
+
TOOL_NAMES.moveLocalFile,
|
|
205396
|
+
TOOL_NAMES.copyLocalFile,
|
|
205397
|
+
TOOL_NAMES.createDirectory
|
|
205398
|
+
]);
|
|
205399
|
+
ADHOC_MAX_ITERATIONS = 10;
|
|
205400
|
+
MAX_NAME_CHARS = 40;
|
|
205401
|
+
MAX_CONTRACT_CHARS = 240;
|
|
205402
|
+
DEFAULT_OUTPUT_CONTRACT = "Return a short structured summary of what was found or done.";
|
|
205403
|
+
}
|
|
205404
|
+
});
|
|
205405
|
+
|
|
205406
|
+
// features/perchTerminal/runtime/flock/flockNicknames.ts
|
|
205407
|
+
function seedFromFlockId(flockId) {
|
|
205408
|
+
let hash = 2166136261;
|
|
205409
|
+
for (let i = 0; i < flockId.length; i++) {
|
|
205410
|
+
hash ^= flockId.charCodeAt(i);
|
|
205411
|
+
hash = Math.imul(hash, 16777619);
|
|
205412
|
+
}
|
|
205413
|
+
return hash >>> 0;
|
|
205414
|
+
}
|
|
205415
|
+
function flockNicknameRotation(flockId) {
|
|
205416
|
+
const names = [...FLOCK_NICKNAMES];
|
|
205417
|
+
let state = seedFromFlockId(flockId) || 1;
|
|
205418
|
+
const nextRandom = () => {
|
|
205419
|
+
state = Math.imul(state, 1664525) + 1013904223 >>> 0;
|
|
205420
|
+
return state / 4294967296;
|
|
205421
|
+
};
|
|
205422
|
+
for (let i = names.length - 1; i > 0; i--) {
|
|
205423
|
+
const j = Math.floor(nextRandom() * (i + 1));
|
|
205424
|
+
[names[i], names[j]] = [names[j], names[i]];
|
|
205425
|
+
}
|
|
205426
|
+
return names;
|
|
205427
|
+
}
|
|
205428
|
+
function flockNicknameFor(flockId, index) {
|
|
205429
|
+
const rotation = flockNicknameRotation(flockId);
|
|
205430
|
+
return rotation[index % rotation.length];
|
|
205431
|
+
}
|
|
205432
|
+
var FLOCK_NICKNAMES;
|
|
205433
|
+
var init_flockNicknames = __esm({
|
|
205434
|
+
"features/perchTerminal/runtime/flock/flockNicknames.ts"() {
|
|
205435
|
+
"use strict";
|
|
205436
|
+
FLOCK_NICKNAMES = [
|
|
205437
|
+
"Little Dum Dum",
|
|
205438
|
+
"Molly",
|
|
205439
|
+
"Boomer",
|
|
205440
|
+
"Shawarma",
|
|
205441
|
+
"Curie",
|
|
205442
|
+
"Eagle",
|
|
205443
|
+
"Noether",
|
|
205444
|
+
"Wonky",
|
|
205445
|
+
"Lovelace",
|
|
205446
|
+
"Bell",
|
|
205447
|
+
"Turing",
|
|
205448
|
+
"Faraday",
|
|
205449
|
+
"Biscuit",
|
|
205450
|
+
"Ice Cream",
|
|
205451
|
+
"Minsky"
|
|
205452
|
+
];
|
|
205453
|
+
}
|
|
205454
|
+
});
|
|
205455
|
+
|
|
205269
205456
|
// features/perchTerminal/runtime/toolSystem/tools/workers/spawnWorker.ts
|
|
205457
|
+
function parseAdhocSpec(raw) {
|
|
205458
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
205459
|
+
const record = raw;
|
|
205460
|
+
if (typeof record.displayName !== "string" || !record.displayName.trim()) return null;
|
|
205461
|
+
return {
|
|
205462
|
+
displayName: record.displayName,
|
|
205463
|
+
allowedTools: Array.isArray(record.allowedTools) ? record.allowedTools.filter((tool) => typeof tool === "string") : void 0,
|
|
205464
|
+
writesWorkspace: record.writesWorkspace === true,
|
|
205465
|
+
maxIterations: typeof record.maxIterations === "number" ? record.maxIterations : void 0,
|
|
205466
|
+
outputContract: typeof record.outputContract === "string" ? record.outputContract : void 0
|
|
205467
|
+
};
|
|
205468
|
+
}
|
|
205270
205469
|
var spawnWorkerTool;
|
|
205271
205470
|
var init_spawnWorker = __esm({
|
|
205272
205471
|
"features/perchTerminal/runtime/toolSystem/tools/workers/spawnWorker.ts"() {
|
|
@@ -205276,12 +205475,53 @@ var init_spawnWorker = __esm({
|
|
|
205276
205475
|
init_agentDispatch();
|
|
205277
205476
|
init_localScope();
|
|
205278
205477
|
init_toolNames();
|
|
205478
|
+
init_adhocManifest();
|
|
205479
|
+
init_registry();
|
|
205480
|
+
init_workerManifest();
|
|
205481
|
+
init_flockNicknames();
|
|
205279
205482
|
spawnWorkerTool = {
|
|
205280
205483
|
name: TOOL_NAMES.spawnWorker,
|
|
205281
205484
|
classification: { native: false },
|
|
205282
205485
|
handler: async (args, ctx) => {
|
|
205283
|
-
|
|
205486
|
+
let workerId = String(args.workerId ?? "");
|
|
205284
205487
|
const objective = String(args.objective ?? "");
|
|
205488
|
+
let adhocManifestId = null;
|
|
205489
|
+
let adhocNickname = null;
|
|
205490
|
+
const customSpec = parseAdhocSpec(args.custom);
|
|
205491
|
+
if (!workerId && args.custom !== void 0 && !customSpec) {
|
|
205492
|
+
return {
|
|
205493
|
+
ok: false,
|
|
205494
|
+
workerId: "",
|
|
205495
|
+
summary: "spawn_worker custom spec is invalid: custom.displayName (non-empty string) is required.",
|
|
205496
|
+
errorCode: "adhoc_spec_invalid"
|
|
205497
|
+
};
|
|
205498
|
+
}
|
|
205499
|
+
if (workerId && customSpec && !resolveWorkerManifest(workerId)) {
|
|
205500
|
+
workerId = "";
|
|
205501
|
+
}
|
|
205502
|
+
if (!workerId && customSpec) {
|
|
205503
|
+
const { manifest, droppedTools } = buildAdhocWorkerManifest(customSpec);
|
|
205504
|
+
registerWorkerManifest(manifest);
|
|
205505
|
+
adhocManifestId = manifest.workerId;
|
|
205506
|
+
adhocNickname = flockNicknameFor(manifest.workerId, 0);
|
|
205507
|
+
workerId = manifest.workerId;
|
|
205508
|
+
if (droppedTools.length > 0 && ctx.onEvent) {
|
|
205509
|
+
ctx.onEvent({
|
|
205510
|
+
type: "diagnostic",
|
|
205511
|
+
code: "adhoc_worker_tools_dropped",
|
|
205512
|
+
message: `Ad-hoc worker "${manifest.name}" was not granted: ${droppedTools.join(", ")} (orchestration or write tools outside its scope).`,
|
|
205513
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
205514
|
+
});
|
|
205515
|
+
}
|
|
205516
|
+
}
|
|
205517
|
+
if (!workerId) {
|
|
205518
|
+
return {
|
|
205519
|
+
ok: false,
|
|
205520
|
+
workerId: "",
|
|
205521
|
+
summary: "spawn_worker requires either workerId (registered worker) or custom { displayName, allowedTools, ... } for an ad-hoc worker.",
|
|
205522
|
+
errorCode: "worker_id_missing"
|
|
205523
|
+
};
|
|
205524
|
+
}
|
|
205285
205525
|
const context = typeof args.context === "string" || typeof args.context === "object" && args.context !== null && !Array.isArray(args.context) ? args.context : void 0;
|
|
205286
205526
|
const lane = typeof args.lane === "string" ? args.lane : void 0;
|
|
205287
205527
|
const maxIterations = typeof args.maxIterations === "number" ? args.maxIterations : void 0;
|
|
@@ -205312,26 +205552,31 @@ var init_spawnWorker = __esm({
|
|
|
205312
205552
|
Proceed with the available context. If a missing fact is essential, ask the user for that fact instead of stopping.` : "",
|
|
205313
205553
|
capabilityNote ? `Capability note: ${capabilityNote}` : ""
|
|
205314
205554
|
].filter(Boolean).join("\n\n");
|
|
205315
|
-
|
|
205316
|
-
|
|
205317
|
-
|
|
205318
|
-
|
|
205319
|
-
|
|
205320
|
-
|
|
205321
|
-
|
|
205322
|
-
|
|
205323
|
-
|
|
205324
|
-
|
|
205325
|
-
|
|
205326
|
-
|
|
205327
|
-
|
|
205328
|
-
|
|
205329
|
-
|
|
205330
|
-
|
|
205331
|
-
|
|
205332
|
-
|
|
205333
|
-
|
|
205334
|
-
|
|
205555
|
+
try {
|
|
205556
|
+
const result2 = await spawnWorker(
|
|
205557
|
+
{ workerId, objective: workerObjective, context: enrichedContext, lane, maxIterations },
|
|
205558
|
+
{
|
|
205559
|
+
workspaceRoot: effectiveWorkspaceRoot(ctx),
|
|
205560
|
+
desktopConnected: ctx.desktopConnected,
|
|
205561
|
+
activeRootPath: ctx.activeRootPath,
|
|
205562
|
+
permissionMode: ctx.permissionMode,
|
|
205563
|
+
workspaceId: ctx.workspaceId,
|
|
205564
|
+
threadId: ctx.threadId,
|
|
205565
|
+
selectedSourceId: ctx.selectedSourceId,
|
|
205566
|
+
supabaseConfigured: ctx.supabaseConfigured,
|
|
205567
|
+
supabase: ctx.supabase,
|
|
205568
|
+
runId: ctx.runId,
|
|
205569
|
+
founderModelSelection: ctx.founderModelSelection ?? null,
|
|
205570
|
+
onEvent: ctx.onEvent,
|
|
205571
|
+
signal: ctx.signal,
|
|
205572
|
+
parentToolCallId: ctx.parentToolCallId,
|
|
205573
|
+
mcpTools: ctx.mcpTools ?? []
|
|
205574
|
+
}
|
|
205575
|
+
);
|
|
205576
|
+
return adhocNickname ? { ...result2, nickname: adhocNickname } : result2;
|
|
205577
|
+
} finally {
|
|
205578
|
+
if (adhocManifestId) unregisterWorkerManifest(adhocManifestId);
|
|
205579
|
+
}
|
|
205335
205580
|
}
|
|
205336
205581
|
};
|
|
205337
205582
|
}
|
|
@@ -221145,56 +221390,6 @@ var init_flockModelHints = __esm({
|
|
|
221145
221390
|
}
|
|
221146
221391
|
});
|
|
221147
221392
|
|
|
221148
|
-
// features/perchTerminal/runtime/flock/flockNicknames.ts
|
|
221149
|
-
function seedFromFlockId(flockId) {
|
|
221150
|
-
let hash = 2166136261;
|
|
221151
|
-
for (let i = 0; i < flockId.length; i++) {
|
|
221152
|
-
hash ^= flockId.charCodeAt(i);
|
|
221153
|
-
hash = Math.imul(hash, 16777619);
|
|
221154
|
-
}
|
|
221155
|
-
return hash >>> 0;
|
|
221156
|
-
}
|
|
221157
|
-
function flockNicknameRotation(flockId) {
|
|
221158
|
-
const names = [...FLOCK_NICKNAMES];
|
|
221159
|
-
let state = seedFromFlockId(flockId) || 1;
|
|
221160
|
-
const nextRandom = () => {
|
|
221161
|
-
state = Math.imul(state, 1664525) + 1013904223 >>> 0;
|
|
221162
|
-
return state / 4294967296;
|
|
221163
|
-
};
|
|
221164
|
-
for (let i = names.length - 1; i > 0; i--) {
|
|
221165
|
-
const j = Math.floor(nextRandom() * (i + 1));
|
|
221166
|
-
[names[i], names[j]] = [names[j], names[i]];
|
|
221167
|
-
}
|
|
221168
|
-
return names;
|
|
221169
|
-
}
|
|
221170
|
-
function flockNicknameFor(flockId, index) {
|
|
221171
|
-
const rotation = flockNicknameRotation(flockId);
|
|
221172
|
-
return rotation[index % rotation.length];
|
|
221173
|
-
}
|
|
221174
|
-
var FLOCK_NICKNAMES;
|
|
221175
|
-
var init_flockNicknames = __esm({
|
|
221176
|
-
"features/perchTerminal/runtime/flock/flockNicknames.ts"() {
|
|
221177
|
-
"use strict";
|
|
221178
|
-
FLOCK_NICKNAMES = [
|
|
221179
|
-
"Little Dum Dum",
|
|
221180
|
-
"Molly",
|
|
221181
|
-
"Boomer",
|
|
221182
|
-
"Shawarma",
|
|
221183
|
-
"Curie",
|
|
221184
|
-
"Eagle",
|
|
221185
|
-
"Noether",
|
|
221186
|
-
"Wonky",
|
|
221187
|
-
"Lovelace",
|
|
221188
|
-
"Bell",
|
|
221189
|
-
"Turing",
|
|
221190
|
-
"Faraday",
|
|
221191
|
-
"Biscuit",
|
|
221192
|
-
"Ice Cream",
|
|
221193
|
-
"Minsky"
|
|
221194
|
-
];
|
|
221195
|
-
}
|
|
221196
|
-
});
|
|
221197
|
-
|
|
221198
221393
|
// features/perchTerminal/runtime/flock/flockRoles.ts
|
|
221199
221394
|
function listFlockRoleSpecs() {
|
|
221200
221395
|
return Object.values(FLOCK_ROLES);
|
|
@@ -221448,6 +221643,53 @@ var init_flockRoles = __esm({
|
|
|
221448
221643
|
"Return JSON with continuityStatus (pass|partial|fail), contradictions (string[]), styleIssues (string[]), warnings (string[])."
|
|
221449
221644
|
)
|
|
221450
221645
|
},
|
|
221646
|
+
legal_drafter: {
|
|
221647
|
+
roleId: "legal_drafter",
|
|
221648
|
+
workerId: "flock_legal_drafter",
|
|
221649
|
+
displayName: "Legal Drafter",
|
|
221650
|
+
role: "worker",
|
|
221651
|
+
lane: "writer",
|
|
221652
|
+
allowedTools: [
|
|
221653
|
+
TOOL_NAMES.searchKnowledge,
|
|
221654
|
+
TOOL_NAMES.glob,
|
|
221655
|
+
TOOL_NAMES.grep,
|
|
221656
|
+
TOOL_NAMES.readLocalFile,
|
|
221657
|
+
TOOL_NAMES.listLocalSources,
|
|
221658
|
+
TOOL_NAMES.readLocalSourceFile,
|
|
221659
|
+
TOOL_NAMES.writeLocalFile
|
|
221660
|
+
],
|
|
221661
|
+
writesWorkspace: true,
|
|
221662
|
+
maxIterations: 10,
|
|
221663
|
+
outputContract: "JSON { title, markdown, irac: { issue, rules, application, conclusion }, authoritiesCited, needsVerification, notes }",
|
|
221664
|
+
objectiveTemplate: (task) => bounded(
|
|
221665
|
+
task,
|
|
221666
|
+
"Draft a legal memo in strict IRAC form \u2014 Issue, Rule, Application, Conclusion, every section present and labeled. First call searchKnowledge for IRAC memo exemplars or templates and imitate their structure and register when any exist. Closed universe: cite ONLY authorities found in the provided case packet (local sources), with pin cites; never invent or import outside authority. Propositions you cannot support from the packet go in needsVerification, never in the prose as settled law.",
|
|
221667
|
+
"Return JSON with title, markdown, irac {issue, rules, application, conclusion}, authoritiesCited (string[]), needsVerification (string[]), notes (string[])."
|
|
221668
|
+
)
|
|
221669
|
+
},
|
|
221670
|
+
legal_citation_checker: {
|
|
221671
|
+
roleId: "legal_citation_checker",
|
|
221672
|
+
workerId: "flock_legal_citation_checker",
|
|
221673
|
+
displayName: "Legal Citation Checker",
|
|
221674
|
+
role: "verifier",
|
|
221675
|
+
lane: "verifier",
|
|
221676
|
+
// Closed universe by construction: no web or global knowledge tools — the packet is the law.
|
|
221677
|
+
allowedTools: [
|
|
221678
|
+
TOOL_NAMES.glob,
|
|
221679
|
+
TOOL_NAMES.grep,
|
|
221680
|
+
TOOL_NAMES.readLocalFile,
|
|
221681
|
+
TOOL_NAMES.listLocalSources,
|
|
221682
|
+
TOOL_NAMES.readLocalSourceFile
|
|
221683
|
+
],
|
|
221684
|
+
writesWorkspace: false,
|
|
221685
|
+
maxIterations: 10,
|
|
221686
|
+
outputContract: "JSON { verificationStatus: 'pass'|'partial'|'fail', verifiedCitations, unsupportedCitations: [{ citation, claim, reason }], missingFromPacket, warnings }",
|
|
221687
|
+
objectiveTemplate: (task) => bounded(
|
|
221688
|
+
task,
|
|
221689
|
+
"Closed-universe citation check: for every authority cited in the provided draft or memo, locate it in the provided case packet (local sources via listLocalSources/grep/readLocalFile). Verify the authority exists in the packet, quoted language appears in it, and each pin cite supports the stated proposition. Mark pass only when the packet supports the citation. Never consult outside sources \u2014 an authority absent from the packet is reported in missingFromPacket, not assumed valid.",
|
|
221690
|
+
"Return JSON with verificationStatus (pass|partial|fail), verifiedCitations, unsupportedCitations ({citation, claim, reason}[]), missingFromPacket (string[]), warnings (string[])."
|
|
221691
|
+
)
|
|
221692
|
+
},
|
|
221451
221693
|
browser_operator: {
|
|
221452
221694
|
roleId: "browser_operator",
|
|
221453
221695
|
// Reuses the roster manifest from workers/registry — its system prompt and
|
|
@@ -221519,6 +221761,22 @@ function planFlock(rawTask, options = {}) {
|
|
|
221519
221761
|
if ((options.surface ?? detectFlockSurface()) === "cli") {
|
|
221520
221762
|
selected.delete("browser_operator");
|
|
221521
221763
|
}
|
|
221764
|
+
if (selected.has("legal_drafter")) {
|
|
221765
|
+
if (!/\b(write|draft|compose|prepare)\b/i.test(task)) {
|
|
221766
|
+
selected.delete("legal_drafter");
|
|
221767
|
+
selected.add("legal_citation_checker");
|
|
221768
|
+
}
|
|
221769
|
+
selected.delete("writer");
|
|
221770
|
+
selected.delete("researcher");
|
|
221771
|
+
selected.delete("citation_checker");
|
|
221772
|
+
selected.delete("consistency_checker");
|
|
221773
|
+
selected.delete("source_verifier");
|
|
221774
|
+
}
|
|
221775
|
+
if (selected.has("legal_drafter")) {
|
|
221776
|
+
selected.add("legal_citation_checker");
|
|
221777
|
+
} else if (selected.has("legal_citation_checker")) {
|
|
221778
|
+
selected.add("workspace_scout");
|
|
221779
|
+
}
|
|
221522
221780
|
if (selected.has("patch_worker") || selected.has("test_runner") || selected.has("ui_reviewer")) {
|
|
221523
221781
|
selected.add("workspace_scout");
|
|
221524
221782
|
}
|
|
@@ -221592,6 +221850,7 @@ var init_flockPlanner = __esm({
|
|
|
221592
221850
|
{ roleId: "writer", pattern: /\b(write|draft|compose|essay|memo|report|article|document|copy)\b/i },
|
|
221593
221851
|
{ roleId: "citation_checker", pattern: /\b(citations?|verify|check sources?|fact.?check|references?)\b/i },
|
|
221594
221852
|
{ roleId: "consistency_checker", pattern: /\b(fiction|stor(?:y|ies)|novel|chapters?|poems?|screenplay|scripts?|lyrics)\b/i },
|
|
221853
|
+
{ roleId: "legal_drafter", pattern: /\b(irac|memorandum|legal (?:memo|brief|writing|citations?)|case ?packet|bluebook|pin ?cites?)\b/i },
|
|
221595
221854
|
{ roleId: "browser_operator", pattern: /\b(browser|open|navigate|click|fill|submit|login|webpage|url|site)\b/i }
|
|
221596
221855
|
];
|
|
221597
221856
|
ROLE_TRIM_PRIORITY = [
|
|
@@ -221599,6 +221858,8 @@ var init_flockPlanner = __esm({
|
|
|
221599
221858
|
"writer",
|
|
221600
221859
|
"test_runner",
|
|
221601
221860
|
"workspace_scout",
|
|
221861
|
+
"legal_drafter",
|
|
221862
|
+
"legal_citation_checker",
|
|
221602
221863
|
"researcher",
|
|
221603
221864
|
"citation_checker",
|
|
221604
221865
|
"consistency_checker",
|
|
@@ -221641,6 +221902,7 @@ function buildFlockPlannerPrompts(ctx) {
|
|
|
221641
221902
|
"- dependsOn: ids of earlier workers whose output this worker needs. No cycles. You decide the graph: parallel where independent, chained where not. Final verifier/reducer workers usually benefit from depending on the workers they check \u2014 guidance, not a requirement.",
|
|
221642
221903
|
"- baseWorkerId: set ONLY to reuse an id from existingWorkers; otherwise null. Prefer reusing existingWorkers ids when a role matches instead of inventing a near-duplicate.",
|
|
221643
221904
|
"- Writing tasks: pair the draft with exactly ONE draft verifier \u2014 reuse flock_citation_checker (together with flock_researcher) when the piece makes factual or source-backed claims, or flock_consistency_checker for creative/fiction drafts (continuity, voice, pacing \u2014 no researcher, never demand citations from fiction).",
|
|
221905
|
+
"- Legal writing or citation-checking tasks (IRAC memos, briefs, case packets, pin cites): reuse flock_legal_drafter and flock_legal_citation_checker instead of the generic writing roles. Closed universe \u2014 verification runs ONLY against the user's provided case packet/local sources, never the open web; a legal draft must include flock_legal_citation_checker.",
|
|
221644
221906
|
"- Decline (accepted=false, with reason) tasks that are trivial, conversational, or need no fanout."
|
|
221645
221907
|
].join("\n");
|
|
221646
221908
|
ensureFlockWorkersRegistered();
|
|
@@ -221746,7 +222008,7 @@ function validateLlmFlockPlan(record, ctx) {
|
|
|
221746
222008
|
const objective = sanitizeText(worker.objective, MAX_OBJECTIVE_CHARS);
|
|
221747
222009
|
if (!displayName || !objective) return null;
|
|
221748
222010
|
const role = VALID_ROLES.has(worker.phase) ? worker.phase : "worker";
|
|
221749
|
-
const outputContract = sanitizeLine(worker.outputContract,
|
|
222011
|
+
const outputContract = sanitizeLine(worker.outputContract, MAX_CONTRACT_CHARS2) || "Return a short structured summary of what was found or done.";
|
|
221750
222012
|
const requestedTools = Array.isArray(worker.allowedTools) ? worker.allowedTools.filter((tool) => typeof tool === "string") : [];
|
|
221751
222013
|
let allowedTools = [...new Set(requestedTools)].filter(
|
|
221752
222014
|
(tool) => available.has(tool) && !FLOCK_FORBIDDEN_TOOLS.includes(tool)
|
|
@@ -221757,7 +222019,7 @@ function validateLlmFlockPlan(record, ctx) {
|
|
|
221757
222019
|
if (!writesWorkspace) {
|
|
221758
222020
|
allowedTools = allowedTools.filter((tool) => !WRITE_TOOLS.has(tool));
|
|
221759
222021
|
}
|
|
221760
|
-
const maxIterations =
|
|
222022
|
+
const maxIterations = clampInt2(worker.maxIterations, 1, caps.maxIterationsPerWorker, 6);
|
|
221761
222023
|
const baseWorkerId = typeof worker.baseWorkerId === "string" && worker.baseWorkerId.trim() ? worker.baseWorkerId.trim() : null;
|
|
221762
222024
|
const reusedManifest = baseWorkerId ? resolveWorkerManifest(baseWorkerId) : null;
|
|
221763
222025
|
const flockWorkerId = `fw${index + 1}_${slugify(displayName)}`;
|
|
@@ -221823,14 +222085,56 @@ function validateLlmFlockPlan(record, ctx) {
|
|
|
221823
222085
|
}
|
|
221824
222086
|
}
|
|
221825
222087
|
}
|
|
221826
|
-
const
|
|
221827
|
-
(worker) => worker.workerId === FLOCK_ROLES.
|
|
222088
|
+
const legalDrafterWorker = surfacedWorkers.find(
|
|
222089
|
+
(worker) => worker.workerId === FLOCK_ROLES.legal_drafter.workerId
|
|
221828
222090
|
);
|
|
222091
|
+
if (legalDrafterWorker && !surfacedWorkers.some((worker) => worker.workerId === FLOCK_ROLES.legal_citation_checker.workerId)) {
|
|
222092
|
+
const spec = FLOCK_ROLES.legal_citation_checker;
|
|
222093
|
+
const genericVerifierIndex = surfacedWorkers.findIndex(
|
|
222094
|
+
(worker) => worker.workerId === FLOCK_ROLES.citation_checker.workerId || worker.workerId === FLOCK_ROLES.consistency_checker.workerId
|
|
222095
|
+
);
|
|
222096
|
+
if (genericVerifierIndex >= 0) {
|
|
222097
|
+
const existing = surfacedWorkers[genericVerifierIndex];
|
|
222098
|
+
surfacedWorkers[genericVerifierIndex] = {
|
|
222099
|
+
...existing,
|
|
222100
|
+
workerId: spec.workerId,
|
|
222101
|
+
displayName: spec.displayName,
|
|
222102
|
+
role: spec.role,
|
|
222103
|
+
objective: spec.objectiveTemplate(ctx.task),
|
|
222104
|
+
maxIterations: Math.min(spec.maxIterations, caps.maxIterationsPerWorker),
|
|
222105
|
+
allowedTools: [...spec.allowedTools],
|
|
222106
|
+
writesWorkspace: false,
|
|
222107
|
+
writeScope: null,
|
|
222108
|
+
outputContract: spec.outputContract,
|
|
222109
|
+
dependsOn: [legalDrafterWorker.flockWorkerId],
|
|
222110
|
+
dynamicManifest: null
|
|
222111
|
+
};
|
|
222112
|
+
} else if (surfacedWorkers.length < FLOCK_MAX_WORKERS) {
|
|
222113
|
+
const index = surfacedWorkers.length;
|
|
222114
|
+
surfacedWorkers.push({
|
|
222115
|
+
flockWorkerId: `fw${index + 1}_${spec.roleId}`,
|
|
222116
|
+
workerId: spec.workerId,
|
|
222117
|
+
displayName: spec.displayName,
|
|
222118
|
+
nickname: flockNicknameFor(ctx.flockId, index),
|
|
222119
|
+
role: spec.role,
|
|
222120
|
+
objective: spec.objectiveTemplate(ctx.task),
|
|
222121
|
+
maxIterations: Math.min(spec.maxIterations, caps.maxIterationsPerWorker),
|
|
222122
|
+
allowedTools: [...spec.allowedTools],
|
|
222123
|
+
writesWorkspace: false,
|
|
222124
|
+
writeScope: null,
|
|
222125
|
+
outputContract: spec.outputContract,
|
|
222126
|
+
dependsOn: [legalDrafterWorker.flockWorkerId],
|
|
222127
|
+
modelOverride: null,
|
|
222128
|
+
dynamicManifest: null
|
|
222129
|
+
});
|
|
222130
|
+
}
|
|
222131
|
+
}
|
|
222132
|
+
const writerWorker = legalDrafterWorker ?? surfacedWorkers.find((worker) => worker.workerId === FLOCK_ROLES.writer.workerId);
|
|
221829
222133
|
if (writerWorker && !surfacedWorkers.some((worker) => worker.role === "verifier") && surfacedWorkers.length < FLOCK_MAX_WORKERS) {
|
|
221830
222134
|
const hasResearch = surfacedWorkers.some(
|
|
221831
222135
|
(worker) => worker.workerId === FLOCK_ROLES.researcher.workerId
|
|
221832
222136
|
);
|
|
221833
|
-
const spec = hasResearch ? FLOCK_ROLES.citation_checker : FLOCK_ROLES.consistency_checker;
|
|
222137
|
+
const spec = legalDrafterWorker ? FLOCK_ROLES.legal_citation_checker : hasResearch ? FLOCK_ROLES.citation_checker : FLOCK_ROLES.consistency_checker;
|
|
221834
222138
|
const index = surfacedWorkers.length;
|
|
221835
222139
|
surfacedWorkers.push({
|
|
221836
222140
|
flockWorkerId: `fw${index + 1}_${spec.roleId}`,
|
|
@@ -221900,11 +222204,11 @@ function sanitizeText(value, max2) {
|
|
|
221900
222204
|
if (typeof value !== "string") return "";
|
|
221901
222205
|
return value.trim().slice(0, max2);
|
|
221902
222206
|
}
|
|
221903
|
-
function
|
|
222207
|
+
function clampInt2(value, min2, max2, fallback) {
|
|
221904
222208
|
const num = typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
221905
222209
|
return Math.min(Math.max(num, min2), max2);
|
|
221906
222210
|
}
|
|
221907
|
-
var FLOCK_FORBIDDEN_TOOLS, WRITE_TOOLS, GUI_ONLY_WORKER_IDS, GUI_ONLY_TOOLS, VALID_ROLES, MAX_DISPLAY_NAME_CHARS, MAX_OBJECTIVE_CHARS,
|
|
222211
|
+
var FLOCK_FORBIDDEN_TOOLS, WRITE_TOOLS, GUI_ONLY_WORKER_IDS, GUI_ONLY_TOOLS, VALID_ROLES, MAX_DISPLAY_NAME_CHARS, MAX_OBJECTIVE_CHARS, MAX_CONTRACT_CHARS2, MAX_ROSTER_IN_PROMPT, MAX_TOOLS_IN_PROMPT;
|
|
221908
222212
|
var init_flockLlmPlanner = __esm({
|
|
221909
222213
|
"features/perchTerminal/runtime/flock/flockLlmPlanner.ts"() {
|
|
221910
222214
|
"use strict";
|
|
@@ -221945,7 +222249,7 @@ var init_flockLlmPlanner = __esm({
|
|
|
221945
222249
|
VALID_ROLES = /* @__PURE__ */ new Set(["scout", "worker", "verifier", "reducer"]);
|
|
221946
222250
|
MAX_DISPLAY_NAME_CHARS = 40;
|
|
221947
222251
|
MAX_OBJECTIVE_CHARS = 1200;
|
|
221948
|
-
|
|
222252
|
+
MAX_CONTRACT_CHARS2 = 240;
|
|
221949
222253
|
MAX_ROSTER_IN_PROMPT = 24;
|
|
221950
222254
|
MAX_TOOLS_IN_PROMPT = 80;
|
|
221951
222255
|
}
|
|
@@ -222088,14 +222392,19 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222088
222392
|
}
|
|
222089
222393
|
const spawnFn = options.spawnWorkerFn ?? spawnWorker;
|
|
222090
222394
|
const outcomes = [];
|
|
222395
|
+
const revisionOutcomes = [];
|
|
222396
|
+
let revisionReport = null;
|
|
222091
222397
|
const outputByFlockWorkerId = /* @__PURE__ */ new Map();
|
|
222092
|
-
const sharedContext = {
|
|
222398
|
+
const sharedContext = {
|
|
222399
|
+
task,
|
|
222400
|
+
currentDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
222401
|
+
};
|
|
222093
222402
|
let toolCallsUsed = 0;
|
|
222094
222403
|
let toolCallsReserved = 0;
|
|
222095
222404
|
let workersAwaitingLaunch = plan.workers.length;
|
|
222096
222405
|
try {
|
|
222097
222406
|
const phases = [...new Set(plan.workers.map((worker) => FLOCK_ROLE_ORDER[worker.role]))].sort((a, b2) => a - b2);
|
|
222098
|
-
const runReadyWorker = async (worker) => {
|
|
222407
|
+
const runReadyWorker = async (worker, contextOverride) => {
|
|
222099
222408
|
const workersLeftIncludingThis = Math.max(1, workersAwaitingLaunch);
|
|
222100
222409
|
workersAwaitingLaunch = Math.max(0, workersAwaitingLaunch - 1);
|
|
222101
222410
|
if (flockRun.controller.signal.aborted) {
|
|
@@ -222130,11 +222439,14 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222130
222439
|
toolCallsReserved += reservedToolCalls;
|
|
222131
222440
|
emitWorkerUpdate(emit, plan, worker, "running");
|
|
222132
222441
|
try {
|
|
222442
|
+
const context = contextOverride ?? buildWorkerContext(worker, sharedContext, outputByFlockWorkerId, plan);
|
|
222443
|
+
const objective = worker.role === "worker" && contextContainsSources(context) ? `${worker.objective}
|
|
222444
|
+
${FLOCK_GROUNDING_CONTRACT}` : worker.objective;
|
|
222133
222445
|
const result2 = await spawnFn(
|
|
222134
222446
|
{
|
|
222135
222447
|
workerId: worker.workerId,
|
|
222136
|
-
objective
|
|
222137
|
-
context
|
|
222448
|
+
objective,
|
|
222449
|
+
context,
|
|
222138
222450
|
maxIterations: worker.maxIterations,
|
|
222139
222451
|
maxToolCalls: reservedToolCalls
|
|
222140
222452
|
},
|
|
@@ -222191,7 +222503,9 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222191
222503
|
break;
|
|
222192
222504
|
}
|
|
222193
222505
|
for (const worker of readyWorkers) pending.delete(worker.flockWorkerId);
|
|
222194
|
-
const batchOutcomes = await Promise.all(
|
|
222506
|
+
const batchOutcomes = await Promise.all(
|
|
222507
|
+
readyWorkers.map((worker) => runReadyWorker(worker))
|
|
222508
|
+
);
|
|
222195
222509
|
for (const outcome of batchOutcomes) {
|
|
222196
222510
|
outcomes.push(outcome);
|
|
222197
222511
|
if (!outcome.result) continue;
|
|
@@ -222201,6 +222515,43 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222201
222515
|
}
|
|
222202
222516
|
}
|
|
222203
222517
|
}
|
|
222518
|
+
const candidate = findRevisionCandidate(plan, outcomes, outputByFlockWorkerId);
|
|
222519
|
+
if (candidate && !flockRun.controller.signal.aborted) {
|
|
222520
|
+
revisionReport = candidate;
|
|
222521
|
+
const revisionWorker = {
|
|
222522
|
+
...candidate.draftWorker,
|
|
222523
|
+
flockWorkerId: `${candidate.draftWorker.flockWorkerId}_r1`,
|
|
222524
|
+
displayName: `${candidate.draftWorker.displayName} (revision)`,
|
|
222525
|
+
objective: buildRevisionObjective(task, candidate),
|
|
222526
|
+
dependsOn: []
|
|
222527
|
+
};
|
|
222528
|
+
const revisionOutcome = await runReadyWorker(revisionWorker, {
|
|
222529
|
+
task,
|
|
222530
|
+
currentDate: sharedContext.currentDate,
|
|
222531
|
+
draft: candidate.draftOutput,
|
|
222532
|
+
verifierFindings: candidate.findings.raw
|
|
222533
|
+
});
|
|
222534
|
+
revisionOutcomes.push(revisionOutcome);
|
|
222535
|
+
candidate.revised = revisionOutcome.status === "done";
|
|
222536
|
+
if (revisionOutcome.result) {
|
|
222537
|
+
const revisedOutput = revisionOutcome.result.structuredOutput ?? clampLine(revisionOutcome.result.summary, 1200);
|
|
222538
|
+
sharedContext[revisionWorker.displayName] = revisedOutput;
|
|
222539
|
+
outputByFlockWorkerId.set(revisionWorker.flockWorkerId, revisedOutput);
|
|
222540
|
+
const recheckWorker = {
|
|
222541
|
+
...candidate.verifierWorker,
|
|
222542
|
+
flockWorkerId: `${candidate.verifierWorker.flockWorkerId}_r1`,
|
|
222543
|
+
displayName: `${candidate.verifierWorker.displayName} (recheck)`,
|
|
222544
|
+
dependsOn: []
|
|
222545
|
+
};
|
|
222546
|
+
const recheckOutcome = await runReadyWorker(recheckWorker, {
|
|
222547
|
+
task,
|
|
222548
|
+
currentDate: sharedContext.currentDate,
|
|
222549
|
+
dependencies: { [revisionWorker.displayName]: revisedOutput }
|
|
222550
|
+
});
|
|
222551
|
+
revisionOutcomes.push(recheckOutcome);
|
|
222552
|
+
candidate.recheckFindings = recheckOutcome.result ? extractVerifierFindings(recheckOutcome.result.structuredOutput) : null;
|
|
222553
|
+
}
|
|
222554
|
+
}
|
|
222204
222555
|
} finally {
|
|
222205
222556
|
clearTimeout(wallTimer);
|
|
222206
222557
|
finishRuntimeRun(
|
|
@@ -222213,7 +222564,14 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222213
222564
|
const workersFailed = outcomes.filter((outcome) => outcome.status === "failed").length;
|
|
222214
222565
|
const flockStatus = userCancelled || wallTimeHit && workersDone === 0 ? "cancelled" : workersFailed === 0 && workersDone === plan.workers.length ? "completed" : workersDone > 0 ? "partial" : "failed";
|
|
222215
222566
|
const assistantText = [
|
|
222216
|
-
buildFlockSummary(
|
|
222567
|
+
buildFlockSummary(
|
|
222568
|
+
plan,
|
|
222569
|
+
[...outcomes, ...revisionOutcomes],
|
|
222570
|
+
flockStatus,
|
|
222571
|
+
toolCallsUsed,
|
|
222572
|
+
wallTimeHit
|
|
222573
|
+
),
|
|
222574
|
+
...buildVerificationSection(revisionReport),
|
|
222217
222575
|
...modelOverrides.applied.map(
|
|
222218
222576
|
(override) => `Model override: ${override.displayName} ran on ${override.label}.`
|
|
222219
222577
|
),
|
|
@@ -222300,7 +222658,117 @@ function buildWorkerContext(worker, sharedContext, outputByFlockWorkerId, plan)
|
|
|
222300
222658
|
const depWorker = plan.workers.find((candidate) => candidate.flockWorkerId === dep);
|
|
222301
222659
|
dependencies[depWorker?.displayName ?? dep] = output;
|
|
222302
222660
|
}
|
|
222303
|
-
return { task: sharedContext.task, dependencies };
|
|
222661
|
+
return { task: sharedContext.task, currentDate: sharedContext.currentDate, dependencies };
|
|
222662
|
+
}
|
|
222663
|
+
function contextContainsSources(context) {
|
|
222664
|
+
const scan = (value, depth) => {
|
|
222665
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || depth > 3) return false;
|
|
222666
|
+
const record = value;
|
|
222667
|
+
const sources = record.sources;
|
|
222668
|
+
if (Array.isArray(sources) && sources.some(
|
|
222669
|
+
(source) => source && typeof source === "object" && typeof source.url === "string"
|
|
222670
|
+
)) {
|
|
222671
|
+
return true;
|
|
222672
|
+
}
|
|
222673
|
+
return Object.values(record).some((nested) => scan(nested, depth + 1));
|
|
222674
|
+
};
|
|
222675
|
+
return scan(context, 0);
|
|
222676
|
+
}
|
|
222677
|
+
function extractVerifierFindings(output) {
|
|
222678
|
+
if (!output || typeof output !== "object" || Array.isArray(output)) return null;
|
|
222679
|
+
const record = output;
|
|
222680
|
+
const verdictRaw = record.verificationStatus ?? record.continuityStatus ?? record.verdict;
|
|
222681
|
+
const verdict = typeof verdictRaw === "string" ? verdictRaw.trim().toLowerCase() : null;
|
|
222682
|
+
const issues = [];
|
|
222683
|
+
for (const key of [
|
|
222684
|
+
"unsupportedClaims",
|
|
222685
|
+
"unsupportedCitations",
|
|
222686
|
+
"missingFromPacket",
|
|
222687
|
+
"contradictions",
|
|
222688
|
+
"styleIssues",
|
|
222689
|
+
"unsupported"
|
|
222690
|
+
]) {
|
|
222691
|
+
const value = record[key];
|
|
222692
|
+
if (!Array.isArray(value)) continue;
|
|
222693
|
+
for (const item of value) {
|
|
222694
|
+
const text = typeof item === "string" ? item : JSON.stringify(item);
|
|
222695
|
+
const clean = clampLine(text, 200);
|
|
222696
|
+
if (clean && clean !== "{}") issues.push(clean);
|
|
222697
|
+
}
|
|
222698
|
+
}
|
|
222699
|
+
return { verdict, issues, raw: record };
|
|
222700
|
+
}
|
|
222701
|
+
function verifierFindingsNeedRevision(findings) {
|
|
222702
|
+
if (!findings) return false;
|
|
222703
|
+
return findings.issues.length > 0 || findings.verdict === "fail" || findings.verdict === "partial";
|
|
222704
|
+
}
|
|
222705
|
+
function findRevisionCandidate(plan, outcomes, outputByFlockWorkerId) {
|
|
222706
|
+
for (const outcome of outcomes) {
|
|
222707
|
+
if (outcome.worker.role !== "verifier" || outcome.status !== "done" || !outcome.result) {
|
|
222708
|
+
continue;
|
|
222709
|
+
}
|
|
222710
|
+
const findings = extractVerifierFindings(outcome.result.structuredOutput);
|
|
222711
|
+
if (!verifierFindingsNeedRevision(findings)) continue;
|
|
222712
|
+
const draftWorker = resolveDraftWorkerForVerifier(plan, outcome.worker, outcomes);
|
|
222713
|
+
if (!draftWorker) continue;
|
|
222714
|
+
const draftOutput = outputByFlockWorkerId.get(draftWorker.flockWorkerId);
|
|
222715
|
+
if (draftOutput === void 0) continue;
|
|
222716
|
+
return {
|
|
222717
|
+
verifierWorker: outcome.worker,
|
|
222718
|
+
draftWorker,
|
|
222719
|
+
draftOutput,
|
|
222720
|
+
findings,
|
|
222721
|
+
revised: false,
|
|
222722
|
+
recheckFindings: null
|
|
222723
|
+
};
|
|
222724
|
+
}
|
|
222725
|
+
return null;
|
|
222726
|
+
}
|
|
222727
|
+
function resolveDraftWorkerForVerifier(plan, verifier, outcomes) {
|
|
222728
|
+
for (const dep of verifier.dependsOn) {
|
|
222729
|
+
const worker = plan.workers.find((candidate) => candidate.flockWorkerId === dep);
|
|
222730
|
+
if (worker?.role === "worker") return worker;
|
|
222731
|
+
}
|
|
222732
|
+
const doneWorkers = outcomes.filter((outcome) => outcome.status === "done" && outcome.worker.role === "worker").map((outcome) => outcome.worker);
|
|
222733
|
+
const writer = doneWorkers.find(
|
|
222734
|
+
(worker) => worker.workerId === "flock_legal_drafter" || worker.workerId === "flock_writer"
|
|
222735
|
+
);
|
|
222736
|
+
if (writer) return writer;
|
|
222737
|
+
const writingWorkers = doneWorkers.filter((worker) => worker.writesWorkspace);
|
|
222738
|
+
if (writingWorkers.length === 1) return writingWorkers[0];
|
|
222739
|
+
return doneWorkers.length === 1 ? doneWorkers[0] : null;
|
|
222740
|
+
}
|
|
222741
|
+
function buildRevisionObjective(task, candidate) {
|
|
222742
|
+
return [
|
|
222743
|
+
"Bounded Flock revision \u2014 stay strictly inside this scope.",
|
|
222744
|
+
`Task: ${task}`,
|
|
222745
|
+
`A verifier (${candidate.verifierWorker.displayName}) reviewed your draft (context key "draft") and reported findings (context key "verifierFindings").`,
|
|
222746
|
+
"Revise the draft: fix what the findings support fixing; remove or explicitly flag any claim you cannot support. Keep the draft's purpose, format, and output contract. Do not start over.",
|
|
222747
|
+
`Output contract: ${candidate.draftWorker.outputContract}`,
|
|
222748
|
+
"Do not delegate, spawn workers, or start suites. Stop when the findings are addressed."
|
|
222749
|
+
].join("\n");
|
|
222750
|
+
}
|
|
222751
|
+
function buildVerificationSection(report) {
|
|
222752
|
+
if (!report) return [];
|
|
222753
|
+
const lines = [];
|
|
222754
|
+
lines.push(
|
|
222755
|
+
`Verification (${report.verifierWorker.displayName}): ${report.findings.verdict ?? "issues found"} \u2014 ${report.findings.issues.length} finding(s).`
|
|
222756
|
+
);
|
|
222757
|
+
const shown = report.findings.issues.slice(0, 6);
|
|
222758
|
+
for (const issue of shown) lines.push(`- ${issue}`);
|
|
222759
|
+
if (report.findings.issues.length > shown.length) {
|
|
222760
|
+
lines.push(`- \u2026and ${report.findings.issues.length - shown.length} more.`);
|
|
222761
|
+
}
|
|
222762
|
+
if (report.revised) {
|
|
222763
|
+
const recheck = report.recheckFindings;
|
|
222764
|
+
lines.push(
|
|
222765
|
+
recheck ? `The draft was revised once; recheck verdict: ${recheck.verdict ?? "no structured verdict"}${recheck.issues.length ? ` (${recheck.issues.length} remaining finding(s))` : ""}.` : "The draft was revised once; the recheck returned no structured verdict."
|
|
222766
|
+
);
|
|
222767
|
+
} else {
|
|
222768
|
+
lines.push("No revision pass ran \u2014 the findings above stand as reported.");
|
|
222769
|
+
}
|
|
222770
|
+
lines.push("Ask about any finding to discuss it, or rerun /flock to retry with adjustments.");
|
|
222771
|
+
return lines;
|
|
222304
222772
|
}
|
|
222305
222773
|
function buildSpawnContext(input, flockId, signal, emit, worker) {
|
|
222306
222774
|
return {
|
|
@@ -222365,6 +222833,7 @@ function makeFlockRunId() {
|
|
|
222365
222833
|
function now11() {
|
|
222366
222834
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
222367
222835
|
}
|
|
222836
|
+
var FLOCK_GROUNDING_CONTRACT;
|
|
222368
222837
|
var init_runFlockTurn = __esm({
|
|
222369
222838
|
"features/perchTerminal/runtime/flock/runFlockTurn.ts"() {
|
|
222370
222839
|
"use strict";
|
|
@@ -222378,6 +222847,12 @@ var init_runFlockTurn = __esm({
|
|
|
222378
222847
|
init_flockLlmPlanner();
|
|
222379
222848
|
init_flockPlanner();
|
|
222380
222849
|
init_flockRoles();
|
|
222850
|
+
FLOCK_GROUNDING_CONTRACT = [
|
|
222851
|
+
"Grounding contract: your context includes sources[] gathered by research.",
|
|
222852
|
+
"Every nontrivial factual claim in the draft must carry an inline [n] marker mapping to one of those sources, with a numbered source list at the end.",
|
|
222853
|
+
"Claims you cannot support from sources[] belong in needsVerification, never in the prose as fact.",
|
|
222854
|
+
"Do not cite anything that is not in sources[]."
|
|
222855
|
+
].join(" ");
|
|
222381
222856
|
}
|
|
222382
222857
|
});
|
|
222383
222858
|
|