opencode-swarm 7.79.7 → 7.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -69,7 +69,7 @@ var package_default;
69
69
  var init_package = __esm(() => {
70
70
  package_default = {
71
71
  name: "opencode-swarm",
72
- version: "7.79.7",
72
+ version: "7.81.0",
73
73
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
74
74
  main: "dist/index.js",
75
75
  types: "dist/index.d.ts",
@@ -683,6 +683,14 @@ var init_tool_metadata = __esm(() => {
683
683
  description: "dispatch multiple read-only exploration/review lanes concurrently and return a structured join result",
684
684
  agents: ["architect"]
685
685
  },
686
+ dispatch_lanes_async: {
687
+ description: "launch multiple read-only advisory lanes asynchronously and return a batch id for later collection",
688
+ agents: ["architect"]
689
+ },
690
+ collect_lane_results: {
691
+ description: "collect or poll results for a dispatch_lanes_async advisory batch without advancing workflow gates",
692
+ agents: ["architect"]
693
+ },
686
694
  summarize_work: {
687
695
  description: "emit a short structured summary of completed work (key decisions, assumptions, risks, constraints) at task completion; rolls up per phase for architecture-supervisor review. Advisory, never blocks.",
688
696
  agents: [
@@ -15967,7 +15975,8 @@ var init_schema = __esm(() => {
15967
15975
  }).optional(),
15968
15976
  enrichment: exports_external.object({
15969
15977
  max_calls_per_day: exports_external.number().int().min(0).max(1000).default(30),
15970
- quota_window: exports_external.enum(["utc", "local"]).default("utc")
15978
+ quota_window: exports_external.enum(["utc", "local"]).default("utc"),
15979
+ batch_size: exports_external.number().int().min(1).max(100).optional()
15971
15980
  }).default({ max_calls_per_day: 30, quota_window: "utc" })
15972
15981
  });
15973
15982
  MemoryConfigSchema = exports_external.object({
@@ -23590,6 +23599,21 @@ function rememberStandardWorktreeDispatch(dispatch) {
23590
23599
  function hasStandardWorktreeDispatchCapacity() {
23591
23600
  return standardWorktreeByCallID.size < MAX_TRACKED_STANDARD_WORKTREE_CALLS;
23592
23601
  }
23602
+ function hasInFlightStandardWorktreeDispatch(parentSessionID) {
23603
+ for (const dispatch of standardWorktreeByCallID.values()) {
23604
+ if (dispatch.parentSessionID === parentSessionID)
23605
+ return true;
23606
+ }
23607
+ return false;
23608
+ }
23609
+ function handleStandardWorktreeFailure(parentSessionID, policy, message) {
23610
+ if (policy === "required")
23611
+ throw new Error(message);
23612
+ if (hasInFlightStandardWorktreeDispatch(parentSessionID)) {
23613
+ throw new Error(`STANDARD_WORKTREE_ISOLATION_UNSAFE: ${message} ` + `Sibling coder task(s) are isolated in worktrees for this session, so ` + `dispatching this coder un-isolated in the main tree would risk a ` + `merge-back collision. Wait for in-flight coder task(s) to be reviewed ` + `and merged, then retry.`);
23614
+ }
23615
+ serializeStandardWorktreeDispatches(parentSessionID, message);
23616
+ }
23593
23617
  function serializeStandardWorktreeDispatches(sessionID, message) {
23594
23618
  rememberStandardWorktreeSerializationSession(sessionID);
23595
23619
  const session = ensureAgentSession(sessionID);
@@ -23643,9 +23667,7 @@ async function precreateStandardWorktreeSession(args2) {
23643
23667
  const client = swarmState.opencodeClient;
23644
23668
  if (!client) {
23645
23669
  const message = "STANDARD_WORKTREE_ISOLATION_UNAVAILABLE: OpenCode SDK client is unavailable; standard parallel coder work cannot be isolated.";
23646
- if (worktreeConfig.policy === "required")
23647
- throw new Error(message);
23648
- serializeStandardWorktreeDispatches(args2.parentSessionID, message);
23670
+ handleStandardWorktreeFailure(args2.parentSessionID, worktreeConfig.policy, message);
23649
23671
  return;
23650
23672
  }
23651
23673
  const provisionResult = await _internals12.provisionWorktree(args2.directory, args2.taskId, args2.parentSessionID, {
@@ -23654,10 +23676,8 @@ async function precreateStandardWorktreeSession(args2) {
23654
23676
  mergeStrategy: worktreeConfig.merge_strategy
23655
23677
  });
23656
23678
  if ("error" in provisionResult) {
23657
- const message = `STANDARD_WORKTREE_PROVISION_FAILED: ${provisionResult.error}`;
23658
- if (worktreeConfig.policy === "required")
23659
- throw new Error(message);
23660
- serializeStandardWorktreeDispatches(args2.parentSessionID, `${message}.`);
23679
+ const message = `STANDARD_WORKTREE_PROVISION_FAILED: ${provisionResult.error}.`;
23680
+ handleStandardWorktreeFailure(args2.parentSessionID, worktreeConfig.policy, message);
23661
23681
  return;
23662
23682
  }
23663
23683
  const createResult = await client.session.create({
@@ -23671,10 +23691,8 @@ async function precreateStandardWorktreeSession(args2) {
23671
23691
  await _internals12.removeWorktree(provisionResult.worktreePath, args2.directory).catch(() => {});
23672
23692
  const createError = createResult.error;
23673
23693
  const detail = typeof createError === "string" ? createError : JSON.stringify(createError ?? "missing session id");
23674
- const message = `STANDARD_WORKTREE_SESSION_CREATE_FAILED: ${detail}`;
23675
- if (worktreeConfig.policy === "required")
23676
- throw new Error(message);
23677
- serializeStandardWorktreeDispatches(args2.parentSessionID, `${message}.`);
23694
+ const message = `STANDARD_WORKTREE_SESSION_CREATE_FAILED: ${detail}.`;
23695
+ handleStandardWorktreeFailure(args2.parentSessionID, worktreeConfig.policy, message);
23678
23696
  return;
23679
23697
  }
23680
23698
  args2.outputArgs.task_id = createResult.data.id;
@@ -42738,7 +42756,47 @@ function parseTaskEnvelope(text) {
42738
42756
  const state = match[2];
42739
42757
  if (!sessionId)
42740
42758
  return null;
42741
- return { sessionId, state };
42759
+ const summary = extractTagText(text, "summary");
42760
+ const resultRaw = state === "error" ? extractTagText(text, "task_error") : extractTagText(text, "task_result");
42761
+ const bounded = boundEnvelopeText(resultRaw);
42762
+ return {
42763
+ sessionId,
42764
+ state,
42765
+ ...summary !== undefined ? { summary } : {},
42766
+ ...state === "error" && bounded ? {
42767
+ errorText: bounded.text,
42768
+ resultChars: bounded.chars,
42769
+ resultTruncated: bounded.truncated
42770
+ } : {},
42771
+ ...state !== "error" && bounded ? {
42772
+ resultText: bounded.text,
42773
+ resultChars: bounded.chars,
42774
+ resultTruncated: bounded.truncated
42775
+ } : {}
42776
+ };
42777
+ }
42778
+ function extractTagText(text, tag) {
42779
+ const re = new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`);
42780
+ const match = text.match(re);
42781
+ if (!match)
42782
+ return;
42783
+ return match[1];
42784
+ }
42785
+ function boundEnvelopeText(text) {
42786
+ if (text === undefined)
42787
+ return;
42788
+ if (text.length <= MAX_TASK_ENVELOPE_TEXT_CHARS) {
42789
+ return { text, chars: text.length, truncated: false };
42790
+ }
42791
+ const omitted = text.length - MAX_TASK_ENVELOPE_TEXT_CHARS;
42792
+ const suffix = `
42793
+ [... ${omitted} chars truncated by task-envelope ...]`;
42794
+ const maxContent = Math.max(0, MAX_TASK_ENVELOPE_TEXT_CHARS - suffix.length);
42795
+ return {
42796
+ text: `${text.slice(0, maxContent)}${suffix}`,
42797
+ chars: text.length,
42798
+ truncated: true
42799
+ };
42742
42800
  }
42743
42801
  function extractDispatchIds(output) {
42744
42802
  let subagentSessionId = null;
@@ -42768,7 +42826,7 @@ function extractDispatchIds(output) {
42768
42826
  }
42769
42827
  return { subagentSessionId, jobId };
42770
42828
  }
42771
- var TASK_ENVELOPE_RE;
42829
+ var TASK_ENVELOPE_RE, MAX_TASK_ENVELOPE_TEXT_CHARS = 20000;
42772
42830
  var init_task_envelope = __esm(() => {
42773
42831
  TASK_ENVELOPE_RE = /<task\s+id="([^"]+)"\s+state="(running|completed|error)"\s*>/;
42774
42832
  });
@@ -42779,7 +42837,10 @@ __export(exports_pending_delegations, {
42779
42837
  sweepStaleDelegations: () => sweepStaleDelegations,
42780
42838
  recordPendingDelegation: () => recordPendingDelegation,
42781
42839
  readDelegations: () => readDelegations,
42840
+ findOpenAsyncLaneBatches: () => findOpenAsyncLaneBatches,
42782
42841
  findByCorrelationId: () => findByCorrelationId,
42842
+ findByBatchId: () => findByBatchId,
42843
+ appendDelegationTransition: () => appendDelegationTransition,
42783
42844
  BACKGROUND_DELEGATIONS_FILE: () => BACKGROUND_DELEGATIONS_FILE
42784
42845
  });
42785
42846
  import * as fs15 from "node:fs";
@@ -42836,7 +42897,7 @@ function appendRecord(directory, record2) {
42836
42897
  async function recordPendingDelegation(directory, input, options = {}) {
42837
42898
  const now = Date.now();
42838
42899
  const record2 = {
42839
- schemaVersion: 1,
42900
+ schemaVersion: input.batchId ? 2 : 1,
42840
42901
  correlationId: input.correlationId,
42841
42902
  jobId: input.jobId,
42842
42903
  subagentSessionId: input.subagentSessionId,
@@ -42848,7 +42909,13 @@ async function recordPendingDelegation(directory, input, options = {}) {
42848
42909
  evidenceTaskId: input.evidenceTaskId,
42849
42910
  status: "pending",
42850
42911
  createdAt: now,
42851
- updatedAt: now
42912
+ updatedAt: now,
42913
+ ...input.batchId ? { batchId: input.batchId } : {},
42914
+ ...input.laneId ? { laneId: input.laneId } : {},
42915
+ ...input.mode ? { mode: input.mode } : {},
42916
+ ...input.promptHash ? { promptHash: input.promptHash } : {},
42917
+ ...input.workspace ? { workspace: input.workspace } : {},
42918
+ ...input.generation !== undefined ? { generation: input.generation } : {}
42852
42919
  };
42853
42920
  try {
42854
42921
  await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
@@ -42863,10 +42930,49 @@ async function recordPendingDelegation(directory, input, options = {}) {
42863
42930
  return null;
42864
42931
  }
42865
42932
  }
42933
+ async function appendDelegationTransition(directory, correlationId, transition) {
42934
+ const now = Date.now();
42935
+ try {
42936
+ let next = null;
42937
+ await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
42938
+ const current = findByCorrelationId(directory, correlationId);
42939
+ if (!current)
42940
+ return;
42941
+ if (isTerminal(current.status) && transition.status !== "consumed") {
42942
+ next = current;
42943
+ return;
42944
+ }
42945
+ next = {
42946
+ ...current,
42947
+ schemaVersion: current.schemaVersion === 1 ? 2 : current.schemaVersion,
42948
+ status: transition.status,
42949
+ updatedAt: now,
42950
+ ...transition.completedAt !== undefined ? { completedAt: transition.completedAt } : transition.status === "completed" || transition.status === "error" ? { completedAt: now } : {},
42951
+ ...transition.result ? { result: transition.result } : {}
42952
+ };
42953
+ appendRecord(directory, next);
42954
+ });
42955
+ return next;
42956
+ } catch (err2) {
42957
+ warn(`[background] appendDelegationTransition failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
42958
+ return null;
42959
+ }
42960
+ }
42961
+ function findByBatchId(directory, batchId, opts) {
42962
+ if (!batchId)
42963
+ return [];
42964
+ return readDelegations(directory).filter((record2) => record2.batchId === batchId && (opts?.parentSessionId === undefined || record2.parentSessionId === opts.parentSessionId));
42965
+ }
42966
+ function findOpenAsyncLaneBatches(directory) {
42967
+ return readDelegations(directory).filter((record2) => record2.batchId !== undefined && (record2.status === "pending" || record2.status === "running"));
42968
+ }
42969
+ function isTerminal(status) {
42970
+ return status === "completed" || status === "error" || status === "cancelled" || status === "stale" || status === "consumed";
42971
+ }
42866
42972
  function sweepStaleLocked(directory, timeoutMs, now) {
42867
42973
  let swept = 0;
42868
42974
  for (const record2 of readDelegations(directory)) {
42869
- if (record2.status !== "pending")
42975
+ if (record2.status !== "pending" && record2.status !== "running")
42870
42976
  continue;
42871
42977
  if (now - record2.updatedAt <= timeoutMs)
42872
42978
  continue;
@@ -42889,14 +42995,28 @@ async function sweepStaleDelegations(directory, timeoutMs) {
42889
42995
  return 0;
42890
42996
  }
42891
42997
  }
42892
- var BACKGROUND_DELEGATIONS_FILE = "background-delegations.jsonl", STORE_LOCK_AGENT = "background", STORE_LOCK_TASK = "background-delegations", RecordSchema;
42998
+ var BACKGROUND_DELEGATIONS_FILE = "background-delegations.jsonl", STORE_LOCK_AGENT = "background", STORE_LOCK_TASK = "background-delegations", ResultSchema, WorkspaceSchema, RecordSchema;
42893
42999
  var init_pending_delegations = __esm(() => {
42894
43000
  init_zod();
42895
43001
  init_lock();
42896
43002
  init_utils2();
42897
43003
  init_logger();
43004
+ ResultSchema = exports_external.object({
43005
+ text: exports_external.string().optional(),
43006
+ error: exports_external.string().optional(),
43007
+ chars: exports_external.number(),
43008
+ truncated: exports_external.boolean(),
43009
+ digest: exports_external.string()
43010
+ }).strict();
43011
+ WorkspaceSchema = exports_external.object({
43012
+ directory: exports_external.string(),
43013
+ gitHead: exports_external.string().nullable(),
43014
+ dirtyHash: exports_external.string().nullable(),
43015
+ prHeadSha: exports_external.string().nullable(),
43016
+ scope: exports_external.string().nullable()
43017
+ }).strict();
42898
43018
  RecordSchema = exports_external.object({
42899
- schemaVersion: exports_external.literal(1),
43019
+ schemaVersion: exports_external.union([exports_external.literal(1), exports_external.literal(2)]),
42900
43020
  correlationId: exports_external.string().min(1),
42901
43021
  jobId: exports_external.string().nullable(),
42902
43022
  subagentSessionId: exports_external.string().min(1),
@@ -42906,9 +43026,25 @@ var init_pending_delegations = __esm(() => {
42906
43026
  swarmPrefixedAgent: exports_external.string(),
42907
43027
  planTaskId: exports_external.string().nullable(),
42908
43028
  evidenceTaskId: exports_external.string().nullable(),
42909
- status: exports_external.enum(["pending", "completed", "error", "stale"]),
43029
+ status: exports_external.enum([
43030
+ "pending",
43031
+ "running",
43032
+ "completed",
43033
+ "error",
43034
+ "cancelled",
43035
+ "stale",
43036
+ "consumed"
43037
+ ]),
42910
43038
  createdAt: exports_external.number(),
42911
- updatedAt: exports_external.number()
43039
+ updatedAt: exports_external.number(),
43040
+ batchId: exports_external.string().optional(),
43041
+ laneId: exports_external.string().optional(),
43042
+ mode: exports_external.string().optional(),
43043
+ promptHash: exports_external.string().optional(),
43044
+ workspace: WorkspaceSchema.optional(),
43045
+ generation: exports_external.number().optional(),
43046
+ result: ResultSchema.optional(),
43047
+ completedAt: exports_external.number().optional()
42912
43048
  }).strict();
42913
43049
  });
42914
43050
 
@@ -43470,6 +43606,14 @@ function createDelegationGateHook(config2, directory) {
43470
43606
  const session = ensureAgentSession(input.sessionID);
43471
43607
  if (!session || !session.taskWorkflowStates)
43472
43608
  return;
43609
+ const plan = await _internals20.loadPlanJsonOnly(directory);
43610
+ const profile = plan?.execution_profile;
43611
+ const parallelEnabled = profile?.parallelization_enabled === true;
43612
+ const maxConcurrent = profile?.max_concurrent_tasks ?? 10;
43613
+ const effectiveMaxConcurrent = session.maxConcurrencyOverride ?? maxConcurrent;
43614
+ const parallelModeActive = parallelEnabled && effectiveMaxConcurrent > 1 && !hasActiveLeanTurbo(input.sessionID);
43615
+ const planTaskIds = plan ? new Set(plan.phases.flatMap((phase) => phase.tasks.map((t) => t.id))) : new Set;
43616
+ const incomingCoderTaskId = resolveDelegatedPlanTaskId(args2, planTaskIds);
43473
43617
  for (const [taskId, state] of session.taskWorkflowStates) {
43474
43618
  if (state !== "coder_delegated")
43475
43619
  continue;
@@ -43487,24 +43631,29 @@ function createDelegationGateHook(config2, directory) {
43487
43631
  if (!isTier3)
43488
43632
  continue;
43489
43633
  }
43634
+ if (parallelModeActive && incomingCoderTaskId && taskId !== incomingCoderTaskId) {
43635
+ continue;
43636
+ }
43490
43637
  throw new Error(`REVIEWER_GATE_VIOLATION: Cannot re-delegate to coder without reviewer delegation. ` + `Task ${taskId} state: coder_delegated. Delegate to reviewer first. ` + `If this is stale state from a prior session, run /swarm reset-session to clear workflow state.`);
43491
43638
  }
43639
+ if (parallelModeActive) {
43640
+ let coderDelegatedCount = 0;
43641
+ for (const s of session.taskWorkflowStates.values()) {
43642
+ if (s === "coder_delegated")
43643
+ coderDelegatedCount++;
43644
+ }
43645
+ if (coderDelegatedCount >= effectiveMaxConcurrent) {
43646
+ throw new Error(`PARALLEL_SLOTS_EXHAUSTED: ${coderDelegatedCount} coder task(s) are awaiting review ` + `(max_concurrent_tasks=${effectiveMaxConcurrent}). Dispatch reviewer/test_engineer for an ` + `in-flight coder task before starting another coder.`);
43647
+ }
43648
+ }
43492
43649
  if (standardWorktreeSerializationSessions.has(input.sessionID)) {
43493
43650
  throw new Error("STANDARD_WORKTREE_ISOLATION_SERIALIZED: prior standard worktree isolation setup failed in this session; wait for the active coder task to finish before dispatching another coder.");
43494
43651
  }
43495
- const plan = await _internals20.loadPlanJsonOnly(directory);
43496
43652
  if (!plan)
43497
43653
  return;
43498
- const profile = plan.execution_profile;
43499
- const parallelEnabled = profile?.parallelization_enabled === true;
43500
- const maxConcurrent = profile?.max_concurrent_tasks ?? 10;
43501
- const effectiveMaxConcurrent = session.maxConcurrencyOverride ?? maxConcurrent;
43502
- if (!parallelEnabled || effectiveMaxConcurrent <= 1)
43654
+ if (!parallelModeActive)
43503
43655
  return;
43504
- if (hasActiveLeanTurbo(input.sessionID))
43505
- return;
43506
- const planTaskIds = new Set(plan.phases.flatMap((phase) => phase.tasks.map((task) => task.id)));
43507
- const resolvedTaskId = resolveDelegatedPlanTaskId(args2, planTaskIds);
43656
+ const resolvedTaskId = incomingCoderTaskId;
43508
43657
  await precreateStandardWorktreeSession({
43509
43658
  config: config2,
43510
43659
  directory,
@@ -67829,6 +67978,35 @@ function buildV3EnrichmentPrompt(lesson, category, tags) {
67829
67978
  ].join(`
67830
67979
  `);
67831
67980
  }
67981
+ function buildV3BatchEnrichmentPrompt(lessons) {
67982
+ const lessonLines = lessons.map((item, idx) => `${idx + 1}. LESSON: ${item.lesson}
67983
+ CATEGORY: ${item.category}
67984
+ TAGS: ${item.tags.join(", ")}`).join(`
67985
+ `);
67986
+ return [
67987
+ "Convert each prose lesson below into an actionable knowledge directive.",
67988
+ "Output ONLY a JSON array (no code fences, no commentary).",
67989
+ `The array length MUST be exactly ${lessons.length}.`,
67990
+ "Each array element at position i maps to lesson i (1-indexed above).",
67991
+ "",
67992
+ "For EACH element, mandatory requirements:",
67993
+ "- At least ONE scope field non-empty:",
67994
+ ' "applies_to_agents": string[] — roles from: architect, coder, reviewer, test_engineer, sme, docs, designer, critic, curator',
67995
+ ' "applies_to_tools": string[] — tool names from: edit, write, patch, bash, read, grep, glob',
67996
+ "- At least ONE predicate field non-empty:",
67997
+ ' "forbidden_actions": string[] | "required_actions": string[] | "verification_checks": string[]',
67998
+ "",
67999
+ "Optional per element:",
68000
+ '"triggers": string[], "directive_priority": "low" | "medium" | "high" | "critical"',
68001
+ "",
68002
+ "Example array:",
68003
+ '[{"applies_to_agents":["coder"],"required_actions":["run focused tests before commit"],"directive_priority":"high"}]',
68004
+ "",
68005
+ "LESSONS:",
68006
+ lessonLines
68007
+ ].join(`
68008
+ `);
68009
+ }
67832
68010
  function parseV3EnrichmentResponse(text) {
67833
68011
  if (!text || typeof text !== "string") {
67834
68012
  return { missing: ["valid JSON object"] };
@@ -67869,6 +68047,109 @@ function parseV3EnrichmentResponse(text) {
67869
68047
  }
67870
68048
  return { fields };
67871
68049
  }
68050
+ function parseV3BatchEnrichmentResponse(text, expectedLength) {
68051
+ const empty = Array.from({ length: expectedLength }, () => null);
68052
+ if (!text || typeof text !== "string") {
68053
+ return { fields: empty, missing: ["valid JSON array"] };
68054
+ }
68055
+ const start2 = text.indexOf("[");
68056
+ const end = text.lastIndexOf("]");
68057
+ if (start2 < 0 || end <= start2) {
68058
+ return { fields: empty, missing: ["valid JSON array"] };
68059
+ }
68060
+ let parsed;
68061
+ try {
68062
+ parsed = JSON.parse(text.slice(start2, end + 1));
68063
+ } catch {
68064
+ return { fields: empty, missing: ["valid JSON array"] };
68065
+ }
68066
+ if (!Array.isArray(parsed)) {
68067
+ return { fields: empty, missing: ["valid JSON array"] };
68068
+ }
68069
+ const fields = Array.from({ length: expectedLength }, () => null);
68070
+ const missing = [];
68071
+ for (let i2 = 0;i2 < expectedLength; i2++) {
68072
+ const item = parsed[i2];
68073
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
68074
+ missing.push(`item ${i2 + 1}: valid JSON object`);
68075
+ continue;
68076
+ }
68077
+ const raw = item;
68078
+ const candidate = {};
68079
+ for (const key of ENRICHMENT_ALLOWED_FIELDS) {
68080
+ if (raw[key] !== undefined) {
68081
+ candidate[key] = raw[key];
68082
+ }
68083
+ }
68084
+ const shape = validateActionableFields(candidate);
68085
+ if (!shape.valid) {
68086
+ missing.push(`item ${i2 + 1}: ${shape.errors.join("; ")}`);
68087
+ continue;
68088
+ }
68089
+ const actionability = validateActionability(candidate);
68090
+ if (!actionability.actionable) {
68091
+ const expected = [];
68092
+ if (actionability.reason === "missing_predicate" || actionability.reason === "missing_predicate_and_scope") {
68093
+ expected.push("a non-empty predicate field (forbidden_actions, required_actions, or verification_checks)");
68094
+ }
68095
+ if (actionability.reason === "missing_scope" || actionability.reason === "missing_predicate_and_scope") {
68096
+ expected.push("a non-empty scope field (applies_to_agents or applies_to_tools)");
68097
+ }
68098
+ missing.push(`item ${i2 + 1}: ${expected.join("; ")}`);
68099
+ continue;
68100
+ }
68101
+ fields[i2] = candidate;
68102
+ }
68103
+ if (parsed.length < expectedLength) {
68104
+ missing.push(`expected ${expectedLength} items but got ${parsed.length}`);
68105
+ } else if (parsed.length > expectedLength) {
68106
+ warn(`[knowledge-curator] parseV3BatchEnrichmentResponse received ${parsed.length} items but expected ${expectedLength}; extras will be discarded`);
68107
+ missing.push(`got ${parsed.length} items but only first ${expectedLength} will be used; extras discarded`);
68108
+ }
68109
+ return { fields, missing };
68110
+ }
68111
+ async function enrichLessonsToV3Batched(params) {
68112
+ const quota = params.quota ?? { maxCalls: 10, window: "utc" };
68113
+ const out2 = Array.from({ length: params.lessons.length }, () => null);
68114
+ const batchSize = params.batchSize ?? ENRICHMENT_BATCH_SIZE;
68115
+ for (let start2 = 0;start2 < params.lessons.length; start2 += batchSize) {
68116
+ const batch = params.lessons.slice(start2, start2 + batchSize);
68117
+ const prompt = buildV3BatchEnrichmentPrompt(batch);
68118
+ let userInput = prompt;
68119
+ let best = Array.from({ length: batch.length }, () => null);
68120
+ let retryHint = "";
68121
+ for (let attempt = 0;attempt < 2; attempt++) {
68122
+ try {
68123
+ const reservation = await reserveQuota(params.directory, {
68124
+ nCalls: 1,
68125
+ maxCalls: quota.maxCalls,
68126
+ window: quota.window,
68127
+ scope: "knowledge-enrichment"
68128
+ });
68129
+ if (!reservation.allowed)
68130
+ break;
68131
+ const response = await params.llmDelegate("", userInput, AbortSignal.timeout(ENRICHMENT_LLM_TIMEOUT_MS));
68132
+ const parsed = parseV3BatchEnrichmentResponse(response, batch.length);
68133
+ best = best.map((current, idx) => current ?? parsed.fields[idx]);
68134
+ const unresolved = best.map((fields, idx) => ({ fields, idx })).filter((item) => item.fields === null).map((item) => item.idx + 1);
68135
+ if (unresolved.length === 0)
68136
+ break;
68137
+ retryHint = parsed.missing.join("; ");
68138
+ const resolvedList = best.map((fields, idx) => ({ fields, idx })).filter((item) => item.fields !== null).map((item) => item.idx + 1);
68139
+ const preserveClause = resolvedList.length > 0 ? `Preserve the already-valid entries for items ${resolvedList.join(", ")} exactly as you returned them previously. ` : "";
68140
+ userInput = `${prompt}
68141
+
68142
+ RETRY: your last output still missed valid directives for items ${unresolved.join(", ")}. ${retryHint} ${preserveClause}Return a full JSON array with valid entries for every item.`;
68143
+ } catch (err2) {
68144
+ warn(`[knowledge-curator] v3 batch enrichment attempt ${attempt + 1} failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
68145
+ }
68146
+ }
68147
+ for (let i2 = 0;i2 < best.length; i2++) {
68148
+ out2[start2 + i2] = best[i2];
68149
+ }
68150
+ }
68151
+ return out2;
68152
+ }
67872
68153
  async function enrichLessonToV3(params) {
67873
68154
  const quota = params.quota ?? { maxCalls: 10, window: "utc" };
67874
68155
  const prompt = buildV3EnrichmentPrompt(params.lesson, params.category, params.tags);
@@ -68006,6 +68287,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
68006
68287
  const snapshotPlusNew = [...snapshot];
68007
68288
  const toAdd = [];
68008
68289
  const pendingReinforcementIds = new Set;
68290
+ const pendingBatchEnrichment = [];
68009
68291
  for (const lesson of lessons) {
68010
68292
  const tags = inferTags(lesson);
68011
68293
  let category = "process";
@@ -68068,20 +68350,10 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
68068
68350
  project_name: projectName,
68069
68351
  auto_generated: true
68070
68352
  };
68071
- let actionability = validateActionability(entry);
68353
+ const actionability = validateActionability(entry);
68072
68354
  if (!actionability.actionable && options?.llmDelegate) {
68073
- const enriched = await enrichLessonToV3({
68074
- directory,
68075
- llmDelegate: options.llmDelegate,
68076
- lesson,
68077
- category,
68078
- tags,
68079
- quota: options.enrichmentQuota
68080
- });
68081
- if (enriched) {
68082
- Object.assign(entry, enriched);
68083
- actionability = validateActionability(entry);
68084
- }
68355
+ pendingBatchEnrichment.push({ entry, lesson, category, tags });
68356
+ continue;
68085
68357
  }
68086
68358
  if (!actionability.actionable) {
68087
68359
  quarantined++;
@@ -68098,6 +68370,41 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
68098
68370
  toAdd.push(entry);
68099
68371
  snapshotPlusNew.push(entry);
68100
68372
  }
68373
+ if (pendingBatchEnrichment.length > 0 && options?.llmDelegate) {
68374
+ const enrichedBatch = await enrichLessonsToV3Batched({
68375
+ directory,
68376
+ llmDelegate: options.llmDelegate,
68377
+ lessons: pendingBatchEnrichment.map((item) => ({
68378
+ lesson: item.lesson,
68379
+ category: item.category,
68380
+ tags: item.tags
68381
+ })),
68382
+ quota: options.enrichmentQuota,
68383
+ batchSize: config3.enrichment?.batch_size
68384
+ });
68385
+ for (let i2 = 0;i2 < pendingBatchEnrichment.length; i2++) {
68386
+ const pending = pendingBatchEnrichment[i2];
68387
+ const enriched = enrichedBatch[i2];
68388
+ if (enriched) {
68389
+ Object.assign(pending.entry, enriched);
68390
+ }
68391
+ const actionability = validateActionability(pending.entry);
68392
+ if (!actionability.actionable) {
68393
+ quarantined++;
68394
+ try {
68395
+ await appendUnactionable(directory, pending.entry, actionability.reason ?? "unactionable");
68396
+ } catch {}
68397
+ await appendCuratorSkippedEvent(directory, {
68398
+ entry_id: pending.entry.id,
68399
+ lesson: pending.lesson,
68400
+ reason: actionability.reason ?? "unactionable"
68401
+ });
68402
+ continue;
68403
+ }
68404
+ toAdd.push(pending.entry);
68405
+ snapshotPlusNew.push(pending.entry);
68406
+ }
68407
+ }
68101
68408
  try {
68102
68409
  const insights = await consumeInsightCandidates(directory);
68103
68410
  for (const cand of insights) {
@@ -68301,7 +68608,7 @@ function createKnowledgeCuratorHook(directory, config3, options = {}) {
68301
68608
  };
68302
68609
  return safeHook(handler);
68303
68610
  }
68304
- var seenRetroSections, MAX_TRACKED_RETRO_SECTIONS = 500, ENRICHMENT_ALLOWED_FIELDS, ENRICHMENT_LLM_TIMEOUT_MS = 60000, MESO_INSIGHT_BATCH_LIMIT = 20, KNOWLEDGE_CATEGORIES, OUTCOME_PROMOTION_BLOCK = -0.3, _internals31;
68611
+ var seenRetroSections, MAX_TRACKED_RETRO_SECTIONS = 500, ENRICHMENT_ALLOWED_FIELDS, ENRICHMENT_LLM_TIMEOUT_MS = 60000, ENRICHMENT_BATCH_SIZE = 6, MESO_INSIGHT_BATCH_LIMIT = 20, KNOWLEDGE_CATEGORIES, OUTCOME_PROMOTION_BLOCK = -0.3, _internals31;
68305
68612
  var init_knowledge_curator = __esm(() => {
68306
68613
  init_skill_improver_quota();
68307
68614
  init_synonym_map();
@@ -95301,7 +95608,7 @@ Present all three items together in a single message. One message, defaults pre-
95301
95608
 
95302
95609
  **1. QA Gates** — accept defaults or customize (the eleven gates listed above).
95303
95610
 
95304
- **2. Parallel Coders** — "How many coders should run in parallel? (default: 1, range: 1-4)"
95611
+ **2. Parallel Coders** — Parallel coders each run in their own isolated git worktree (a separate working directory on its own branch); each coder's work is committed and merged back to the main tree automatically when it finishes, so concurrent coders never overwrite each other's files. This is safe and faster — but only for tasks whose declared file scopes do NOT overlap. Before you ask, INSPECT the plan's tasks: group the dependency-ready tasks whose file scopes are disjoint, and let your RECOMMENDED count be the number of such independent groups, clamped to the 1-4 range. If task scopes overlap or you cannot determine them, recommend 1 (serial). File-scope disjointness is your recommendation to make, not a runtime-enforced guarantee: if overlapping tasks run in parallel a merge conflict will preserve the work in its worktree and surface an advisory, but it stalls progress — so prefer serial whenever you are unsure. Ask: "How many coders should run in parallel? (default: 1, range: 1-4; my recommendation: <N>, because <independent task groups>)"
95305
95612
 
95306
95613
  **3. Commit Frequency** — "Commit frequency for completed tasks? (default: phase-level only; optional per-task checkpoint commit after each task completion)"
95307
95614
 
@@ -95313,7 +95620,7 @@ Wait for the user to answer all three in a single reply. Then apply:
95313
95620
  ## Pending Parallelization Config
95314
95621
  - parallelization_enabled: true
95315
95622
  - max_concurrent_tasks: <user's number>
95316
- - council_parallel: true
95623
+ - council_parallel: false
95317
95624
  - locked: true
95318
95625
  - recorded_at: <ISO timestamp>
95319
95626
  \`\`\`
@@ -95650,8 +95957,8 @@ If a tool modifies a file, it is a CODER tool. Delegate.
95650
95957
  - Rationale: declare_scope persists the allowed set to disk (.swarm/scopes/scope-\${taskId}.json) so it survives cross-process delegation. Without a call, the coder or test_engineer process reads an empty scope and every Edit/Write is denied.
95651
95958
  <!-- BEHAVIORAL_GUIDANCE_END -->
95652
95959
  2. ONE agent per message. Send, STOP, wait for response.
95653
- Exception: Stage B reviewer/test_engineer gate agents for the SAME completed coder task may be dispatched together before waiting when both gates are required.
95654
- This exception NEVER applies to coder delegations. Preserve ONE task per coder call.
95960
+ Exception: Stage B reviewer/test_engineer gate agents for the SAME completed coder task may be dispatched together before waiting when both gates are required. This exception NEVER applies to coder delegations. Preserve ONE task per coder call.
95961
+ Separate parallel-mode exception (distinct from the Stage B exception above, and the ONLY case where more than one coder may be dispatched before waiting): when an active \`[PARALLEL EXECUTION PROFILE]\` directive is present in your context (parallelization_enabled=true), you MAY dispatch multiple {{AGENT_PREFIX}}coder agents in a single message — up to the stated max_concurrent_tasks — but ONLY for distinct, dependency-ready tasks whose declared file scopes do NOT overlap. Each coder still requires its own \`declare_scope\` call and carries exactly ONE task (Rule 3 still holds: never batch multiple objectives into one coder). Parallel coders each run in an isolated git worktree, so their writes never collide and are merged back automatically. If no \`[PARALLEL EXECUTION PROFILE]\` directive is present, dispatch coders one at a time.
95655
95962
  3. ONE task per {{AGENT_PREFIX}}coder call. Never batch.
95656
95963
  3a. PRE-DELEGATION SCOPE CALL (required): BEFORE every {{AGENT_PREFIX}}coder delegation, you MUST call \`declare_scope\` with { taskId, files } listing the exact file(s) this task will modify (including generated/lockfile paths). No \`declare_scope\` call → no coder delegation. See Rule 1a.
95657
95964
  3b. PRE-DELEGATION SCOPE CALL (test_engineer): BEFORE any {{AGENT_PREFIX}}test_engineer delegation that will CREATE or MODIFY test files, you MUST call \`declare_scope\` with { taskId, files } listing the exact test file path(s) to write. Omitting this call leaves the write scope undeclared and will block the write. See Rule 1a.
@@ -96582,6 +96889,7 @@ RULES:
96582
96889
  - Respect CONSTRAINT
96583
96890
  - No web searches or documentation lookups — but DO use the search tool for cross-codebase pattern lookup before using any function
96584
96891
  - Verify all import paths exist before using them
96892
+ - WORKTREE ISOLATION: when the orchestrator runs coders in parallel, you may be working inside an isolated git worktree — a separate working directory on its own branch. Work exactly as normal: read and edit files at the paths you are given. Your changes are committed and merged back to the main tree automatically when you finish. If a merge conflict arises during merge-back, your work is preserved in its worktree and an advisory is surfaced to the orchestrator — your changes are never lost. Do NOT run git worktree/branch/checkout/merge commands yourself, and do NOT switch directories. Stay strictly within your declared FILE scope so coders working in sibling worktrees never collide with you.
96585
96893
 
96586
96894
  ## ANTI-HALLUCINATION PROTOCOL (MANDATORY)
96587
96895
  Before importing ANY function, type, or class from an existing project module:
@@ -106830,6 +107138,7 @@ init_worker();
106830
107138
  init_logger();
106831
107139
  init_pending_delegations();
106832
107140
  init_task_envelope();
107141
+ import { createHash as createHash11 } from "node:crypto";
106833
107142
  function createBackgroundCompletionObserver(opts) {
106834
107143
  const { config: config3, directory } = opts;
106835
107144
  const event = async (input) => {
@@ -106854,16 +107163,37 @@ function createBackgroundCompletionObserver(opts) {
106854
107163
  const pending = findByCorrelationId(directory, envelope.sessionId);
106855
107164
  const parentSessionId = typeof part.sessionID === "string" ? part.sessionID : "unknown";
106856
107165
  if (!pending) {
106857
- log(`[background] observed synthetic completion (state=${envelope.state}) for subagent ${envelope.sessionId} in parent ${parentSessionId} with NO matching pending record — ignored (Stage A observe-only)`);
107166
+ log(`[background] observed synthetic completion (state=${envelope.state}) for subagent ${envelope.sessionId} in parent ${parentSessionId} with NO matching pending record — ignored`);
107167
+ return;
107168
+ }
107169
+ if (pending.parentSessionId !== parentSessionId) {
107170
+ warn(`[background] observed synthetic completion for ${envelope.sessionId} with parent mismatch: expected=${pending.parentSessionId} observed=${parentSessionId}; ignored`);
107171
+ return;
107172
+ }
107173
+ if (pending.status !== "pending" && pending.status !== "running") {
107174
+ log(`[background] observed duplicate/late completion for ${envelope.sessionId}; current status=${pending.status}; ignored`);
106858
107175
  return;
106859
107176
  }
106860
- log(`[background] observed trusted completion (state=${envelope.state}) correlated to pending delegation: ` + `agent=${pending.normalizedAgent} task=${pending.evidenceTaskId ?? pending.planTaskId ?? "unknown"} ` + `parent=${pending.parentSessionId} observedParent=${parentSessionId} pendingStatus=${pending.status} ` + "(Stage A observe-only — no gate effect; Stage B will ingest completion)");
107177
+ const text = envelope.state === "error" ? envelope.errorText ?? "" : envelope.resultText ?? "";
107178
+ await appendDelegationTransition(directory, envelope.sessionId, {
107179
+ status: envelope.state === "error" ? "error" : "completed",
107180
+ result: {
107181
+ ...envelope.state === "error" ? { error: text } : { text },
107182
+ chars: envelope.resultChars ?? text.length,
107183
+ truncated: envelope.resultTruncated ?? false,
107184
+ digest: digest(text)
107185
+ }
107186
+ });
107187
+ log(`[background] observed trusted completion (state=${envelope.state}) correlated to pending delegation: ` + `agent=${pending.normalizedAgent} task=${pending.evidenceTaskId ?? pending.planTaskId ?? "unknown"} ` + `parent=${pending.parentSessionId} observedParent=${parentSessionId} pendingStatus=${pending.status} ` + "(advisory ledger update only — no gate effect)");
106861
107188
  } catch (err2) {
106862
107189
  warn(`[background] completion observer error: ${err2 instanceof Error ? err2.message : String(err2)}`);
106863
107190
  }
106864
107191
  };
106865
107192
  return { event };
106866
107193
  }
107194
+ function digest(text) {
107195
+ return createHash11("sha256").update(text).digest("hex");
107196
+ }
106867
107197
 
106868
107198
  // src/index.ts
106869
107199
  init_pr_subscriptions();
@@ -117074,7 +117404,7 @@ init_knowledge_events();
117074
117404
  // src/hooks/knowledge-injector.ts
117075
117405
  init_schema();
117076
117406
  init_manager();
117077
- import { createHash as createHash14 } from "node:crypto";
117407
+ import { createHash as createHash15 } from "node:crypto";
117078
117408
 
117079
117409
  // src/services/run-memory.ts
117080
117410
  import * as crypto10 from "node:crypto";
@@ -117578,7 +117908,7 @@ function createKnowledgeInjectorHook(directory, config3, modelLimitOverrides = {
117578
117908
  ctx.taskId ?? "",
117579
117909
  (ctx.filePaths ?? []).slice(0, 8).join(",")
117580
117910
  ].join("|");
117581
- return createHash14("sha1").update(parts2).digest("hex").slice(0, 16);
117911
+ return createHash15("sha1").update(parts2).digest("hex").slice(0, 16);
117582
117912
  }
117583
117913
  let lastSeenCacheKey = null;
117584
117914
  let cachedInjectionText = null;
@@ -125574,6 +125904,9 @@ var diff_summary = createSwarmTool({
125574
125904
  }
125575
125905
  });
125576
125906
 
125907
+ // src/tools/dispatch-lanes.ts
125908
+ import { createHash as createHash16 } from "node:crypto";
125909
+
125577
125910
  // node_modules/yocto-queue/index.js
125578
125911
  class Node2 {
125579
125912
  value;
@@ -125732,6 +126065,7 @@ function validateConcurrency(concurrency) {
125732
126065
 
125733
126066
  // src/tools/dispatch-lanes.ts
125734
126067
  init_zod();
126068
+ init_pending_delegations();
125735
126069
  init_constants();
125736
126070
  init_schema();
125737
126071
 
@@ -125796,6 +126130,12 @@ var MAX_TIMEOUT_MS2 = 1800000;
125796
126130
  var MAX_LANE_OUTPUT_CHARS = 20000;
125797
126131
  var MAX_ERROR_CHARS = 200;
125798
126132
  var ERROR_TRUNCATION_SUFFIX = "...";
126133
+ var MAX_BATCH_ID_CHARS = 120;
126134
+ var DEFAULT_ASYNC_STALE_TIMEOUT_MS = 30 * 60000;
126135
+ var DEFAULT_COLLECT_TIMEOUT_MS = DEFAULT_ASYNC_STALE_TIMEOUT_MS;
126136
+ var MAX_COLLECT_TIMEOUT_MS = 60 * 60000;
126137
+ var COLLECT_POLL_INTERVAL_MS = 500;
126138
+ var MAX_COLLECT_POLL_INTERVAL_MS = 1e4;
125799
126139
  var AGENT_NAME_SEPARATORS = ["_", "-", " "];
125800
126140
  var READ_ONLY_LANE_ROLES = new Set([
125801
126141
  "explorer",
@@ -125847,11 +126187,25 @@ var DispatchLanesArgsSchema = exports_external.object({
125847
126187
  max_concurrent: exports_external.number().int().min(1).max(MAX_LANES).optional().describe("Maximum lanes in flight at once; defaults to lane count"),
125848
126188
  timeout_ms: exports_external.number().int().min(10).max(MAX_TIMEOUT_MS2).optional().describe("Per-lane session create/prompt timeout in milliseconds")
125849
126189
  });
126190
+ var DispatchLanesAsyncArgsSchema = DispatchLanesArgsSchema.extend({
126191
+ batch_id: exports_external.string().min(1).max(MAX_BATCH_ID_CHARS).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/).optional().describe("Stable async batch id for later collection; generated when omitted"),
126192
+ mode: exports_external.string().min(1).max(80).optional().describe("Advisory workflow mode, such as deep-dive or swarm-pr-review"),
126193
+ pr_head_sha: exports_external.string().min(1).max(80).optional(),
126194
+ scope: exports_external.string().min(1).max(500).optional()
126195
+ });
126196
+ var CollectLaneResultsArgsSchema = exports_external.object({
126197
+ batch_id: exports_external.string().min(1).max(MAX_BATCH_ID_CHARS),
126198
+ wait: exports_external.boolean().optional().describe("Poll until all lanes settle or timeout"),
126199
+ timeout_ms: exports_external.number().int().min(0).max(MAX_COLLECT_TIMEOUT_MS).optional().describe("Total wait budget when wait=true"),
126200
+ include_pending: exports_external.boolean().optional(),
126201
+ cancel_pending: exports_external.boolean().optional().describe("Abort and mark pending/running lanes cancelled")
126202
+ });
125850
126203
  var _internals81 = {
125851
126204
  getSessionOps: () => swarmState.opencodeClient?.session ?? null,
125852
126205
  getGeneratedAgentNames: () => swarmState.generatedAgentNames,
125853
126206
  createParallelDispatcher,
125854
- now: () => Date.now()
126207
+ now: () => Date.now(),
126208
+ sleep: sleep2
125855
126209
  };
125856
126210
  async function executeDispatchLanes(args2, directory, context = {}) {
125857
126211
  const parsed = DispatchLanesArgsSchema.safeParse(args2);
@@ -125893,6 +126247,322 @@ async function executeDispatchLanes(args2, directory, context = {}) {
125893
126247
  dispatcher.shutdown();
125894
126248
  }
125895
126249
  }
126250
+ async function executeDispatchLanesAsync(args2, directory, context = {}) {
126251
+ const parsed = DispatchLanesAsyncArgsSchema.safeParse(args2);
126252
+ if (!parsed.success) {
126253
+ return asyncFailureResult({
126254
+ failure_class: "invalid_args",
126255
+ message: "Invalid dispatch_lanes_async arguments",
126256
+ errors: parsed.error.issues.map((issue3) => `${issue3.path.join(".")}: ${issue3.message}`)
126257
+ });
126258
+ }
126259
+ const duplicateLaneIds = findDuplicateLaneIds(parsed.data.lanes);
126260
+ if (duplicateLaneIds.length > 0) {
126261
+ return asyncFailureResult({
126262
+ failure_class: "invalid_args",
126263
+ message: "Lane IDs must be unique within one dispatch_lanes_async batch",
126264
+ errors: duplicateLaneIds.map((id) => `Duplicate lane id: ${id}`)
126265
+ });
126266
+ }
126267
+ const session = _internals81.getSessionOps();
126268
+ if (!session || typeof session.promptAsync !== "function") {
126269
+ return asyncFailureResult({
126270
+ failure_class: "no_client",
126271
+ message: "OpenCode session promptAsync client is not available"
126272
+ });
126273
+ }
126274
+ const lanes = parsed.data.lanes;
126275
+ const batchId = parsed.data.batch_id ?? makeBatchId();
126276
+ if (findByBatchId(directory, batchId).length > 0) {
126277
+ return asyncFailureResult({
126278
+ failure_class: "invalid_args",
126279
+ message: `Async lane batch already exists: ${batchId}`,
126280
+ errors: [`batch_id must be unique: ${batchId}`]
126281
+ });
126282
+ }
126283
+ const maxConcurrent = Math.min(parsed.data.max_concurrent ?? lanes.length, lanes.length, MAX_LANES);
126284
+ const timeoutMs = parsed.data.timeout_ms ?? DEFAULT_TIMEOUT_MS3;
126285
+ const dispatcher = _internals81.createParallelDispatcher({
126286
+ enabled: true,
126287
+ maxConcurrentTasks: maxConcurrent,
126288
+ evidenceLockTimeoutMs: 0
126289
+ });
126290
+ const limit = pLimit(maxConcurrent);
126291
+ try {
126292
+ const laneResults = await Promise.all(lanes.map((lane) => limit(() => launchAsyncLane({
126293
+ session,
126294
+ dispatcher,
126295
+ lane,
126296
+ directory,
126297
+ timeoutMs,
126298
+ context,
126299
+ batchId,
126300
+ mode: parsed.data.mode,
126301
+ prHeadSha: parsed.data.pr_head_sha,
126302
+ scope: parsed.data.scope
126303
+ }))));
126304
+ const failed = laneResults.filter((lane) => lane.status === "failed");
126305
+ const rejected = laneResults.filter((lane) => lane.status === "rejected");
126306
+ const pending = laneResults.filter((lane) => lane.status === "pending");
126307
+ return {
126308
+ success: failed.length === 0 && rejected.length === 0,
126309
+ batch_id: batchId,
126310
+ dispatched: laneResults.length,
126311
+ pending: pending.length,
126312
+ failed: failed.length,
126313
+ rejected: rejected.length,
126314
+ max_concurrent: maxConcurrent,
126315
+ timeout_ms: timeoutMs,
126316
+ lane_results: laneResults
126317
+ };
126318
+ } finally {
126319
+ dispatcher.shutdown();
126320
+ }
126321
+ }
126322
+ async function executeCollectLaneResults(args2, directory, context = {}) {
126323
+ const parsed = CollectLaneResultsArgsSchema.safeParse(args2);
126324
+ if (!parsed.success) {
126325
+ return collectFailureResult({
126326
+ failure_class: "invalid_args",
126327
+ batch_id: "",
126328
+ message: "Invalid collect_lane_results arguments",
126329
+ errors: parsed.error.issues.map((issue3) => `${issue3.path.join(".")}: ${issue3.message}`)
126330
+ });
126331
+ }
126332
+ const session = _internals81.getSessionOps();
126333
+ if (!session || typeof session.messages !== "function") {
126334
+ return collectFailureResult({
126335
+ failure_class: "no_client",
126336
+ batch_id: parsed.data.batch_id,
126337
+ message: "OpenCode session messages client is not available"
126338
+ });
126339
+ }
126340
+ const timeoutMs = parsed.data.timeout_ms ?? DEFAULT_COLLECT_TIMEOUT_MS;
126341
+ const deadline = _internals81.now() + timeoutMs;
126342
+ const batchFilter = context.sessionID !== undefined ? { parentSessionId: context.sessionID } : undefined;
126343
+ await sweepStaleDelegations(directory, DEFAULT_ASYNC_STALE_TIMEOUT_MS);
126344
+ let records = findByBatchId(directory, parsed.data.batch_id, batchFilter);
126345
+ if (records.length === 0) {
126346
+ return collectFailureResult({
126347
+ failure_class: "not_found",
126348
+ batch_id: parsed.data.batch_id,
126349
+ message: `No async lane batch found for ${parsed.data.batch_id}`
126350
+ });
126351
+ }
126352
+ let keepPolling = true;
126353
+ let pollIntervalMs = COLLECT_POLL_INTERVAL_MS;
126354
+ while (keepPolling) {
126355
+ await collectOnce(session, directory, records, parsed.data.cancel_pending === true);
126356
+ await sweepStaleDelegations(directory, DEFAULT_ASYNC_STALE_TIMEOUT_MS);
126357
+ records = findByBatchId(directory, parsed.data.batch_id, batchFilter);
126358
+ if (allSettled(records) || parsed.data.wait !== true) {
126359
+ keepPolling = false;
126360
+ continue;
126361
+ }
126362
+ if (_internals81.now() >= deadline) {
126363
+ keepPolling = false;
126364
+ continue;
126365
+ }
126366
+ await _internals81.sleep(Math.min(pollIntervalMs, Math.max(0, deadline - _internals81.now())));
126367
+ pollIntervalMs = nextCollectPollInterval(pollIntervalMs);
126368
+ }
126369
+ return buildCollectResult(parsed.data.batch_id, records, parsed.data.include_pending === true);
126370
+ }
126371
+ async function launchAsyncLane(args2) {
126372
+ const validation2 = validateLaneAgent(args2.lane.agent, args2.context);
126373
+ const role = validation2.role;
126374
+ const startedAt = isoNow();
126375
+ if (!validation2.ok) {
126376
+ return {
126377
+ id: args2.lane.id,
126378
+ agent: args2.lane.agent,
126379
+ role,
126380
+ status: "rejected",
126381
+ started_at: startedAt,
126382
+ completed_at: isoNow(),
126383
+ error: validation2.error
126384
+ };
126385
+ }
126386
+ const decision = args2.dispatcher.dispatch(args2.lane.id);
126387
+ if (decision.action !== "dispatch") {
126388
+ return {
126389
+ id: args2.lane.id,
126390
+ agent: args2.lane.agent,
126391
+ role,
126392
+ status: "failed",
126393
+ started_at: startedAt,
126394
+ completed_at: isoNow(),
126395
+ error: `dispatcher ${decision.action}: ${decision.reason}`
126396
+ };
126397
+ }
126398
+ try {
126399
+ const createTimeoutMessage = `Lane "${args2.lane.id}" session.create timed out after ${args2.timeoutMs}ms`;
126400
+ const createPromise = args2.session.create({
126401
+ query: { directory: args2.directory }
126402
+ });
126403
+ let createTimedOut = false;
126404
+ createPromise.then((createResult2) => {
126405
+ if (createTimedOut && createResult2.data?.id) {
126406
+ scheduleSessionCleanup(args2.session, createResult2.data.id);
126407
+ }
126408
+ }).catch(() => {
126409
+ return;
126410
+ });
126411
+ const createResult = await withTimeout2(createPromise, args2.timeoutMs, createTimeoutMessage).catch((error93) => {
126412
+ if (formatError3(error93) === createTimeoutMessage) {
126413
+ createTimedOut = true;
126414
+ }
126415
+ throw error93;
126416
+ });
126417
+ const sessionId = createResult.data?.id;
126418
+ if (!sessionId) {
126419
+ return failedLane(args2.lane, role, startedAt, `session.create failed: ${formatError3(createResult.error)}`, decision.slot.slotId, decision.slot.runId);
126420
+ }
126421
+ const pendingRecord = await recordPendingDelegation(args2.directory, {
126422
+ correlationId: sessionId,
126423
+ jobId: null,
126424
+ subagentSessionId: sessionId,
126425
+ parentSessionId: args2.context.sessionID ?? `dispatch_lanes_async:${args2.batchId}`,
126426
+ callID: args2.batchId,
126427
+ normalizedAgent: role,
126428
+ swarmPrefixedAgent: args2.lane.agent,
126429
+ planTaskId: null,
126430
+ evidenceTaskId: null,
126431
+ batchId: args2.batchId,
126432
+ laneId: args2.lane.id,
126433
+ mode: args2.mode ?? "advisory",
126434
+ promptHash: promptHash(args2.lane, args2.directory, args2.batchId),
126435
+ workspace: {
126436
+ directory: args2.directory,
126437
+ gitHead: null,
126438
+ dirtyHash: null,
126439
+ prHeadSha: args2.prHeadSha ?? null,
126440
+ scope: args2.scope ?? null
126441
+ },
126442
+ generation: 1
126443
+ }, { staleTimeoutMs: DEFAULT_ASYNC_STALE_TIMEOUT_MS });
126444
+ if (!pendingRecord) {
126445
+ cleanupAsyncLaunchSession(args2.session, sessionId);
126446
+ return failedLane(args2.lane, role, startedAt, "Failed to record async lane in background delegation ledger", decision.slot.slotId, decision.slot.runId);
126447
+ }
126448
+ const promptController = new AbortController;
126449
+ let promptResult;
126450
+ try {
126451
+ promptResult = await withTimeout2(args2.session.promptAsync({
126452
+ path: { id: sessionId },
126453
+ query: { directory: args2.directory },
126454
+ body: {
126455
+ agent: args2.lane.agent,
126456
+ tools: buildReadOnlyTools(),
126457
+ parts: [{ type: "text", text: args2.lane.prompt }]
126458
+ },
126459
+ signal: promptController.signal
126460
+ }), args2.timeoutMs, `Lane "${args2.lane.id}" session.promptAsync timed out after ${args2.timeoutMs}ms`, promptController);
126461
+ } catch (error93) {
126462
+ const message = formatError3(error93);
126463
+ await appendDelegationTransition(args2.directory, sessionId, {
126464
+ status: "error",
126465
+ result: {
126466
+ error: message,
126467
+ chars: message.length,
126468
+ truncated: false,
126469
+ digest: digestText(message)
126470
+ }
126471
+ });
126472
+ cleanupAsyncLaunchSession(args2.session, sessionId);
126473
+ return failedLane(args2.lane, role, startedAt, message, decision.slot.slotId, decision.slot.runId);
126474
+ }
126475
+ if (promptResult.error) {
126476
+ const error93 = `session.promptAsync failed: ${formatError3(promptResult.error)}`;
126477
+ await appendDelegationTransition(args2.directory, sessionId, {
126478
+ status: "error",
126479
+ result: {
126480
+ error: error93,
126481
+ chars: error93.length,
126482
+ truncated: false,
126483
+ digest: digestText(error93)
126484
+ }
126485
+ });
126486
+ cleanupAsyncLaunchSession(args2.session, sessionId);
126487
+ return failedLane(args2.lane, role, startedAt, error93, decision.slot.slotId, decision.slot.runId);
126488
+ }
126489
+ await appendDelegationTransition(args2.directory, sessionId, {
126490
+ status: "running"
126491
+ });
126492
+ return {
126493
+ id: args2.lane.id,
126494
+ agent: args2.lane.agent,
126495
+ role,
126496
+ status: "pending",
126497
+ session_id: sessionId,
126498
+ slot_id: decision.slot.slotId,
126499
+ run_id: decision.slot.runId,
126500
+ started_at: startedAt,
126501
+ completed_at: isoNow()
126502
+ };
126503
+ } catch (error93) {
126504
+ return failedLane(args2.lane, role, startedAt, formatError3(error93), decision.slot.slotId, decision.slot.runId);
126505
+ } finally {
126506
+ args2.dispatcher.releaseSlot(decision.slot.slotId);
126507
+ }
126508
+ }
126509
+ async function collectOnce(session, directory, records, cancelPending) {
126510
+ for (const record3 of records) {
126511
+ if (record3.status !== "pending" && record3.status !== "running")
126512
+ continue;
126513
+ if (cancelPending) {
126514
+ if (typeof session.abort === "function") {
126515
+ await session.abort({ path: { id: record3.subagentSessionId } }).catch(() => {
126516
+ return;
126517
+ });
126518
+ }
126519
+ await appendDelegationTransition(directory, record3.correlationId, {
126520
+ status: "cancelled"
126521
+ });
126522
+ continue;
126523
+ }
126524
+ let messages;
126525
+ try {
126526
+ messages = await session.messages({
126527
+ path: { id: record3.subagentSessionId },
126528
+ query: { directory, limit: 50 }
126529
+ });
126530
+ } catch {
126531
+ continue;
126532
+ }
126533
+ if (!messages.data)
126534
+ continue;
126535
+ const text = extractLastAssistantText(messages.data);
126536
+ if (!text)
126537
+ continue;
126538
+ const bounded = boundLaneOutput(text);
126539
+ await appendDelegationTransition(directory, record3.correlationId, {
126540
+ status: "completed",
126541
+ result: {
126542
+ text: bounded.output,
126543
+ chars: bounded.output_chars,
126544
+ truncated: bounded.output_truncated,
126545
+ digest: digestText(text)
126546
+ }
126547
+ });
126548
+ }
126549
+ }
126550
+ function extractLastAssistantText(messages) {
126551
+ for (let i2 = messages.length - 1;i2 >= 0; i2--) {
126552
+ const message = messages[i2];
126553
+ if (message.info?.role !== "assistant")
126554
+ continue;
126555
+ const text = extractText3(message.parts);
126556
+ if (text.trim().length > 0)
126557
+ return text;
126558
+ }
126559
+ return "";
126560
+ }
126561
+ function nextCollectPollInterval(currentMs) {
126562
+ if (currentMs <= 0)
126563
+ return COLLECT_POLL_INTERVAL_MS;
126564
+ return Math.min(currentMs * 2, MAX_COLLECT_POLL_INTERVAL_MS);
126565
+ }
125896
126566
  async function runLane(session, dispatcher, lane, directory, timeoutMs, context) {
125897
126567
  const validation2 = validateLaneAgent(lane.agent, context);
125898
126568
  const role = validation2.role;
@@ -125993,6 +126663,49 @@ function buildResult(laneResults, maxConcurrent, timeoutMs) {
125993
126663
  lane_results: laneResults
125994
126664
  };
125995
126665
  }
126666
+ function buildCollectResult(batchId, records, includePending) {
126667
+ const laneResults = records.filter((record3) => includePending || record3.status !== "pending" && record3.status !== "running").map(recordToLaneResult);
126668
+ const completed = records.filter((record3) => record3.status === "completed");
126669
+ const failed = records.filter((record3) => record3.status === "error");
126670
+ const cancelled = records.filter((record3) => record3.status === "cancelled");
126671
+ const stale = records.filter((record3) => record3.status === "stale");
126672
+ const pending = records.filter((record3) => record3.status === "pending" || record3.status === "running");
126673
+ const consumed = records.filter((record3) => record3.status === "consumed");
126674
+ return {
126675
+ success: pending.length === 0 && failed.length === 0 && cancelled.length === 0 && stale.length === 0,
126676
+ batch_id: batchId,
126677
+ total: records.length,
126678
+ completed: completed.length,
126679
+ failed: failed.length,
126680
+ cancelled: cancelled.length,
126681
+ stale: stale.length,
126682
+ pending: pending.length,
126683
+ consumed: consumed.length,
126684
+ all_settled: pending.length === 0,
126685
+ lane_results: laneResults
126686
+ };
126687
+ }
126688
+ function recordToLaneResult(record3) {
126689
+ const status = record3.status === "error" ? "failed" : record3.status === "running" ? "pending" : record3.status;
126690
+ return {
126691
+ id: record3.laneId ?? record3.correlationId,
126692
+ agent: record3.swarmPrefixedAgent,
126693
+ role: record3.normalizedAgent,
126694
+ status,
126695
+ session_id: record3.subagentSessionId,
126696
+ started_at: new Date(record3.createdAt).toISOString(),
126697
+ completed_at: new Date(record3.completedAt ?? record3.updatedAt).toISOString(),
126698
+ ...record3.result?.text !== undefined ? {
126699
+ output: record3.result.text,
126700
+ output_chars: record3.result.chars,
126701
+ output_truncated: record3.result.truncated
126702
+ } : {},
126703
+ ...record3.result?.error !== undefined ? { error: record3.result.error } : {}
126704
+ };
126705
+ }
126706
+ function allSettled(records) {
126707
+ return records.every((record3) => record3.status !== "pending" && record3.status !== "running");
126708
+ }
125996
126709
  function failedLane(lane, role, startedAt, error93, slotId, runId, sessionId) {
125997
126710
  return {
125998
126711
  id: lane.id,
@@ -126095,6 +126808,40 @@ function failureResult(args2) {
126095
126808
  errors: args2.errors
126096
126809
  };
126097
126810
  }
126811
+ function asyncFailureResult(args2) {
126812
+ return {
126813
+ success: false,
126814
+ failure_class: args2.failure_class,
126815
+ message: args2.message,
126816
+ batch_id: null,
126817
+ dispatched: 0,
126818
+ pending: 0,
126819
+ failed: 0,
126820
+ rejected: 0,
126821
+ max_concurrent: 0,
126822
+ timeout_ms: 0,
126823
+ lane_results: [],
126824
+ errors: args2.errors
126825
+ };
126826
+ }
126827
+ function collectFailureResult(args2) {
126828
+ return {
126829
+ success: false,
126830
+ failure_class: args2.failure_class,
126831
+ message: args2.message,
126832
+ batch_id: args2.batch_id,
126833
+ total: 0,
126834
+ completed: 0,
126835
+ failed: 0,
126836
+ cancelled: 0,
126837
+ stale: 0,
126838
+ pending: 0,
126839
+ consumed: 0,
126840
+ all_settled: false,
126841
+ lane_results: [],
126842
+ errors: args2.errors
126843
+ };
126844
+ }
126098
126845
  function findDuplicateLaneIds(lanes) {
126099
126846
  const seen = new Set;
126100
126847
  const duplicates = new Set;
@@ -126110,6 +126857,14 @@ function scheduleSessionCleanup(session, sessionId) {
126110
126857
  return;
126111
126858
  });
126112
126859
  }
126860
+ function cleanupAsyncLaunchSession(session, sessionId) {
126861
+ if (typeof session.abort === "function") {
126862
+ session.abort({ path: { id: sessionId } }).catch(() => {
126863
+ return;
126864
+ });
126865
+ }
126866
+ scheduleSessionCleanup(session, sessionId);
126867
+ }
126113
126868
  async function withTimeout2(promise3, timeoutMs, message, controller) {
126114
126869
  let timeout;
126115
126870
  try {
@@ -126120,9 +126875,6 @@ async function withTimeout2(promise3, timeoutMs, message, controller) {
126120
126875
  controller?.abort();
126121
126876
  reject(new Error(message));
126122
126877
  }, timeoutMs);
126123
- if (typeof timeout.unref === "function") {
126124
- timeout.unref();
126125
- }
126126
126878
  })
126127
126879
  ]);
126128
126880
  } finally {
@@ -126150,6 +126902,29 @@ function boundErrorString(text) {
126150
126902
  function isoNow() {
126151
126903
  return new Date(_internals81.now()).toISOString();
126152
126904
  }
126905
+ function makeBatchId() {
126906
+ return `lanes-${_internals81.now().toString(36)}`;
126907
+ }
126908
+ function promptHash(lane, directory, batchId) {
126909
+ return digestText(JSON.stringify({
126910
+ batchId,
126911
+ laneId: lane.id,
126912
+ agent: lane.agent,
126913
+ directory,
126914
+ prompt: lane.prompt.replace(/\r\n/g, `
126915
+ `)
126916
+ }));
126917
+ }
126918
+ function digestText(text) {
126919
+ return createHash16("sha256").update(text).digest("hex");
126920
+ }
126921
+ function sleep2(ms) {
126922
+ if (ms <= 0)
126923
+ return Promise.resolve();
126924
+ return new Promise((resolve57) => {
126925
+ setTimeout(resolve57, ms);
126926
+ });
126927
+ }
126153
126928
  var dispatch_lanes = createSwarmTool({
126154
126929
  description: "Dispatch multiple read-only exploration/review lanes concurrently through OpenCode sessions and return a structured join result.",
126155
126930
  args: {
@@ -126164,12 +126939,53 @@ var dispatch_lanes = createSwarmTool({
126164
126939
  return JSON.stringify(result, null, 2);
126165
126940
  }
126166
126941
  });
126942
+ var dispatch_lanes_async = createSwarmTool({
126943
+ description: "Launch multiple read-only advisory lanes with OpenCode promptAsync and return immediately with a batch id for collect_lane_results.",
126944
+ args: {
126945
+ lanes: DispatchLanesAsyncArgsSchema.shape.lanes,
126946
+ max_concurrent: DispatchLanesAsyncArgsSchema.shape.max_concurrent,
126947
+ timeout_ms: DispatchLanesAsyncArgsSchema.shape.timeout_ms,
126948
+ batch_id: DispatchLanesAsyncArgsSchema.shape.batch_id,
126949
+ mode: DispatchLanesAsyncArgsSchema.shape.mode,
126950
+ pr_head_sha: DispatchLanesAsyncArgsSchema.shape.pr_head_sha,
126951
+ scope: DispatchLanesAsyncArgsSchema.shape.scope
126952
+ },
126953
+ execute: async (args2, directory, ctx) => {
126954
+ const result = await executeDispatchLanesAsync(args2, directory, {
126955
+ callerAgent: getContextAgent(ctx),
126956
+ sessionID: getContextSessionID(ctx)
126957
+ });
126958
+ return JSON.stringify(result, null, 2);
126959
+ }
126960
+ });
126961
+ var collect_lane_results = createSwarmTool({
126962
+ description: "Collect or poll results for a dispatch_lanes_async batch; this is the required join barrier for advisory lane workflows and does not advance workflow gates.",
126963
+ args: {
126964
+ batch_id: CollectLaneResultsArgsSchema.shape.batch_id,
126965
+ wait: CollectLaneResultsArgsSchema.shape.wait,
126966
+ timeout_ms: CollectLaneResultsArgsSchema.shape.timeout_ms,
126967
+ include_pending: CollectLaneResultsArgsSchema.shape.include_pending,
126968
+ cancel_pending: CollectLaneResultsArgsSchema.shape.cancel_pending
126969
+ },
126970
+ execute: async (args2, directory, ctx) => {
126971
+ const result = await executeCollectLaneResults(args2, directory, {
126972
+ sessionID: getContextSessionID(ctx)
126973
+ });
126974
+ return JSON.stringify(result, null, 2);
126975
+ }
126976
+ });
126167
126977
  function getContextAgent(ctx) {
126168
126978
  if (!ctx || typeof ctx !== "object")
126169
126979
  return;
126170
126980
  const value = ctx.agent;
126171
126981
  return typeof value === "string" ? value : undefined;
126172
126982
  }
126983
+ function getContextSessionID(ctx) {
126984
+ if (!ctx || typeof ctx !== "object")
126985
+ return;
126986
+ const value = ctx.sessionID;
126987
+ return typeof value === "string" ? value : undefined;
126988
+ }
126173
126989
 
126174
126990
  // src/tools/manifest.ts
126175
126991
  init_doc_scan();
@@ -126850,11 +127666,11 @@ var external_skill_delete = createSwarmTool({
126850
127666
  // src/tools/external-skill-discover.ts
126851
127667
  init_zod();
126852
127668
  init_loader();
126853
- import { createHash as createHash16, randomUUID as randomUUID12 } from "node:crypto";
127669
+ import { createHash as createHash18, randomUUID as randomUUID12 } from "node:crypto";
126854
127670
 
126855
127671
  // src/services/external-skill-validator.ts
126856
127672
  init_knowledge_validator();
126857
- import { createHash as createHash15 } from "node:crypto";
127673
+ import { createHash as createHash17 } from "node:crypto";
126858
127674
  var PROMPT_INJECTION_PATTERNS = [
126859
127675
  {
126860
127676
  pattern: /system\s*:/i,
@@ -127389,7 +128205,7 @@ function evaluateCandidate(candidate, options) {
127389
128205
  }
127390
128206
  var _internals84 = {
127391
128207
  getTimestamp: () => new Date().toISOString(),
127392
- computeSha256: (content) => createHash15("sha256").update(content).digest("hex"),
128208
+ computeSha256: (content) => createHash17("sha256").update(content).digest("hex"),
127393
128209
  stripMarkdownCodeForUnsafeScan
127394
128210
  };
127395
128211
 
@@ -127411,7 +128227,7 @@ var _internals85 = {
127411
128227
  return { content, finalUrl: response.url };
127412
128228
  },
127413
128229
  getTimestamp: () => new Date().toISOString(),
127414
- computeSha256: (content) => createHash16("sha256").update(content).digest("hex"),
128230
+ computeSha256: (content) => createHash18("sha256").update(content).digest("hex"),
127415
128231
  uuid: () => randomUUID12()
127416
128232
  };
127417
128233
  var SOURCE_TRUST_LEVELS = {
@@ -127797,7 +128613,7 @@ var external_skill_list = createSwarmTool({
127797
128613
  // src/tools/external-skill-promote.ts
127798
128614
  init_zod();
127799
128615
  init_loader();
127800
- import { createHash as createHash17 } from "node:crypto";
128616
+ import { createHash as createHash19 } from "node:crypto";
127801
128617
  import * as fs95 from "node:fs/promises";
127802
128618
  import * as path149 from "node:path";
127803
128619
  init_create_tool();
@@ -127962,7 +128778,7 @@ var external_skill_promote = createSwarmTool({
127962
128778
  }
127963
128779
  throw writeErr;
127964
128780
  }
127965
- const promotedContentHash = createHash17("sha256").update(skillMarkdown).digest("hex");
128781
+ const promotedContentHash = createHash19("sha256").update(skillMarkdown).digest("hex");
127966
128782
  const prePromotionHistory = candidate.evaluation_history;
127967
128783
  const lastPrePromotionEntry = prePromotionHistory.length > 0 ? prePromotionHistory[prePromotionHistory.length - 1] : undefined;
127968
128784
  const originalEvaluation = lastPrePromotionEntry ? {
@@ -145435,7 +146251,7 @@ import * as zlib from "node:zlib";
145435
146251
  // src/evidence/documents.ts
145436
146252
  init_utils2();
145437
146253
  init_redaction();
145438
- import { createHash as createHash19 } from "node:crypto";
146254
+ import { createHash as createHash21 } from "node:crypto";
145439
146255
  import { appendFile as appendFile17, mkdir as mkdir34 } from "node:fs/promises";
145440
146256
  import * as path189 from "node:path";
145441
146257
  var EVIDENCE_CACHE_FILE = "evidence-cache/documents.jsonl";
@@ -145479,7 +146295,7 @@ function createEvidenceDocumentRecord(input, defaultCapturedAt) {
145479
146295
  };
145480
146296
  }
145481
146297
  function createEvidenceDocumentId(input) {
145482
- const hash4 = createHash19("sha256").update([
146298
+ const hash4 = createHash21("sha256").update([
145483
146299
  input.sourceType,
145484
146300
  input.query ?? "",
145485
146301
  input.title ?? "",
@@ -147276,6 +148092,8 @@ var TOOL_MANIFEST = defineHandlers({
147276
148092
  swarm_memory_propose: () => swarm_memory_propose,
147277
148093
  swarm_command: () => swarm_command,
147278
148094
  dispatch_lanes: () => dispatch_lanes,
148095
+ dispatch_lanes_async: () => dispatch_lanes_async,
148096
+ collect_lane_results: () => collect_lane_results,
147279
148097
  summarize_work: () => summarize_work,
147280
148098
  write_architecture_supervisor_evidence: () => write_architecture_supervisor_evidence,
147281
148099
  lean_turbo_plan_lanes: () => lean_turbo_plan_lanes,