opencode-swarm 7.79.6 → 7.80.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.6",
72
+ version: "7.80.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,
@@ -62861,10 +63010,12 @@ function isSkillMaturityEligible(entry, opts, outcomes = entry.retrieval_outcome
62861
63010
  if (outcomeSignal < 0)
62862
63011
  return false;
62863
63012
  const strongOutcomes = hasStrongSkillOutcomeRecord(outcomes);
63013
+ if (outcomeSignal > 0 && strongOutcomes)
63014
+ return true;
62864
63015
  if (entry.confidence < opts.minConfidence && !strongOutcomes)
62865
63016
  return false;
62866
- const confirmations = (entry.confirmed_by ?? []).length;
62867
- return confirmations >= opts.minConfirmations || strongOutcomes;
63017
+ const distinctPhases = new Set((entry.confirmed_by ?? []).map((c) => c.phase_number).filter((p) => typeof p === "number")).size;
63018
+ return distinctPhases >= opts.minConfirmations || strongOutcomes;
62868
63019
  }
62869
63020
  function jaccardSimilarity(setA, setB) {
62870
63021
  const normA = setA.map((s) => s.toLowerCase());
@@ -65459,7 +65610,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config3, know
65459
65610
  try {
65460
65611
  const skillModule = await Promise.resolve().then(() => (init_skill_generator(), exports_skill_generator));
65461
65612
  for (const cand of skillCandidates) {
65462
- if (cand.confidence < (config3.min_skill_confidence ?? 0.85)) {
65613
+ if (cand.confidence < (config3.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
65463
65614
  continue;
65464
65615
  }
65465
65616
  await skillModule.generateSkills({
@@ -67827,6 +67978,35 @@ function buildV3EnrichmentPrompt(lesson, category, tags) {
67827
67978
  ].join(`
67828
67979
  `);
67829
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
+ }
67830
68010
  function parseV3EnrichmentResponse(text) {
67831
68011
  if (!text || typeof text !== "string") {
67832
68012
  return { missing: ["valid JSON object"] };
@@ -67867,6 +68047,109 @@ function parseV3EnrichmentResponse(text) {
67867
68047
  }
67868
68048
  return { fields };
67869
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
+ }
67870
68153
  async function enrichLessonToV3(params) {
67871
68154
  const quota = params.quota ?? { maxCalls: 10, window: "utc" };
67872
68155
  const prompt = buildV3EnrichmentPrompt(params.lesson, params.category, params.tags);
@@ -68004,6 +68287,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
68004
68287
  const snapshotPlusNew = [...snapshot];
68005
68288
  const toAdd = [];
68006
68289
  const pendingReinforcementIds = new Set;
68290
+ const pendingBatchEnrichment = [];
68007
68291
  for (const lesson of lessons) {
68008
68292
  const tags = inferTags(lesson);
68009
68293
  let category = "process";
@@ -68066,20 +68350,10 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
68066
68350
  project_name: projectName,
68067
68351
  auto_generated: true
68068
68352
  };
68069
- let actionability = validateActionability(entry);
68353
+ const actionability = validateActionability(entry);
68070
68354
  if (!actionability.actionable && options?.llmDelegate) {
68071
- const enriched = await enrichLessonToV3({
68072
- directory,
68073
- llmDelegate: options.llmDelegate,
68074
- lesson,
68075
- category,
68076
- tags,
68077
- quota: options.enrichmentQuota
68078
- });
68079
- if (enriched) {
68080
- Object.assign(entry, enriched);
68081
- actionability = validateActionability(entry);
68082
- }
68355
+ pendingBatchEnrichment.push({ entry, lesson, category, tags });
68356
+ continue;
68083
68357
  }
68084
68358
  if (!actionability.actionable) {
68085
68359
  quarantined++;
@@ -68096,6 +68370,41 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
68096
68370
  toAdd.push(entry);
68097
68371
  snapshotPlusNew.push(entry);
68098
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
+ }
68099
68408
  try {
68100
68409
  const insights = await consumeInsightCandidates(directory);
68101
68410
  for (const cand of insights) {
@@ -68299,7 +68608,7 @@ function createKnowledgeCuratorHook(directory, config3, options = {}) {
68299
68608
  };
68300
68609
  return safeHook(handler);
68301
68610
  }
68302
- 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;
68303
68612
  var init_knowledge_curator = __esm(() => {
68304
68613
  init_skill_improver_quota();
68305
68614
  init_synonym_map();
@@ -95299,7 +95608,7 @@ Present all three items together in a single message. One message, defaults pre-
95299
95608
 
95300
95609
  **1. QA Gates** — accept defaults or customize (the eleven gates listed above).
95301
95610
 
95302
- **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>)"
95303
95612
 
95304
95613
  **3. Commit Frequency** — "Commit frequency for completed tasks? (default: phase-level only; optional per-task checkpoint commit after each task completion)"
95305
95614
 
@@ -95311,7 +95620,7 @@ Wait for the user to answer all three in a single reply. Then apply:
95311
95620
  ## Pending Parallelization Config
95312
95621
  - parallelization_enabled: true
95313
95622
  - max_concurrent_tasks: <user's number>
95314
- - council_parallel: true
95623
+ - council_parallel: false
95315
95624
  - locked: true
95316
95625
  - recorded_at: <ISO timestamp>
95317
95626
  \`\`\`
@@ -95648,8 +95957,8 @@ If a tool modifies a file, it is a CODER tool. Delegate.
95648
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.
95649
95958
  <!-- BEHAVIORAL_GUIDANCE_END -->
95650
95959
  2. ONE agent per message. Send, STOP, wait for response.
95651
- 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.
95652
- 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.
95653
95962
  3. ONE task per {{AGENT_PREFIX}}coder call. Never batch.
95654
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.
95655
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.
@@ -96580,6 +96889,7 @@ RULES:
96580
96889
  - Respect CONSTRAINT
96581
96890
  - No web searches or documentation lookups — but DO use the search tool for cross-codebase pattern lookup before using any function
96582
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.
96583
96893
 
96584
96894
  ## ANTI-HALLUCINATION PROTOCOL (MANDATORY)
96585
96895
  Before importing ANY function, type, or class from an existing project module:
@@ -106828,6 +107138,7 @@ init_worker();
106828
107138
  init_logger();
106829
107139
  init_pending_delegations();
106830
107140
  init_task_envelope();
107141
+ import { createHash as createHash11 } from "node:crypto";
106831
107142
  function createBackgroundCompletionObserver(opts) {
106832
107143
  const { config: config3, directory } = opts;
106833
107144
  const event = async (input) => {
@@ -106852,16 +107163,37 @@ function createBackgroundCompletionObserver(opts) {
106852
107163
  const pending = findByCorrelationId(directory, envelope.sessionId);
106853
107164
  const parentSessionId = typeof part.sessionID === "string" ? part.sessionID : "unknown";
106854
107165
  if (!pending) {
106855
- 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`);
106856
107175
  return;
106857
107176
  }
106858
- 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)");
106859
107188
  } catch (err2) {
106860
107189
  warn(`[background] completion observer error: ${err2 instanceof Error ? err2.message : String(err2)}`);
106861
107190
  }
106862
107191
  };
106863
107192
  return { event };
106864
107193
  }
107194
+ function digest(text) {
107195
+ return createHash11("sha256").update(text).digest("hex");
107196
+ }
106865
107197
 
106866
107198
  // src/index.ts
106867
107199
  init_pr_subscriptions();
@@ -117072,7 +117404,7 @@ init_knowledge_events();
117072
117404
  // src/hooks/knowledge-injector.ts
117073
117405
  init_schema();
117074
117406
  init_manager();
117075
- import { createHash as createHash14 } from "node:crypto";
117407
+ import { createHash as createHash15 } from "node:crypto";
117076
117408
 
117077
117409
  // src/services/run-memory.ts
117078
117410
  import * as crypto10 from "node:crypto";
@@ -117576,7 +117908,7 @@ function createKnowledgeInjectorHook(directory, config3, modelLimitOverrides = {
117576
117908
  ctx.taskId ?? "",
117577
117909
  (ctx.filePaths ?? []).slice(0, 8).join(",")
117578
117910
  ].join("|");
117579
- return createHash14("sha1").update(parts2).digest("hex").slice(0, 16);
117911
+ return createHash15("sha1").update(parts2).digest("hex").slice(0, 16);
117580
117912
  }
117581
117913
  let lastSeenCacheKey = null;
117582
117914
  let cachedInjectionText = null;
@@ -125572,6 +125904,9 @@ var diff_summary = createSwarmTool({
125572
125904
  }
125573
125905
  });
125574
125906
 
125907
+ // src/tools/dispatch-lanes.ts
125908
+ import { createHash as createHash16 } from "node:crypto";
125909
+
125575
125910
  // node_modules/yocto-queue/index.js
125576
125911
  class Node2 {
125577
125912
  value;
@@ -125730,6 +126065,7 @@ function validateConcurrency(concurrency) {
125730
126065
 
125731
126066
  // src/tools/dispatch-lanes.ts
125732
126067
  init_zod();
126068
+ init_pending_delegations();
125733
126069
  init_constants();
125734
126070
  init_schema();
125735
126071
 
@@ -125794,6 +126130,12 @@ var MAX_TIMEOUT_MS2 = 1800000;
125794
126130
  var MAX_LANE_OUTPUT_CHARS = 20000;
125795
126131
  var MAX_ERROR_CHARS = 200;
125796
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;
125797
126139
  var AGENT_NAME_SEPARATORS = ["_", "-", " "];
125798
126140
  var READ_ONLY_LANE_ROLES = new Set([
125799
126141
  "explorer",
@@ -125845,11 +126187,25 @@ var DispatchLanesArgsSchema = exports_external.object({
125845
126187
  max_concurrent: exports_external.number().int().min(1).max(MAX_LANES).optional().describe("Maximum lanes in flight at once; defaults to lane count"),
125846
126188
  timeout_ms: exports_external.number().int().min(10).max(MAX_TIMEOUT_MS2).optional().describe("Per-lane session create/prompt timeout in milliseconds")
125847
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
+ });
125848
126203
  var _internals81 = {
125849
126204
  getSessionOps: () => swarmState.opencodeClient?.session ?? null,
125850
126205
  getGeneratedAgentNames: () => swarmState.generatedAgentNames,
125851
126206
  createParallelDispatcher,
125852
- now: () => Date.now()
126207
+ now: () => Date.now(),
126208
+ sleep: sleep2
125853
126209
  };
125854
126210
  async function executeDispatchLanes(args2, directory, context = {}) {
125855
126211
  const parsed = DispatchLanesArgsSchema.safeParse(args2);
@@ -125891,6 +126247,322 @@ async function executeDispatchLanes(args2, directory, context = {}) {
125891
126247
  dispatcher.shutdown();
125892
126248
  }
125893
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
+ }
125894
126566
  async function runLane(session, dispatcher, lane, directory, timeoutMs, context) {
125895
126567
  const validation2 = validateLaneAgent(lane.agent, context);
125896
126568
  const role = validation2.role;
@@ -125991,6 +126663,49 @@ function buildResult(laneResults, maxConcurrent, timeoutMs) {
125991
126663
  lane_results: laneResults
125992
126664
  };
125993
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
+ }
125994
126709
  function failedLane(lane, role, startedAt, error93, slotId, runId, sessionId) {
125995
126710
  return {
125996
126711
  id: lane.id,
@@ -126093,6 +126808,40 @@ function failureResult(args2) {
126093
126808
  errors: args2.errors
126094
126809
  };
126095
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
+ }
126096
126845
  function findDuplicateLaneIds(lanes) {
126097
126846
  const seen = new Set;
126098
126847
  const duplicates = new Set;
@@ -126108,6 +126857,14 @@ function scheduleSessionCleanup(session, sessionId) {
126108
126857
  return;
126109
126858
  });
126110
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
+ }
126111
126868
  async function withTimeout2(promise3, timeoutMs, message, controller) {
126112
126869
  let timeout;
126113
126870
  try {
@@ -126118,9 +126875,6 @@ async function withTimeout2(promise3, timeoutMs, message, controller) {
126118
126875
  controller?.abort();
126119
126876
  reject(new Error(message));
126120
126877
  }, timeoutMs);
126121
- if (typeof timeout.unref === "function") {
126122
- timeout.unref();
126123
- }
126124
126878
  })
126125
126879
  ]);
126126
126880
  } finally {
@@ -126148,6 +126902,29 @@ function boundErrorString(text) {
126148
126902
  function isoNow() {
126149
126903
  return new Date(_internals81.now()).toISOString();
126150
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
+ }
126151
126928
  var dispatch_lanes = createSwarmTool({
126152
126929
  description: "Dispatch multiple read-only exploration/review lanes concurrently through OpenCode sessions and return a structured join result.",
126153
126930
  args: {
@@ -126162,12 +126939,53 @@ var dispatch_lanes = createSwarmTool({
126162
126939
  return JSON.stringify(result, null, 2);
126163
126940
  }
126164
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
+ });
126165
126977
  function getContextAgent(ctx) {
126166
126978
  if (!ctx || typeof ctx !== "object")
126167
126979
  return;
126168
126980
  const value = ctx.agent;
126169
126981
  return typeof value === "string" ? value : undefined;
126170
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
+ }
126171
126989
 
126172
126990
  // src/tools/manifest.ts
126173
126991
  init_doc_scan();
@@ -126848,11 +127666,11 @@ var external_skill_delete = createSwarmTool({
126848
127666
  // src/tools/external-skill-discover.ts
126849
127667
  init_zod();
126850
127668
  init_loader();
126851
- import { createHash as createHash16, randomUUID as randomUUID12 } from "node:crypto";
127669
+ import { createHash as createHash18, randomUUID as randomUUID12 } from "node:crypto";
126852
127670
 
126853
127671
  // src/services/external-skill-validator.ts
126854
127672
  init_knowledge_validator();
126855
- import { createHash as createHash15 } from "node:crypto";
127673
+ import { createHash as createHash17 } from "node:crypto";
126856
127674
  var PROMPT_INJECTION_PATTERNS = [
126857
127675
  {
126858
127676
  pattern: /system\s*:/i,
@@ -127387,7 +128205,7 @@ function evaluateCandidate(candidate, options) {
127387
128205
  }
127388
128206
  var _internals84 = {
127389
128207
  getTimestamp: () => new Date().toISOString(),
127390
- computeSha256: (content) => createHash15("sha256").update(content).digest("hex"),
128208
+ computeSha256: (content) => createHash17("sha256").update(content).digest("hex"),
127391
128209
  stripMarkdownCodeForUnsafeScan
127392
128210
  };
127393
128211
 
@@ -127409,7 +128227,7 @@ var _internals85 = {
127409
128227
  return { content, finalUrl: response.url };
127410
128228
  },
127411
128229
  getTimestamp: () => new Date().toISOString(),
127412
- computeSha256: (content) => createHash16("sha256").update(content).digest("hex"),
128230
+ computeSha256: (content) => createHash18("sha256").update(content).digest("hex"),
127413
128231
  uuid: () => randomUUID12()
127414
128232
  };
127415
128233
  var SOURCE_TRUST_LEVELS = {
@@ -127795,7 +128613,7 @@ var external_skill_list = createSwarmTool({
127795
128613
  // src/tools/external-skill-promote.ts
127796
128614
  init_zod();
127797
128615
  init_loader();
127798
- import { createHash as createHash17 } from "node:crypto";
128616
+ import { createHash as createHash19 } from "node:crypto";
127799
128617
  import * as fs95 from "node:fs/promises";
127800
128618
  import * as path149 from "node:path";
127801
128619
  init_create_tool();
@@ -127960,7 +128778,7 @@ var external_skill_promote = createSwarmTool({
127960
128778
  }
127961
128779
  throw writeErr;
127962
128780
  }
127963
- const promotedContentHash = createHash17("sha256").update(skillMarkdown).digest("hex");
128781
+ const promotedContentHash = createHash19("sha256").update(skillMarkdown).digest("hex");
127964
128782
  const prePromotionHistory = candidate.evaluation_history;
127965
128783
  const lastPrePromotionEntry = prePromotionHistory.length > 0 ? prePromotionHistory[prePromotionHistory.length - 1] : undefined;
127966
128784
  const originalEvaluation = lastPrePromotionEntry ? {
@@ -145433,7 +146251,7 @@ import * as zlib from "node:zlib";
145433
146251
  // src/evidence/documents.ts
145434
146252
  init_utils2();
145435
146253
  init_redaction();
145436
- import { createHash as createHash19 } from "node:crypto";
146254
+ import { createHash as createHash21 } from "node:crypto";
145437
146255
  import { appendFile as appendFile17, mkdir as mkdir34 } from "node:fs/promises";
145438
146256
  import * as path189 from "node:path";
145439
146257
  var EVIDENCE_CACHE_FILE = "evidence-cache/documents.jsonl";
@@ -145477,7 +146295,7 @@ function createEvidenceDocumentRecord(input, defaultCapturedAt) {
145477
146295
  };
145478
146296
  }
145479
146297
  function createEvidenceDocumentId(input) {
145480
- const hash4 = createHash19("sha256").update([
146298
+ const hash4 = createHash21("sha256").update([
145481
146299
  input.sourceType,
145482
146300
  input.query ?? "",
145483
146301
  input.title ?? "",
@@ -147274,6 +148092,8 @@ var TOOL_MANIFEST = defineHandlers({
147274
148092
  swarm_memory_propose: () => swarm_memory_propose,
147275
148093
  swarm_command: () => swarm_command,
147276
148094
  dispatch_lanes: () => dispatch_lanes,
148095
+ dispatch_lanes_async: () => dispatch_lanes_async,
148096
+ collect_lane_results: () => collect_lane_results,
147277
148097
  summarize_work: () => summarize_work,
147278
148098
  write_architecture_supervisor_evidence: () => write_architecture_supervisor_evidence,
147279
148099
  lean_turbo_plan_lanes: () => lean_turbo_plan_lanes,