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.
Files changed (2) hide show
  1. package/dist/perch.mjs +541 -90
  2. 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: ["workerId", "objective"],
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
- const workerId = String(args.workerId ?? "");
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
- return spawnWorker(
205316
- { workerId, objective: workerObjective, context: enrichedContext, lane, maxIterations },
205317
- {
205318
- workspaceRoot: effectiveWorkspaceRoot(ctx),
205319
- desktopConnected: ctx.desktopConnected,
205320
- activeRootPath: ctx.activeRootPath,
205321
- permissionMode: ctx.permissionMode,
205322
- workspaceId: ctx.workspaceId,
205323
- threadId: ctx.threadId,
205324
- selectedSourceId: ctx.selectedSourceId,
205325
- supabaseConfigured: ctx.supabaseConfigured,
205326
- supabase: ctx.supabase,
205327
- runId: ctx.runId,
205328
- founderModelSelection: ctx.founderModelSelection ?? null,
205329
- onEvent: ctx.onEvent,
205330
- signal: ctx.signal,
205331
- parentToolCallId: ctx.parentToolCallId,
205332
- mcpTools: ctx.mcpTools ?? []
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, MAX_CONTRACT_CHARS) || "Return a short structured summary of what was found or done.";
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 = clampInt(worker.maxIterations, 1, caps.maxIterationsPerWorker, 6);
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 writerWorker = surfacedWorkers.find(
221827
- (worker) => worker.workerId === FLOCK_ROLES.writer.workerId
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 clampInt(value, min2, max2, fallback) {
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, MAX_CONTRACT_CHARS, MAX_ROSTER_IN_PROMPT, MAX_TOOLS_IN_PROMPT;
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
- MAX_CONTRACT_CHARS = 240;
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 = { task };
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: worker.objective,
222137
- context: buildWorkerContext(worker, sharedContext, outputByFlockWorkerId, plan),
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(readyWorkers.map(runReadyWorker));
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(plan, outcomes, flockStatus, toolCallsUsed, wallTimeHit),
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perchai-cli",
3
- "version": "2.4.33",
3
+ "version": "2.4.34",
4
4
  "description": "Perch AI command-line interface",
5
5
  "bin": {
6
6
  "perch": "bin/perch"