perchai-cli 2.4.33 → 2.4.34
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 +541 -90
- package/package.json +1 -1
package/dist/perch.mjs
CHANGED
|
@@ -75566,6 +75566,7 @@ var init_payroll = __esm({
|
|
|
75566
75566
|
// lib/perchBusinessTools/index.ts
|
|
75567
75567
|
var init_perchBusinessTools = __esm({
|
|
75568
75568
|
"lib/perchBusinessTools/index.ts"() {
|
|
75569
|
+
"use strict";
|
|
75569
75570
|
init_generateAPAuditPacket();
|
|
75570
75571
|
init_inventoryFolder();
|
|
75571
75572
|
init_loadBusinessTables();
|
|
@@ -76218,6 +76219,7 @@ function getToolDisplayName(toolName) {
|
|
|
76218
76219
|
var NON_MODULE_TOOL_OWNERS, TOOL_RISK, TOOL_DISPLAY_NAMES;
|
|
76219
76220
|
var init_catalog = __esm({
|
|
76220
76221
|
"features/perchTerminal/runtime/toolSystem/catalog.ts"() {
|
|
76222
|
+
"use strict";
|
|
76221
76223
|
init_toolNames();
|
|
76222
76224
|
NON_MODULE_TOOL_OWNERS = {
|
|
76223
76225
|
[TOOL_NAMES.listSources]: "lane",
|
|
@@ -83125,7 +83127,6 @@ function listFinancialRoleIds() {
|
|
|
83125
83127
|
var FINANCIAL_ROLE_REGISTRY, evidenceScoutManifest;
|
|
83126
83128
|
var init_financialRoles = __esm({
|
|
83127
83129
|
"features/perchTerminal/agentPlatform/financialRoles.ts"() {
|
|
83128
|
-
"use strict";
|
|
83129
83130
|
FINANCIAL_ROLE_REGISTRY = /* @__PURE__ */ new Map();
|
|
83130
83131
|
evidenceScoutManifest = {
|
|
83131
83132
|
workerId: "evidence_scout",
|
|
@@ -86723,6 +86724,7 @@ function truncateHistoryLine(value, max2) {
|
|
|
86723
86724
|
}
|
|
86724
86725
|
var init_operatorTruth = __esm({
|
|
86725
86726
|
"features/perchTerminal/runtime/operatorTruth.ts"() {
|
|
86727
|
+
"use strict";
|
|
86726
86728
|
}
|
|
86727
86729
|
});
|
|
86728
86730
|
|
|
@@ -135928,13 +135930,38 @@ function getDesktopToolDefinitions() {
|
|
|
135928
135930
|
type: "function",
|
|
135929
135931
|
function: {
|
|
135930
135932
|
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.",
|
|
135933
|
+
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
135934
|
parameters: {
|
|
135933
135935
|
type: "object",
|
|
135934
135936
|
properties: {
|
|
135935
135937
|
workerId: {
|
|
135936
135938
|
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."
|
|
135939
|
+
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."
|
|
135940
|
+
},
|
|
135941
|
+
custom: {
|
|
135942
|
+
type: "object",
|
|
135943
|
+
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.",
|
|
135944
|
+
properties: {
|
|
135945
|
+
displayName: {
|
|
135946
|
+
type: "string",
|
|
135947
|
+
description: 'Short task-specific role name, e.g. "Changelog Summarizer".'
|
|
135948
|
+
},
|
|
135949
|
+
allowedTools: {
|
|
135950
|
+
type: "array",
|
|
135951
|
+
items: { type: "string" },
|
|
135952
|
+
description: "Minimal tool names this worker needs. Unknown/forbidden tools are dropped."
|
|
135953
|
+
},
|
|
135954
|
+
writesWorkspace: {
|
|
135955
|
+
type: "boolean",
|
|
135956
|
+
description: "True only if the worker must edit files inside the workspace root."
|
|
135957
|
+
},
|
|
135958
|
+
outputContract: {
|
|
135959
|
+
type: "string",
|
|
135960
|
+
description: "Short contract for the worker's structured output."
|
|
135961
|
+
}
|
|
135962
|
+
},
|
|
135963
|
+
required: ["displayName"],
|
|
135964
|
+
additionalProperties: false
|
|
135938
135965
|
},
|
|
135939
135966
|
objective: {
|
|
135940
135967
|
type: "string",
|
|
@@ -135963,7 +135990,7 @@ function getDesktopToolDefinitions() {
|
|
|
135963
135990
|
description: "Optional child loop cap. Defaults to the worker manifest cap."
|
|
135964
135991
|
}
|
|
135965
135992
|
},
|
|
135966
|
-
required: ["
|
|
135993
|
+
required: ["objective"],
|
|
135967
135994
|
additionalProperties: false
|
|
135968
135995
|
}
|
|
135969
135996
|
}
|
|
@@ -205266,7 +205293,159 @@ var init_sendWorkerMessage2 = __esm({
|
|
|
205266
205293
|
}
|
|
205267
205294
|
});
|
|
205268
205295
|
|
|
205296
|
+
// features/perchTerminal/runtime/workers/adhocManifest.ts
|
|
205297
|
+
function buildAdhocWorkerManifest(spec) {
|
|
205298
|
+
const displayName = spec.displayName.replace(/\s+/g, " ").trim().slice(0, MAX_NAME_CHARS) || "Ad-hoc Worker";
|
|
205299
|
+
const writesWorkspace = spec.writesWorkspace === true;
|
|
205300
|
+
const outputContract = (spec.outputContract ?? "").replace(/\s+/g, " ").trim().slice(0, MAX_CONTRACT_CHARS) || DEFAULT_OUTPUT_CONTRACT;
|
|
205301
|
+
const requested = [...new Set(spec.allowedTools ?? [])].filter(
|
|
205302
|
+
(tool) => typeof tool === "string" && tool.trim().length > 0
|
|
205303
|
+
);
|
|
205304
|
+
const droppedTools = [];
|
|
205305
|
+
const allowedTools = requested.filter((tool) => {
|
|
205306
|
+
if (ADHOC_FORBIDDEN_TOOLS.includes(tool)) {
|
|
205307
|
+
droppedTools.push(tool);
|
|
205308
|
+
return false;
|
|
205309
|
+
}
|
|
205310
|
+
if (!writesWorkspace && ADHOC_WRITE_TOOLS.has(tool)) {
|
|
205311
|
+
droppedTools.push(tool);
|
|
205312
|
+
return false;
|
|
205313
|
+
}
|
|
205314
|
+
return true;
|
|
205315
|
+
});
|
|
205316
|
+
const maxIterations = clampInt(spec.maxIterations, 1, ADHOC_MAX_ITERATIONS, 6);
|
|
205317
|
+
const workerId = uniqueAdhocWorkerId(displayName);
|
|
205318
|
+
const manifest = {
|
|
205319
|
+
workerId,
|
|
205320
|
+
name: displayName,
|
|
205321
|
+
description: `Ad-hoc worker (run-scoped): ${displayName}.`,
|
|
205322
|
+
lane: writesWorkspace ? "code_worker" : "fast_worker",
|
|
205323
|
+
systemPrompt: [
|
|
205324
|
+
`You are ${displayName}, one bounded ad-hoc worker spawned for a single objective.`,
|
|
205325
|
+
"Work only on the objective you are given. Never delegate, never spawn other workers, never start suites.",
|
|
205326
|
+
`Respect the output contract exactly: ${outputContract}.`,
|
|
205327
|
+
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."
|
|
205328
|
+
].join("\n"),
|
|
205329
|
+
allowedTools,
|
|
205330
|
+
maxIterations,
|
|
205331
|
+
callableAgents: [],
|
|
205332
|
+
outputContract
|
|
205333
|
+
};
|
|
205334
|
+
return { manifest, droppedTools };
|
|
205335
|
+
}
|
|
205336
|
+
function uniqueAdhocWorkerId(displayName) {
|
|
205337
|
+
const slug = displayName.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 24) || "worker";
|
|
205338
|
+
const base = `adhoc_${Date.now().toString(36)}_${slug}`;
|
|
205339
|
+
let candidate = base;
|
|
205340
|
+
let bump = 0;
|
|
205341
|
+
while (getWorkerManifest(candidate)) {
|
|
205342
|
+
bump += 1;
|
|
205343
|
+
candidate = `${base}_${bump}`;
|
|
205344
|
+
}
|
|
205345
|
+
return candidate;
|
|
205346
|
+
}
|
|
205347
|
+
function clampInt(value, min2, max2, fallback) {
|
|
205348
|
+
const num = typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
205349
|
+
return Math.min(Math.max(num, min2), max2);
|
|
205350
|
+
}
|
|
205351
|
+
var ADHOC_FORBIDDEN_TOOLS, ADHOC_WRITE_TOOLS, ADHOC_MAX_ITERATIONS, MAX_NAME_CHARS, MAX_CONTRACT_CHARS, DEFAULT_OUTPUT_CONTRACT;
|
|
205352
|
+
var init_adhocManifest = __esm({
|
|
205353
|
+
"features/perchTerminal/runtime/workers/adhocManifest.ts"() {
|
|
205354
|
+
"use strict";
|
|
205355
|
+
init_toolNames();
|
|
205356
|
+
init_registry();
|
|
205357
|
+
init_types();
|
|
205358
|
+
ADHOC_FORBIDDEN_TOOLS = [
|
|
205359
|
+
...WORKER_DISALLOWED_TOOLS,
|
|
205360
|
+
TOOL_NAMES.dispatchAgent,
|
|
205361
|
+
TOOL_NAMES.sendWorkerMessage,
|
|
205362
|
+
TOOL_NAMES.taskStop,
|
|
205363
|
+
TOOL_NAMES.runSuite,
|
|
205364
|
+
TOOL_NAMES.listSuiteCatalog,
|
|
205365
|
+
TOOL_NAMES.proposeSuitePlan,
|
|
205366
|
+
TOOL_NAMES.executeSuitePlan,
|
|
205367
|
+
TOOL_NAMES.proposeWork,
|
|
205368
|
+
TOOL_NAMES.executeWork,
|
|
205369
|
+
TOOL_NAMES.proposePlan
|
|
205370
|
+
];
|
|
205371
|
+
ADHOC_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
205372
|
+
TOOL_NAMES.writeLocalFile,
|
|
205373
|
+
TOOL_NAMES.editLocalFile,
|
|
205374
|
+
TOOL_NAMES.deleteLocalFile,
|
|
205375
|
+
TOOL_NAMES.moveLocalFile,
|
|
205376
|
+
TOOL_NAMES.copyLocalFile,
|
|
205377
|
+
TOOL_NAMES.createDirectory
|
|
205378
|
+
]);
|
|
205379
|
+
ADHOC_MAX_ITERATIONS = 10;
|
|
205380
|
+
MAX_NAME_CHARS = 40;
|
|
205381
|
+
MAX_CONTRACT_CHARS = 240;
|
|
205382
|
+
DEFAULT_OUTPUT_CONTRACT = "Return a short structured summary of what was found or done.";
|
|
205383
|
+
}
|
|
205384
|
+
});
|
|
205385
|
+
|
|
205386
|
+
// features/perchTerminal/runtime/flock/flockNicknames.ts
|
|
205387
|
+
function seedFromFlockId(flockId) {
|
|
205388
|
+
let hash = 2166136261;
|
|
205389
|
+
for (let i = 0; i < flockId.length; i++) {
|
|
205390
|
+
hash ^= flockId.charCodeAt(i);
|
|
205391
|
+
hash = Math.imul(hash, 16777619);
|
|
205392
|
+
}
|
|
205393
|
+
return hash >>> 0;
|
|
205394
|
+
}
|
|
205395
|
+
function flockNicknameRotation(flockId) {
|
|
205396
|
+
const names = [...FLOCK_NICKNAMES];
|
|
205397
|
+
let state = seedFromFlockId(flockId) || 1;
|
|
205398
|
+
const nextRandom = () => {
|
|
205399
|
+
state = Math.imul(state, 1664525) + 1013904223 >>> 0;
|
|
205400
|
+
return state / 4294967296;
|
|
205401
|
+
};
|
|
205402
|
+
for (let i = names.length - 1; i > 0; i--) {
|
|
205403
|
+
const j = Math.floor(nextRandom() * (i + 1));
|
|
205404
|
+
[names[i], names[j]] = [names[j], names[i]];
|
|
205405
|
+
}
|
|
205406
|
+
return names;
|
|
205407
|
+
}
|
|
205408
|
+
function flockNicknameFor(flockId, index) {
|
|
205409
|
+
const rotation = flockNicknameRotation(flockId);
|
|
205410
|
+
return rotation[index % rotation.length];
|
|
205411
|
+
}
|
|
205412
|
+
var FLOCK_NICKNAMES;
|
|
205413
|
+
var init_flockNicknames = __esm({
|
|
205414
|
+
"features/perchTerminal/runtime/flock/flockNicknames.ts"() {
|
|
205415
|
+
"use strict";
|
|
205416
|
+
FLOCK_NICKNAMES = [
|
|
205417
|
+
"Little Dum Dum",
|
|
205418
|
+
"Molly",
|
|
205419
|
+
"Boomer",
|
|
205420
|
+
"Shawarma",
|
|
205421
|
+
"Curie",
|
|
205422
|
+
"Eagle",
|
|
205423
|
+
"Noether",
|
|
205424
|
+
"Wonky",
|
|
205425
|
+
"Lovelace",
|
|
205426
|
+
"Bell",
|
|
205427
|
+
"Turing",
|
|
205428
|
+
"Faraday",
|
|
205429
|
+
"Biscuit",
|
|
205430
|
+
"Ice Cream",
|
|
205431
|
+
"Minsky"
|
|
205432
|
+
];
|
|
205433
|
+
}
|
|
205434
|
+
});
|
|
205435
|
+
|
|
205269
205436
|
// features/perchTerminal/runtime/toolSystem/tools/workers/spawnWorker.ts
|
|
205437
|
+
function parseAdhocSpec(raw) {
|
|
205438
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
205439
|
+
const record = raw;
|
|
205440
|
+
if (typeof record.displayName !== "string" || !record.displayName.trim()) return null;
|
|
205441
|
+
return {
|
|
205442
|
+
displayName: record.displayName,
|
|
205443
|
+
allowedTools: Array.isArray(record.allowedTools) ? record.allowedTools.filter((tool) => typeof tool === "string") : void 0,
|
|
205444
|
+
writesWorkspace: record.writesWorkspace === true,
|
|
205445
|
+
maxIterations: typeof record.maxIterations === "number" ? record.maxIterations : void 0,
|
|
205446
|
+
outputContract: typeof record.outputContract === "string" ? record.outputContract : void 0
|
|
205447
|
+
};
|
|
205448
|
+
}
|
|
205270
205449
|
var spawnWorkerTool;
|
|
205271
205450
|
var init_spawnWorker = __esm({
|
|
205272
205451
|
"features/perchTerminal/runtime/toolSystem/tools/workers/spawnWorker.ts"() {
|
|
@@ -205276,12 +205455,49 @@ var init_spawnWorker = __esm({
|
|
|
205276
205455
|
init_agentDispatch();
|
|
205277
205456
|
init_localScope();
|
|
205278
205457
|
init_toolNames();
|
|
205458
|
+
init_adhocManifest();
|
|
205459
|
+
init_registry();
|
|
205460
|
+
init_flockNicknames();
|
|
205279
205461
|
spawnWorkerTool = {
|
|
205280
205462
|
name: TOOL_NAMES.spawnWorker,
|
|
205281
205463
|
classification: { native: false },
|
|
205282
205464
|
handler: async (args, ctx) => {
|
|
205283
|
-
|
|
205465
|
+
let workerId = String(args.workerId ?? "");
|
|
205284
205466
|
const objective = String(args.objective ?? "");
|
|
205467
|
+
let adhocManifestId = null;
|
|
205468
|
+
let adhocNickname = null;
|
|
205469
|
+
const customSpec = parseAdhocSpec(args.custom);
|
|
205470
|
+
if (!workerId && args.custom !== void 0 && !customSpec) {
|
|
205471
|
+
return {
|
|
205472
|
+
ok: false,
|
|
205473
|
+
workerId: "",
|
|
205474
|
+
summary: "spawn_worker custom spec is invalid: custom.displayName (non-empty string) is required.",
|
|
205475
|
+
errorCode: "adhoc_spec_invalid"
|
|
205476
|
+
};
|
|
205477
|
+
}
|
|
205478
|
+
if (!workerId && customSpec) {
|
|
205479
|
+
const { manifest, droppedTools } = buildAdhocWorkerManifest(customSpec);
|
|
205480
|
+
registerWorkerManifest(manifest);
|
|
205481
|
+
adhocManifestId = manifest.workerId;
|
|
205482
|
+
adhocNickname = flockNicknameFor(manifest.workerId, 0);
|
|
205483
|
+
workerId = manifest.workerId;
|
|
205484
|
+
if (droppedTools.length > 0 && ctx.onEvent) {
|
|
205485
|
+
ctx.onEvent({
|
|
205486
|
+
type: "diagnostic",
|
|
205487
|
+
code: "adhoc_worker_tools_dropped",
|
|
205488
|
+
message: `Ad-hoc worker "${manifest.name}" was not granted: ${droppedTools.join(", ")} (orchestration or write tools outside its scope).`,
|
|
205489
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
205490
|
+
});
|
|
205491
|
+
}
|
|
205492
|
+
}
|
|
205493
|
+
if (!workerId) {
|
|
205494
|
+
return {
|
|
205495
|
+
ok: false,
|
|
205496
|
+
workerId: "",
|
|
205497
|
+
summary: "spawn_worker requires either workerId (registered worker) or custom { displayName, allowedTools, ... } for an ad-hoc worker.",
|
|
205498
|
+
errorCode: "worker_id_missing"
|
|
205499
|
+
};
|
|
205500
|
+
}
|
|
205285
205501
|
const context = typeof args.context === "string" || typeof args.context === "object" && args.context !== null && !Array.isArray(args.context) ? args.context : void 0;
|
|
205286
205502
|
const lane = typeof args.lane === "string" ? args.lane : void 0;
|
|
205287
205503
|
const maxIterations = typeof args.maxIterations === "number" ? args.maxIterations : void 0;
|
|
@@ -205312,26 +205528,31 @@ var init_spawnWorker = __esm({
|
|
|
205312
205528
|
Proceed with the available context. If a missing fact is essential, ask the user for that fact instead of stopping.` : "",
|
|
205313
205529
|
capabilityNote ? `Capability note: ${capabilityNote}` : ""
|
|
205314
205530
|
].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
|
-
|
|
205531
|
+
try {
|
|
205532
|
+
const result2 = await spawnWorker(
|
|
205533
|
+
{ workerId, objective: workerObjective, context: enrichedContext, lane, maxIterations },
|
|
205534
|
+
{
|
|
205535
|
+
workspaceRoot: effectiveWorkspaceRoot(ctx),
|
|
205536
|
+
desktopConnected: ctx.desktopConnected,
|
|
205537
|
+
activeRootPath: ctx.activeRootPath,
|
|
205538
|
+
permissionMode: ctx.permissionMode,
|
|
205539
|
+
workspaceId: ctx.workspaceId,
|
|
205540
|
+
threadId: ctx.threadId,
|
|
205541
|
+
selectedSourceId: ctx.selectedSourceId,
|
|
205542
|
+
supabaseConfigured: ctx.supabaseConfigured,
|
|
205543
|
+
supabase: ctx.supabase,
|
|
205544
|
+
runId: ctx.runId,
|
|
205545
|
+
founderModelSelection: ctx.founderModelSelection ?? null,
|
|
205546
|
+
onEvent: ctx.onEvent,
|
|
205547
|
+
signal: ctx.signal,
|
|
205548
|
+
parentToolCallId: ctx.parentToolCallId,
|
|
205549
|
+
mcpTools: ctx.mcpTools ?? []
|
|
205550
|
+
}
|
|
205551
|
+
);
|
|
205552
|
+
return adhocNickname ? { ...result2, nickname: adhocNickname } : result2;
|
|
205553
|
+
} finally {
|
|
205554
|
+
if (adhocManifestId) unregisterWorkerManifest(adhocManifestId);
|
|
205555
|
+
}
|
|
205335
205556
|
}
|
|
205336
205557
|
};
|
|
205337
205558
|
}
|
|
@@ -221145,56 +221366,6 @@ var init_flockModelHints = __esm({
|
|
|
221145
221366
|
}
|
|
221146
221367
|
});
|
|
221147
221368
|
|
|
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
221369
|
// features/perchTerminal/runtime/flock/flockRoles.ts
|
|
221199
221370
|
function listFlockRoleSpecs() {
|
|
221200
221371
|
return Object.values(FLOCK_ROLES);
|
|
@@ -221448,6 +221619,53 @@ var init_flockRoles = __esm({
|
|
|
221448
221619
|
"Return JSON with continuityStatus (pass|partial|fail), contradictions (string[]), styleIssues (string[]), warnings (string[])."
|
|
221449
221620
|
)
|
|
221450
221621
|
},
|
|
221622
|
+
legal_drafter: {
|
|
221623
|
+
roleId: "legal_drafter",
|
|
221624
|
+
workerId: "flock_legal_drafter",
|
|
221625
|
+
displayName: "Legal Drafter",
|
|
221626
|
+
role: "worker",
|
|
221627
|
+
lane: "writer",
|
|
221628
|
+
allowedTools: [
|
|
221629
|
+
TOOL_NAMES.searchKnowledge,
|
|
221630
|
+
TOOL_NAMES.glob,
|
|
221631
|
+
TOOL_NAMES.grep,
|
|
221632
|
+
TOOL_NAMES.readLocalFile,
|
|
221633
|
+
TOOL_NAMES.listLocalSources,
|
|
221634
|
+
TOOL_NAMES.readLocalSourceFile,
|
|
221635
|
+
TOOL_NAMES.writeLocalFile
|
|
221636
|
+
],
|
|
221637
|
+
writesWorkspace: true,
|
|
221638
|
+
maxIterations: 10,
|
|
221639
|
+
outputContract: "JSON { title, markdown, irac: { issue, rules, application, conclusion }, authoritiesCited, needsVerification, notes }",
|
|
221640
|
+
objectiveTemplate: (task) => bounded(
|
|
221641
|
+
task,
|
|
221642
|
+
"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.",
|
|
221643
|
+
"Return JSON with title, markdown, irac {issue, rules, application, conclusion}, authoritiesCited (string[]), needsVerification (string[]), notes (string[])."
|
|
221644
|
+
)
|
|
221645
|
+
},
|
|
221646
|
+
legal_citation_checker: {
|
|
221647
|
+
roleId: "legal_citation_checker",
|
|
221648
|
+
workerId: "flock_legal_citation_checker",
|
|
221649
|
+
displayName: "Legal Citation Checker",
|
|
221650
|
+
role: "verifier",
|
|
221651
|
+
lane: "verifier",
|
|
221652
|
+
// Closed universe by construction: no web or global knowledge tools — the packet is the law.
|
|
221653
|
+
allowedTools: [
|
|
221654
|
+
TOOL_NAMES.glob,
|
|
221655
|
+
TOOL_NAMES.grep,
|
|
221656
|
+
TOOL_NAMES.readLocalFile,
|
|
221657
|
+
TOOL_NAMES.listLocalSources,
|
|
221658
|
+
TOOL_NAMES.readLocalSourceFile
|
|
221659
|
+
],
|
|
221660
|
+
writesWorkspace: false,
|
|
221661
|
+
maxIterations: 10,
|
|
221662
|
+
outputContract: "JSON { verificationStatus: 'pass'|'partial'|'fail', verifiedCitations, unsupportedCitations: [{ citation, claim, reason }], missingFromPacket, warnings }",
|
|
221663
|
+
objectiveTemplate: (task) => bounded(
|
|
221664
|
+
task,
|
|
221665
|
+
"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.",
|
|
221666
|
+
"Return JSON with verificationStatus (pass|partial|fail), verifiedCitations, unsupportedCitations ({citation, claim, reason}[]), missingFromPacket (string[]), warnings (string[])."
|
|
221667
|
+
)
|
|
221668
|
+
},
|
|
221451
221669
|
browser_operator: {
|
|
221452
221670
|
roleId: "browser_operator",
|
|
221453
221671
|
// Reuses the roster manifest from workers/registry — its system prompt and
|
|
@@ -221519,6 +221737,22 @@ function planFlock(rawTask, options = {}) {
|
|
|
221519
221737
|
if ((options.surface ?? detectFlockSurface()) === "cli") {
|
|
221520
221738
|
selected.delete("browser_operator");
|
|
221521
221739
|
}
|
|
221740
|
+
if (selected.has("legal_drafter")) {
|
|
221741
|
+
if (!/\b(write|draft|compose|prepare)\b/i.test(task)) {
|
|
221742
|
+
selected.delete("legal_drafter");
|
|
221743
|
+
selected.add("legal_citation_checker");
|
|
221744
|
+
}
|
|
221745
|
+
selected.delete("writer");
|
|
221746
|
+
selected.delete("researcher");
|
|
221747
|
+
selected.delete("citation_checker");
|
|
221748
|
+
selected.delete("consistency_checker");
|
|
221749
|
+
selected.delete("source_verifier");
|
|
221750
|
+
}
|
|
221751
|
+
if (selected.has("legal_drafter")) {
|
|
221752
|
+
selected.add("legal_citation_checker");
|
|
221753
|
+
} else if (selected.has("legal_citation_checker")) {
|
|
221754
|
+
selected.add("workspace_scout");
|
|
221755
|
+
}
|
|
221522
221756
|
if (selected.has("patch_worker") || selected.has("test_runner") || selected.has("ui_reviewer")) {
|
|
221523
221757
|
selected.add("workspace_scout");
|
|
221524
221758
|
}
|
|
@@ -221592,6 +221826,7 @@ var init_flockPlanner = __esm({
|
|
|
221592
221826
|
{ roleId: "writer", pattern: /\b(write|draft|compose|essay|memo|report|article|document|copy)\b/i },
|
|
221593
221827
|
{ roleId: "citation_checker", pattern: /\b(citations?|verify|check sources?|fact.?check|references?)\b/i },
|
|
221594
221828
|
{ roleId: "consistency_checker", pattern: /\b(fiction|stor(?:y|ies)|novel|chapters?|poems?|screenplay|scripts?|lyrics)\b/i },
|
|
221829
|
+
{ roleId: "legal_drafter", pattern: /\b(irac|memorandum|legal (?:memo|brief|writing|citations?)|case ?packet|bluebook|pin ?cites?)\b/i },
|
|
221595
221830
|
{ roleId: "browser_operator", pattern: /\b(browser|open|navigate|click|fill|submit|login|webpage|url|site)\b/i }
|
|
221596
221831
|
];
|
|
221597
221832
|
ROLE_TRIM_PRIORITY = [
|
|
@@ -221599,6 +221834,8 @@ var init_flockPlanner = __esm({
|
|
|
221599
221834
|
"writer",
|
|
221600
221835
|
"test_runner",
|
|
221601
221836
|
"workspace_scout",
|
|
221837
|
+
"legal_drafter",
|
|
221838
|
+
"legal_citation_checker",
|
|
221602
221839
|
"researcher",
|
|
221603
221840
|
"citation_checker",
|
|
221604
221841
|
"consistency_checker",
|
|
@@ -221641,6 +221878,7 @@ function buildFlockPlannerPrompts(ctx) {
|
|
|
221641
221878
|
"- 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
221879
|
"- 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
221880
|
"- 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).",
|
|
221881
|
+
"- 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
221882
|
"- Decline (accepted=false, with reason) tasks that are trivial, conversational, or need no fanout."
|
|
221645
221883
|
].join("\n");
|
|
221646
221884
|
ensureFlockWorkersRegistered();
|
|
@@ -221746,7 +221984,7 @@ function validateLlmFlockPlan(record, ctx) {
|
|
|
221746
221984
|
const objective = sanitizeText(worker.objective, MAX_OBJECTIVE_CHARS);
|
|
221747
221985
|
if (!displayName || !objective) return null;
|
|
221748
221986
|
const role = VALID_ROLES.has(worker.phase) ? worker.phase : "worker";
|
|
221749
|
-
const outputContract = sanitizeLine(worker.outputContract,
|
|
221987
|
+
const outputContract = sanitizeLine(worker.outputContract, MAX_CONTRACT_CHARS2) || "Return a short structured summary of what was found or done.";
|
|
221750
221988
|
const requestedTools = Array.isArray(worker.allowedTools) ? worker.allowedTools.filter((tool) => typeof tool === "string") : [];
|
|
221751
221989
|
let allowedTools = [...new Set(requestedTools)].filter(
|
|
221752
221990
|
(tool) => available.has(tool) && !FLOCK_FORBIDDEN_TOOLS.includes(tool)
|
|
@@ -221757,7 +221995,7 @@ function validateLlmFlockPlan(record, ctx) {
|
|
|
221757
221995
|
if (!writesWorkspace) {
|
|
221758
221996
|
allowedTools = allowedTools.filter((tool) => !WRITE_TOOLS.has(tool));
|
|
221759
221997
|
}
|
|
221760
|
-
const maxIterations =
|
|
221998
|
+
const maxIterations = clampInt2(worker.maxIterations, 1, caps.maxIterationsPerWorker, 6);
|
|
221761
221999
|
const baseWorkerId = typeof worker.baseWorkerId === "string" && worker.baseWorkerId.trim() ? worker.baseWorkerId.trim() : null;
|
|
221762
222000
|
const reusedManifest = baseWorkerId ? resolveWorkerManifest(baseWorkerId) : null;
|
|
221763
222001
|
const flockWorkerId = `fw${index + 1}_${slugify(displayName)}`;
|
|
@@ -221823,14 +222061,56 @@ function validateLlmFlockPlan(record, ctx) {
|
|
|
221823
222061
|
}
|
|
221824
222062
|
}
|
|
221825
222063
|
}
|
|
221826
|
-
const
|
|
221827
|
-
(worker) => worker.workerId === FLOCK_ROLES.
|
|
222064
|
+
const legalDrafterWorker = surfacedWorkers.find(
|
|
222065
|
+
(worker) => worker.workerId === FLOCK_ROLES.legal_drafter.workerId
|
|
221828
222066
|
);
|
|
222067
|
+
if (legalDrafterWorker && !surfacedWorkers.some((worker) => worker.workerId === FLOCK_ROLES.legal_citation_checker.workerId)) {
|
|
222068
|
+
const spec = FLOCK_ROLES.legal_citation_checker;
|
|
222069
|
+
const genericVerifierIndex = surfacedWorkers.findIndex(
|
|
222070
|
+
(worker) => worker.workerId === FLOCK_ROLES.citation_checker.workerId || worker.workerId === FLOCK_ROLES.consistency_checker.workerId
|
|
222071
|
+
);
|
|
222072
|
+
if (genericVerifierIndex >= 0) {
|
|
222073
|
+
const existing = surfacedWorkers[genericVerifierIndex];
|
|
222074
|
+
surfacedWorkers[genericVerifierIndex] = {
|
|
222075
|
+
...existing,
|
|
222076
|
+
workerId: spec.workerId,
|
|
222077
|
+
displayName: spec.displayName,
|
|
222078
|
+
role: spec.role,
|
|
222079
|
+
objective: spec.objectiveTemplate(ctx.task),
|
|
222080
|
+
maxIterations: Math.min(spec.maxIterations, caps.maxIterationsPerWorker),
|
|
222081
|
+
allowedTools: [...spec.allowedTools],
|
|
222082
|
+
writesWorkspace: false,
|
|
222083
|
+
writeScope: null,
|
|
222084
|
+
outputContract: spec.outputContract,
|
|
222085
|
+
dependsOn: [legalDrafterWorker.flockWorkerId],
|
|
222086
|
+
dynamicManifest: null
|
|
222087
|
+
};
|
|
222088
|
+
} else if (surfacedWorkers.length < FLOCK_MAX_WORKERS) {
|
|
222089
|
+
const index = surfacedWorkers.length;
|
|
222090
|
+
surfacedWorkers.push({
|
|
222091
|
+
flockWorkerId: `fw${index + 1}_${spec.roleId}`,
|
|
222092
|
+
workerId: spec.workerId,
|
|
222093
|
+
displayName: spec.displayName,
|
|
222094
|
+
nickname: flockNicknameFor(ctx.flockId, index),
|
|
222095
|
+
role: spec.role,
|
|
222096
|
+
objective: spec.objectiveTemplate(ctx.task),
|
|
222097
|
+
maxIterations: Math.min(spec.maxIterations, caps.maxIterationsPerWorker),
|
|
222098
|
+
allowedTools: [...spec.allowedTools],
|
|
222099
|
+
writesWorkspace: false,
|
|
222100
|
+
writeScope: null,
|
|
222101
|
+
outputContract: spec.outputContract,
|
|
222102
|
+
dependsOn: [legalDrafterWorker.flockWorkerId],
|
|
222103
|
+
modelOverride: null,
|
|
222104
|
+
dynamicManifest: null
|
|
222105
|
+
});
|
|
222106
|
+
}
|
|
222107
|
+
}
|
|
222108
|
+
const writerWorker = legalDrafterWorker ?? surfacedWorkers.find((worker) => worker.workerId === FLOCK_ROLES.writer.workerId);
|
|
221829
222109
|
if (writerWorker && !surfacedWorkers.some((worker) => worker.role === "verifier") && surfacedWorkers.length < FLOCK_MAX_WORKERS) {
|
|
221830
222110
|
const hasResearch = surfacedWorkers.some(
|
|
221831
222111
|
(worker) => worker.workerId === FLOCK_ROLES.researcher.workerId
|
|
221832
222112
|
);
|
|
221833
|
-
const spec = hasResearch ? FLOCK_ROLES.citation_checker : FLOCK_ROLES.consistency_checker;
|
|
222113
|
+
const spec = legalDrafterWorker ? FLOCK_ROLES.legal_citation_checker : hasResearch ? FLOCK_ROLES.citation_checker : FLOCK_ROLES.consistency_checker;
|
|
221834
222114
|
const index = surfacedWorkers.length;
|
|
221835
222115
|
surfacedWorkers.push({
|
|
221836
222116
|
flockWorkerId: `fw${index + 1}_${spec.roleId}`,
|
|
@@ -221900,11 +222180,11 @@ function sanitizeText(value, max2) {
|
|
|
221900
222180
|
if (typeof value !== "string") return "";
|
|
221901
222181
|
return value.trim().slice(0, max2);
|
|
221902
222182
|
}
|
|
221903
|
-
function
|
|
222183
|
+
function clampInt2(value, min2, max2, fallback) {
|
|
221904
222184
|
const num = typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
221905
222185
|
return Math.min(Math.max(num, min2), max2);
|
|
221906
222186
|
}
|
|
221907
|
-
var FLOCK_FORBIDDEN_TOOLS, WRITE_TOOLS, GUI_ONLY_WORKER_IDS, GUI_ONLY_TOOLS, VALID_ROLES, MAX_DISPLAY_NAME_CHARS, MAX_OBJECTIVE_CHARS,
|
|
222187
|
+
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
222188
|
var init_flockLlmPlanner = __esm({
|
|
221909
222189
|
"features/perchTerminal/runtime/flock/flockLlmPlanner.ts"() {
|
|
221910
222190
|
"use strict";
|
|
@@ -221945,7 +222225,7 @@ var init_flockLlmPlanner = __esm({
|
|
|
221945
222225
|
VALID_ROLES = /* @__PURE__ */ new Set(["scout", "worker", "verifier", "reducer"]);
|
|
221946
222226
|
MAX_DISPLAY_NAME_CHARS = 40;
|
|
221947
222227
|
MAX_OBJECTIVE_CHARS = 1200;
|
|
221948
|
-
|
|
222228
|
+
MAX_CONTRACT_CHARS2 = 240;
|
|
221949
222229
|
MAX_ROSTER_IN_PROMPT = 24;
|
|
221950
222230
|
MAX_TOOLS_IN_PROMPT = 80;
|
|
221951
222231
|
}
|
|
@@ -222088,14 +222368,19 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222088
222368
|
}
|
|
222089
222369
|
const spawnFn = options.spawnWorkerFn ?? spawnWorker;
|
|
222090
222370
|
const outcomes = [];
|
|
222371
|
+
const revisionOutcomes = [];
|
|
222372
|
+
let revisionReport = null;
|
|
222091
222373
|
const outputByFlockWorkerId = /* @__PURE__ */ new Map();
|
|
222092
|
-
const sharedContext = {
|
|
222374
|
+
const sharedContext = {
|
|
222375
|
+
task,
|
|
222376
|
+
currentDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
222377
|
+
};
|
|
222093
222378
|
let toolCallsUsed = 0;
|
|
222094
222379
|
let toolCallsReserved = 0;
|
|
222095
222380
|
let workersAwaitingLaunch = plan.workers.length;
|
|
222096
222381
|
try {
|
|
222097
222382
|
const phases = [...new Set(plan.workers.map((worker) => FLOCK_ROLE_ORDER[worker.role]))].sort((a, b2) => a - b2);
|
|
222098
|
-
const runReadyWorker = async (worker) => {
|
|
222383
|
+
const runReadyWorker = async (worker, contextOverride) => {
|
|
222099
222384
|
const workersLeftIncludingThis = Math.max(1, workersAwaitingLaunch);
|
|
222100
222385
|
workersAwaitingLaunch = Math.max(0, workersAwaitingLaunch - 1);
|
|
222101
222386
|
if (flockRun.controller.signal.aborted) {
|
|
@@ -222130,11 +222415,14 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222130
222415
|
toolCallsReserved += reservedToolCalls;
|
|
222131
222416
|
emitWorkerUpdate(emit, plan, worker, "running");
|
|
222132
222417
|
try {
|
|
222418
|
+
const context = contextOverride ?? buildWorkerContext(worker, sharedContext, outputByFlockWorkerId, plan);
|
|
222419
|
+
const objective = worker.role === "worker" && contextContainsSources(context) ? `${worker.objective}
|
|
222420
|
+
${FLOCK_GROUNDING_CONTRACT}` : worker.objective;
|
|
222133
222421
|
const result2 = await spawnFn(
|
|
222134
222422
|
{
|
|
222135
222423
|
workerId: worker.workerId,
|
|
222136
|
-
objective
|
|
222137
|
-
context
|
|
222424
|
+
objective,
|
|
222425
|
+
context,
|
|
222138
222426
|
maxIterations: worker.maxIterations,
|
|
222139
222427
|
maxToolCalls: reservedToolCalls
|
|
222140
222428
|
},
|
|
@@ -222191,7 +222479,9 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222191
222479
|
break;
|
|
222192
222480
|
}
|
|
222193
222481
|
for (const worker of readyWorkers) pending.delete(worker.flockWorkerId);
|
|
222194
|
-
const batchOutcomes = await Promise.all(
|
|
222482
|
+
const batchOutcomes = await Promise.all(
|
|
222483
|
+
readyWorkers.map((worker) => runReadyWorker(worker))
|
|
222484
|
+
);
|
|
222195
222485
|
for (const outcome of batchOutcomes) {
|
|
222196
222486
|
outcomes.push(outcome);
|
|
222197
222487
|
if (!outcome.result) continue;
|
|
@@ -222201,6 +222491,43 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222201
222491
|
}
|
|
222202
222492
|
}
|
|
222203
222493
|
}
|
|
222494
|
+
const candidate = findRevisionCandidate(plan, outcomes, outputByFlockWorkerId);
|
|
222495
|
+
if (candidate && !flockRun.controller.signal.aborted) {
|
|
222496
|
+
revisionReport = candidate;
|
|
222497
|
+
const revisionWorker = {
|
|
222498
|
+
...candidate.draftWorker,
|
|
222499
|
+
flockWorkerId: `${candidate.draftWorker.flockWorkerId}_r1`,
|
|
222500
|
+
displayName: `${candidate.draftWorker.displayName} (revision)`,
|
|
222501
|
+
objective: buildRevisionObjective(task, candidate),
|
|
222502
|
+
dependsOn: []
|
|
222503
|
+
};
|
|
222504
|
+
const revisionOutcome = await runReadyWorker(revisionWorker, {
|
|
222505
|
+
task,
|
|
222506
|
+
currentDate: sharedContext.currentDate,
|
|
222507
|
+
draft: candidate.draftOutput,
|
|
222508
|
+
verifierFindings: candidate.findings.raw
|
|
222509
|
+
});
|
|
222510
|
+
revisionOutcomes.push(revisionOutcome);
|
|
222511
|
+
candidate.revised = revisionOutcome.status === "done";
|
|
222512
|
+
if (revisionOutcome.result) {
|
|
222513
|
+
const revisedOutput = revisionOutcome.result.structuredOutput ?? clampLine(revisionOutcome.result.summary, 1200);
|
|
222514
|
+
sharedContext[revisionWorker.displayName] = revisedOutput;
|
|
222515
|
+
outputByFlockWorkerId.set(revisionWorker.flockWorkerId, revisedOutput);
|
|
222516
|
+
const recheckWorker = {
|
|
222517
|
+
...candidate.verifierWorker,
|
|
222518
|
+
flockWorkerId: `${candidate.verifierWorker.flockWorkerId}_r1`,
|
|
222519
|
+
displayName: `${candidate.verifierWorker.displayName} (recheck)`,
|
|
222520
|
+
dependsOn: []
|
|
222521
|
+
};
|
|
222522
|
+
const recheckOutcome = await runReadyWorker(recheckWorker, {
|
|
222523
|
+
task,
|
|
222524
|
+
currentDate: sharedContext.currentDate,
|
|
222525
|
+
dependencies: { [revisionWorker.displayName]: revisedOutput }
|
|
222526
|
+
});
|
|
222527
|
+
revisionOutcomes.push(recheckOutcome);
|
|
222528
|
+
candidate.recheckFindings = recheckOutcome.result ? extractVerifierFindings(recheckOutcome.result.structuredOutput) : null;
|
|
222529
|
+
}
|
|
222530
|
+
}
|
|
222204
222531
|
} finally {
|
|
222205
222532
|
clearTimeout(wallTimer);
|
|
222206
222533
|
finishRuntimeRun(
|
|
@@ -222213,7 +222540,14 @@ async function runFlockTurn(input, deps, options = {}) {
|
|
|
222213
222540
|
const workersFailed = outcomes.filter((outcome) => outcome.status === "failed").length;
|
|
222214
222541
|
const flockStatus = userCancelled || wallTimeHit && workersDone === 0 ? "cancelled" : workersFailed === 0 && workersDone === plan.workers.length ? "completed" : workersDone > 0 ? "partial" : "failed";
|
|
222215
222542
|
const assistantText = [
|
|
222216
|
-
buildFlockSummary(
|
|
222543
|
+
buildFlockSummary(
|
|
222544
|
+
plan,
|
|
222545
|
+
[...outcomes, ...revisionOutcomes],
|
|
222546
|
+
flockStatus,
|
|
222547
|
+
toolCallsUsed,
|
|
222548
|
+
wallTimeHit
|
|
222549
|
+
),
|
|
222550
|
+
...buildVerificationSection(revisionReport),
|
|
222217
222551
|
...modelOverrides.applied.map(
|
|
222218
222552
|
(override) => `Model override: ${override.displayName} ran on ${override.label}.`
|
|
222219
222553
|
),
|
|
@@ -222300,7 +222634,117 @@ function buildWorkerContext(worker, sharedContext, outputByFlockWorkerId, plan)
|
|
|
222300
222634
|
const depWorker = plan.workers.find((candidate) => candidate.flockWorkerId === dep);
|
|
222301
222635
|
dependencies[depWorker?.displayName ?? dep] = output;
|
|
222302
222636
|
}
|
|
222303
|
-
return { task: sharedContext.task, dependencies };
|
|
222637
|
+
return { task: sharedContext.task, currentDate: sharedContext.currentDate, dependencies };
|
|
222638
|
+
}
|
|
222639
|
+
function contextContainsSources(context) {
|
|
222640
|
+
const scan = (value, depth) => {
|
|
222641
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || depth > 3) return false;
|
|
222642
|
+
const record = value;
|
|
222643
|
+
const sources = record.sources;
|
|
222644
|
+
if (Array.isArray(sources) && sources.some(
|
|
222645
|
+
(source) => source && typeof source === "object" && typeof source.url === "string"
|
|
222646
|
+
)) {
|
|
222647
|
+
return true;
|
|
222648
|
+
}
|
|
222649
|
+
return Object.values(record).some((nested) => scan(nested, depth + 1));
|
|
222650
|
+
};
|
|
222651
|
+
return scan(context, 0);
|
|
222652
|
+
}
|
|
222653
|
+
function extractVerifierFindings(output) {
|
|
222654
|
+
if (!output || typeof output !== "object" || Array.isArray(output)) return null;
|
|
222655
|
+
const record = output;
|
|
222656
|
+
const verdictRaw = record.verificationStatus ?? record.continuityStatus ?? record.verdict;
|
|
222657
|
+
const verdict = typeof verdictRaw === "string" ? verdictRaw.trim().toLowerCase() : null;
|
|
222658
|
+
const issues = [];
|
|
222659
|
+
for (const key of [
|
|
222660
|
+
"unsupportedClaims",
|
|
222661
|
+
"unsupportedCitations",
|
|
222662
|
+
"missingFromPacket",
|
|
222663
|
+
"contradictions",
|
|
222664
|
+
"styleIssues",
|
|
222665
|
+
"unsupported"
|
|
222666
|
+
]) {
|
|
222667
|
+
const value = record[key];
|
|
222668
|
+
if (!Array.isArray(value)) continue;
|
|
222669
|
+
for (const item of value) {
|
|
222670
|
+
const text = typeof item === "string" ? item : JSON.stringify(item);
|
|
222671
|
+
const clean = clampLine(text, 200);
|
|
222672
|
+
if (clean && clean !== "{}") issues.push(clean);
|
|
222673
|
+
}
|
|
222674
|
+
}
|
|
222675
|
+
return { verdict, issues, raw: record };
|
|
222676
|
+
}
|
|
222677
|
+
function verifierFindingsNeedRevision(findings) {
|
|
222678
|
+
if (!findings) return false;
|
|
222679
|
+
return findings.issues.length > 0 || findings.verdict === "fail" || findings.verdict === "partial";
|
|
222680
|
+
}
|
|
222681
|
+
function findRevisionCandidate(plan, outcomes, outputByFlockWorkerId) {
|
|
222682
|
+
for (const outcome of outcomes) {
|
|
222683
|
+
if (outcome.worker.role !== "verifier" || outcome.status !== "done" || !outcome.result) {
|
|
222684
|
+
continue;
|
|
222685
|
+
}
|
|
222686
|
+
const findings = extractVerifierFindings(outcome.result.structuredOutput);
|
|
222687
|
+
if (!verifierFindingsNeedRevision(findings)) continue;
|
|
222688
|
+
const draftWorker = resolveDraftWorkerForVerifier(plan, outcome.worker, outcomes);
|
|
222689
|
+
if (!draftWorker) continue;
|
|
222690
|
+
const draftOutput = outputByFlockWorkerId.get(draftWorker.flockWorkerId);
|
|
222691
|
+
if (draftOutput === void 0) continue;
|
|
222692
|
+
return {
|
|
222693
|
+
verifierWorker: outcome.worker,
|
|
222694
|
+
draftWorker,
|
|
222695
|
+
draftOutput,
|
|
222696
|
+
findings,
|
|
222697
|
+
revised: false,
|
|
222698
|
+
recheckFindings: null
|
|
222699
|
+
};
|
|
222700
|
+
}
|
|
222701
|
+
return null;
|
|
222702
|
+
}
|
|
222703
|
+
function resolveDraftWorkerForVerifier(plan, verifier, outcomes) {
|
|
222704
|
+
for (const dep of verifier.dependsOn) {
|
|
222705
|
+
const worker = plan.workers.find((candidate) => candidate.flockWorkerId === dep);
|
|
222706
|
+
if (worker?.role === "worker") return worker;
|
|
222707
|
+
}
|
|
222708
|
+
const doneWorkers = outcomes.filter((outcome) => outcome.status === "done" && outcome.worker.role === "worker").map((outcome) => outcome.worker);
|
|
222709
|
+
const writer = doneWorkers.find(
|
|
222710
|
+
(worker) => worker.workerId === "flock_legal_drafter" || worker.workerId === "flock_writer"
|
|
222711
|
+
);
|
|
222712
|
+
if (writer) return writer;
|
|
222713
|
+
const writingWorkers = doneWorkers.filter((worker) => worker.writesWorkspace);
|
|
222714
|
+
if (writingWorkers.length === 1) return writingWorkers[0];
|
|
222715
|
+
return doneWorkers.length === 1 ? doneWorkers[0] : null;
|
|
222716
|
+
}
|
|
222717
|
+
function buildRevisionObjective(task, candidate) {
|
|
222718
|
+
return [
|
|
222719
|
+
"Bounded Flock revision \u2014 stay strictly inside this scope.",
|
|
222720
|
+
`Task: ${task}`,
|
|
222721
|
+
`A verifier (${candidate.verifierWorker.displayName}) reviewed your draft (context key "draft") and reported findings (context key "verifierFindings").`,
|
|
222722
|
+
"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.",
|
|
222723
|
+
`Output contract: ${candidate.draftWorker.outputContract}`,
|
|
222724
|
+
"Do not delegate, spawn workers, or start suites. Stop when the findings are addressed."
|
|
222725
|
+
].join("\n");
|
|
222726
|
+
}
|
|
222727
|
+
function buildVerificationSection(report) {
|
|
222728
|
+
if (!report) return [];
|
|
222729
|
+
const lines = [];
|
|
222730
|
+
lines.push(
|
|
222731
|
+
`Verification (${report.verifierWorker.displayName}): ${report.findings.verdict ?? "issues found"} \u2014 ${report.findings.issues.length} finding(s).`
|
|
222732
|
+
);
|
|
222733
|
+
const shown = report.findings.issues.slice(0, 6);
|
|
222734
|
+
for (const issue of shown) lines.push(`- ${issue}`);
|
|
222735
|
+
if (report.findings.issues.length > shown.length) {
|
|
222736
|
+
lines.push(`- \u2026and ${report.findings.issues.length - shown.length} more.`);
|
|
222737
|
+
}
|
|
222738
|
+
if (report.revised) {
|
|
222739
|
+
const recheck = report.recheckFindings;
|
|
222740
|
+
lines.push(
|
|
222741
|
+
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."
|
|
222742
|
+
);
|
|
222743
|
+
} else {
|
|
222744
|
+
lines.push("No revision pass ran \u2014 the findings above stand as reported.");
|
|
222745
|
+
}
|
|
222746
|
+
lines.push("Ask about any finding to discuss it, or rerun /flock to retry with adjustments.");
|
|
222747
|
+
return lines;
|
|
222304
222748
|
}
|
|
222305
222749
|
function buildSpawnContext(input, flockId, signal, emit, worker) {
|
|
222306
222750
|
return {
|
|
@@ -222365,6 +222809,7 @@ function makeFlockRunId() {
|
|
|
222365
222809
|
function now11() {
|
|
222366
222810
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
222367
222811
|
}
|
|
222812
|
+
var FLOCK_GROUNDING_CONTRACT;
|
|
222368
222813
|
var init_runFlockTurn = __esm({
|
|
222369
222814
|
"features/perchTerminal/runtime/flock/runFlockTurn.ts"() {
|
|
222370
222815
|
"use strict";
|
|
@@ -222378,6 +222823,12 @@ var init_runFlockTurn = __esm({
|
|
|
222378
222823
|
init_flockLlmPlanner();
|
|
222379
222824
|
init_flockPlanner();
|
|
222380
222825
|
init_flockRoles();
|
|
222826
|
+
FLOCK_GROUNDING_CONTRACT = [
|
|
222827
|
+
"Grounding contract: your context includes sources[] gathered by research.",
|
|
222828
|
+
"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.",
|
|
222829
|
+
"Claims you cannot support from sources[] belong in needsVerification, never in the prose as fact.",
|
|
222830
|
+
"Do not cite anything that is not in sources[]."
|
|
222831
|
+
].join(" ");
|
|
222381
222832
|
}
|
|
222382
222833
|
});
|
|
222383
222834
|
|