opencode-swarm 7.32.3 → 7.33.1

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
@@ -48,7 +48,7 @@ var package_default;
48
48
  var init_package = __esm(() => {
49
49
  package_default = {
50
50
  name: "opencode-swarm",
51
- version: "7.32.3",
51
+ version: "7.33.1",
52
52
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
53
53
  main: "dist/index.js",
54
54
  types: "dist/index.d.ts",
@@ -15539,8 +15539,12 @@ var init_schema = __esm(() => {
15539
15539
  });
15540
15540
  MemoryConfigSchema = exports_external.object({
15541
15541
  enabled: exports_external.boolean().default(false),
15542
- provider: exports_external.literal("local-jsonl").default("local-jsonl"),
15542
+ provider: exports_external.enum(["local-jsonl", "sqlite"]).default("local-jsonl"),
15543
15543
  storageDir: exports_external.string().default(".swarm/memory"),
15544
+ sqlite: exports_external.object({
15545
+ path: exports_external.string().default(".swarm/memory/memory.db"),
15546
+ busyTimeoutMs: exports_external.number().int().min(0).max(60000).default(5000)
15547
+ }).default({ path: ".swarm/memory/memory.db", busyTimeoutMs: 5000 }),
15544
15548
  recall: exports_external.object({
15545
15549
  defaultMaxItems: exports_external.number().int().min(1).max(20).default(8),
15546
15550
  defaultTokenBudget: exports_external.number().int().min(100).max(5000).default(1200),
@@ -17366,6 +17370,32 @@ async function appendLedgerEvent(directory, eventInput, options) {
17366
17370
  fs4.renameSync(tempPath, ledgerPath);
17367
17371
  return event;
17368
17372
  }
17373
+ async function takeSnapshotWithRetry(directory, plan, options) {
17374
+ const MAX_RETRIES = 3;
17375
+ const TOTAL_ATTEMPTS = 1 + MAX_RETRIES;
17376
+ const telemetrySource = options?.source ?? "save_plan_tool";
17377
+ const snapshotOptions = { planHashAfter: options?.planHashAfter };
17378
+ let lastError;
17379
+ for (let attempt = 1;attempt <= TOTAL_ATTEMPTS; attempt++) {
17380
+ try {
17381
+ await takeSnapshotEvent(directory, plan, snapshotOptions);
17382
+ return;
17383
+ } catch (err2) {
17384
+ lastError = err2 instanceof Error ? err2 : new Error(String(err2));
17385
+ if (attempt < TOTAL_ATTEMPTS) {
17386
+ await new Promise((r) => setTimeout(r, 10 * 2 ** (attempt - 1)));
17387
+ }
17388
+ }
17389
+ }
17390
+ console.warn(`[takeSnapshotWithRetry] Snapshot failed after ${MAX_RETRIES} retries (${TOTAL_ATTEMPTS} attempts): ${lastError.message}`);
17391
+ try {
17392
+ emit("snapshot_failed", {
17393
+ error: lastError.message,
17394
+ retries: MAX_RETRIES,
17395
+ source: telemetrySource
17396
+ });
17397
+ } catch {}
17398
+ }
17369
17399
  async function takeSnapshotEvent(directory, plan, options) {
17370
17400
  const payloadHash = computePlanHash(plan);
17371
17401
  const snapshotPayload = {
@@ -17599,6 +17629,7 @@ async function loadLastApprovedPlan(directory, expectedPlanId) {
17599
17629
  var LEDGER_SCHEMA_VERSION = "1.1.0", LEDGER_FILENAME = "plan-ledger.jsonl", PLAN_JSON_FILENAME = "plan.json", LedgerStaleWriterError;
17600
17630
  var init_ledger = __esm(() => {
17601
17631
  init_plan_schema();
17632
+ init_telemetry();
17602
17633
  LedgerStaleWriterError = class LedgerStaleWriterError extends Error {
17603
17634
  constructor(message) {
17604
17635
  super(message);
@@ -17792,7 +17823,9 @@ async function loadPlan(directory) {
17792
17823
  try {
17793
17824
  const rebuilt = await replayFromLedger(directory);
17794
17825
  if (rebuilt) {
17795
- await rebuildPlan(directory, rebuilt);
17826
+ await rebuildPlan(directory, rebuilt, {
17827
+ reason: "ledger_hash_mismatch_recovery"
17828
+ });
17796
17829
  warn("[loadPlan] Rebuilt plan from ledger. Checkpoint available at .swarm/SWARM_PLAN.md if it exists.");
17797
17830
  return rebuilt;
17798
17831
  }
@@ -17800,7 +17833,9 @@ async function loadPlan(directory) {
17800
17833
  try {
17801
17834
  const approved = await loadLastApprovedPlan(directory, currentPlanId);
17802
17835
  if (approved) {
17803
- await rebuildPlan(directory, approved.plan);
17836
+ await rebuildPlan(directory, approved.plan, {
17837
+ reason: "approved_snapshot_fallback"
17838
+ });
17804
17839
  try {
17805
17840
  await takeSnapshotEvent(directory, approved.plan, {
17806
17841
  source: "recovery_from_approved_snapshot",
@@ -17877,7 +17912,9 @@ async function loadPlan(directory) {
17877
17912
  } else if (catchFirstEvent !== null && rawPlanId !== null) {
17878
17913
  const rebuilt = await replayFromLedger(directory);
17879
17914
  if (rebuilt) {
17880
- await rebuildPlan(directory, rebuilt);
17915
+ await rebuildPlan(directory, rebuilt, {
17916
+ reason: "validation_failure_recovery"
17917
+ });
17881
17918
  warn("[loadPlan] Rebuilt plan from ledger after validation failure. Projection was stale.");
17882
17919
  return rebuilt;
17883
17920
  }
@@ -18238,12 +18275,9 @@ async function savePlan(directory, plan, options) {
18238
18275
  const SNAPSHOT_INTERVAL = 50;
18239
18276
  const latestSeq = await getLatestLedgerSeq(directory);
18240
18277
  if (latestSeq > 0 && latestSeq % SNAPSHOT_INTERVAL === 0) {
18241
- await takeSnapshotEvent(directory, validated, {
18242
- planHashAfter: hashAfter
18243
- }).catch((err2) => {
18244
- if (process.env.DEBUG_SWARM) {
18245
- warn(`[savePlan] Periodic snapshot write failed (non-fatal): ${err2 instanceof Error ? err2.message : String(err2)}`);
18246
- }
18278
+ await takeSnapshotWithRetry(directory, validated, {
18279
+ planHashAfter: hashAfter,
18280
+ source: "savePlan_manager"
18247
18281
  });
18248
18282
  }
18249
18283
  const swarmDir = path6.resolve(directory, ".swarm");
@@ -18257,6 +18291,17 @@ async function savePlan(directory, plan, options) {
18257
18291
  unlinkSync(tempPath);
18258
18292
  } catch {}
18259
18293
  }
18294
+ try {
18295
+ const markerPath = path6.join(swarmDir, ".plan-write-marker");
18296
+ const inProgressMarker = JSON.stringify({
18297
+ source: "plan_manager",
18298
+ timestamp: new Date().toISOString(),
18299
+ phases_count: validated.phases.length,
18300
+ tasks_count: validated.phases.reduce((sum, p) => sum + p.tasks.length, 0),
18301
+ in_progress: true
18302
+ });
18303
+ await bunWrite(markerPath, inProgressMarker);
18304
+ } catch {}
18260
18305
  try {
18261
18306
  const contentHash = computePlanContentHash(validated);
18262
18307
  const markdown = derivePlanMarkdown(validated);
@@ -18290,41 +18335,146 @@ ${markdown}`;
18290
18335
  source: "plan_manager",
18291
18336
  timestamp: new Date().toISOString(),
18292
18337
  phases_count: validated.phases.length,
18293
- tasks_count: tasksCount
18338
+ tasks_count: tasksCount,
18339
+ in_progress: false
18294
18340
  });
18295
18341
  await bunWrite(markerPath, marker);
18296
18342
  } catch {}
18297
18343
  }
18298
- async function rebuildPlan(directory, plan) {
18344
+ async function rebuildPlan(directory, plan, options) {
18299
18345
  const targetPlan = plan ?? await replayFromLedger(directory);
18300
18346
  if (!targetPlan)
18301
18347
  return null;
18302
18348
  const swarmDir = path6.join(directory, ".swarm");
18303
18349
  const planPath = path6.join(swarmDir, "plan.json");
18304
18350
  const mdPath = path6.join(swarmDir, "plan.md");
18305
- const tempPlanPath = path6.join(swarmDir, `plan.json.rebuild.${Date.now()}`);
18351
+ const tempPlanPath = path6.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
18306
18352
  await bunWrite(tempPlanPath, JSON.stringify(targetPlan, null, 2));
18307
18353
  renameSync3(tempPlanPath, planPath);
18308
- const contentHash = computePlanContentHash(targetPlan);
18309
- const markdown = derivePlanMarkdown(targetPlan);
18310
- const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
18311
- ${markdown}`;
18312
- const tempMdPath = path6.join(swarmDir, `plan.md.rebuild.${Date.now()}`);
18313
- await bunWrite(tempMdPath, markdownWithHash);
18314
- renameSync3(tempMdPath, mdPath);
18315
18354
  try {
18316
18355
  const markerPath = path6.join(swarmDir, ".plan-write-marker");
18317
- const tasksCount = targetPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
18318
- const marker = JSON.stringify({
18356
+ const inProgressMarker = JSON.stringify({
18319
18357
  source: "plan_manager",
18320
18358
  timestamp: new Date().toISOString(),
18321
18359
  phases_count: targetPlan.phases.length,
18322
- tasks_count: tasksCount
18360
+ tasks_count: targetPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0),
18361
+ in_progress: true
18323
18362
  });
18324
- await bunWrite(markerPath, marker);
18363
+ await bunWrite(markerPath, inProgressMarker);
18364
+ } catch {}
18365
+ try {
18366
+ const contentHash = computePlanContentHash(targetPlan);
18367
+ const markdown = derivePlanMarkdown(targetPlan);
18368
+ const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
18369
+ ${markdown}`;
18370
+ const tempMdPath = path6.join(swarmDir, `plan.md.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
18371
+ await bunWrite(tempMdPath, markdownWithHash);
18372
+ renameSync3(tempMdPath, mdPath);
18373
+ } finally {
18374
+ try {
18375
+ const markerPath = path6.join(swarmDir, ".plan-write-marker");
18376
+ const tasksCount = targetPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
18377
+ const marker = JSON.stringify({
18378
+ source: "plan_manager",
18379
+ timestamp: new Date().toISOString(),
18380
+ phases_count: targetPlan.phases.length,
18381
+ tasks_count: tasksCount,
18382
+ in_progress: false
18383
+ });
18384
+ await bunWrite(markerPath, marker);
18385
+ } catch {}
18386
+ }
18387
+ try {
18388
+ const planId = derivePlanId(targetPlan);
18389
+ const planHashAfter = computePlanHash(targetPlan);
18390
+ await appendLedgerEvent(directory, {
18391
+ event_type: "plan_rebuilt",
18392
+ source: "rebuildPlan",
18393
+ plan_id: planId,
18394
+ payload: {
18395
+ reason: options?.reason ?? "ledger_replay_recovery",
18396
+ phases_count: targetPlan.phases.length,
18397
+ tasks_count: targetPlan.phases.reduce((sum, p) => sum + p.tasks.length, 0)
18398
+ }
18399
+ }, { planHashAfter });
18325
18400
  } catch {}
18326
18401
  return targetPlan;
18327
18402
  }
18403
+ async function closePlanTerminalState(directory, plan, options) {
18404
+ const planId = derivePlanId(plan);
18405
+ const validated = PlanSchema.parse(plan);
18406
+ const hashAfter = computePlanHash(validated);
18407
+ for (const taskId of options.closedTaskIds) {
18408
+ let taskPhaseId;
18409
+ for (const phase of validated.phases) {
18410
+ if (phase.tasks.some((t) => t.id === taskId)) {
18411
+ taskPhaseId = phase.id;
18412
+ break;
18413
+ }
18414
+ }
18415
+ const fromStatus = options.originalStatuses?.get(taskId) ?? "in_progress";
18416
+ await appendLedgerEvent(directory, {
18417
+ plan_id: planId,
18418
+ event_type: "task_status_changed",
18419
+ task_id: taskId,
18420
+ phase_id: taskPhaseId,
18421
+ from_status: fromStatus,
18422
+ to_status: "closed",
18423
+ source: "close_terminal"
18424
+ }, { planHashAfter: hashAfter });
18425
+ }
18426
+ for (const phaseId of options.closedPhaseIds) {
18427
+ await appendLedgerEvent(directory, {
18428
+ plan_id: planId,
18429
+ event_type: "phase_completed",
18430
+ phase_id: phaseId,
18431
+ source: "close_terminal"
18432
+ }, { planHashAfter: hashAfter });
18433
+ }
18434
+ await takeSnapshotEvent(directory, validated, {
18435
+ planHashAfter: hashAfter,
18436
+ source: "close_terminal"
18437
+ });
18438
+ const swarmDir = path6.join(directory, ".swarm");
18439
+ const planPath = path6.join(swarmDir, "plan.json");
18440
+ const tempPlanPath = path6.join(swarmDir, `plan.json.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
18441
+ await bunWrite(tempPlanPath, JSON.stringify(validated, null, 2));
18442
+ renameSync3(tempPlanPath, planPath);
18443
+ try {
18444
+ const markerPath = path6.join(swarmDir, ".plan-write-marker");
18445
+ const inProgressMarker = JSON.stringify({
18446
+ source: "plan_manager_close",
18447
+ timestamp: new Date().toISOString(),
18448
+ phases_count: validated.phases.length,
18449
+ tasks_count: validated.phases.reduce((sum, phase) => sum + phase.tasks.length, 0),
18450
+ in_progress: true
18451
+ });
18452
+ await bunWrite(markerPath, inProgressMarker);
18453
+ } catch {}
18454
+ try {
18455
+ const mdPath = path6.join(swarmDir, "plan.md");
18456
+ const contentHash = computePlanContentHash(validated);
18457
+ const markdown = derivePlanMarkdown(validated);
18458
+ const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
18459
+ ${markdown}`;
18460
+ const mdTempPath = path6.join(swarmDir, `plan.md.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
18461
+ await bunWrite(mdTempPath, markdownWithHash);
18462
+ renameSync3(mdTempPath, mdPath);
18463
+ } finally {
18464
+ try {
18465
+ const markerPath = path6.join(swarmDir, ".plan-write-marker");
18466
+ const tasksCount = validated.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
18467
+ const marker = JSON.stringify({
18468
+ source: "plan_manager_close",
18469
+ timestamp: new Date().toISOString(),
18470
+ phases_count: validated.phases.length,
18471
+ tasks_count: tasksCount,
18472
+ in_progress: false
18473
+ });
18474
+ await bunWrite(markerPath, marker);
18475
+ } catch {}
18476
+ }
18477
+ }
18328
18478
  async function updateTaskStatus(directory, taskId, status) {
18329
18479
  const derivePhaseStatusFromTasks = (tasks) => {
18330
18480
  if (tasks.length > 0 && tasks.every((task) => task.status === "completed")) {
@@ -58840,6 +58990,12 @@ async function handleCloseCommand(directory, args2, options = {}) {
58840
58990
  }
58841
58991
  }
58842
58992
  if (planExists) {
58993
+ const originalStatuses = new Map;
58994
+ for (const phase of planData.phases ?? []) {
58995
+ for (const task of phase.tasks ?? []) {
58996
+ originalStatuses.set(task.id, task.status);
58997
+ }
58998
+ }
58843
58999
  const guaranteeResult = guaranteeAllPlansComplete(planData);
58844
59000
  for (const phaseId of guaranteeResult.closedPhaseIds) {
58845
59001
  if (!closedPhases.includes(phaseId)) {
@@ -58853,11 +59009,15 @@ async function handleCloseCommand(directory, args2, options = {}) {
58853
59009
  }
58854
59010
  if (!planAlreadyDone || guaranteeResult.closedPhaseIds.length > 0 || guaranteeResult.closedTaskIds.length > 0) {
58855
59011
  try {
58856
- await fs13.writeFile(planPath, JSON.stringify(planData, null, 2), "utf-8");
59012
+ await closePlanTerminalState(directory, planData, {
59013
+ closedPhaseIds: guaranteeResult.closedPhaseIds,
59014
+ closedTaskIds: guaranteeResult.closedTaskIds,
59015
+ originalStatuses
59016
+ });
58857
59017
  } catch (error93) {
58858
59018
  const msg = error93 instanceof Error ? error93.message : String(error93);
58859
- warnings.push(`Failed to persist terminal plan.json state: ${msg}`);
58860
- console.warn("[close-command] Failed to write plan.json:", error93);
59019
+ warnings.push(`Failed to persist terminal plan state: ${msg}`);
59020
+ console.warn("[close-command] Failed to write terminal plan state:", error93);
58861
59021
  }
58862
59022
  }
58863
59023
  }
@@ -59145,6 +59305,7 @@ var init_close = __esm(() => {
59145
59305
  init_knowledge_curator();
59146
59306
  init_knowledge_store();
59147
59307
  init_utils2();
59308
+ init_manager();
59148
59309
  init_scope_persistence();
59149
59310
  init_skill_improver();
59150
59311
  init_state();
@@ -59887,6 +60048,120 @@ function getPluginCachePaths() {
59887
60048
  }
59888
60049
  var init_cache_paths = () => {};
59889
60050
 
60051
+ // src/evidence/gate-bridge.ts
60052
+ async function readDurableGateEvidence(directory, taskId) {
60053
+ try {
60054
+ return await readTaskEvidence(directory, taskId);
60055
+ } catch {
60056
+ return null;
60057
+ }
60058
+ }
60059
+ function getDurableGateEvidenceStatus(evidence) {
60060
+ if (!evidence?.gates || typeof evidence.gates !== "object") {
60061
+ return {
60062
+ isComplete: false,
60063
+ missingGates: [],
60064
+ evidenceExists: evidence != null,
60065
+ invalid: false
60066
+ };
60067
+ }
60068
+ if (!Array.isArray(evidence.required_gates) || evidence.required_gates.length === 0) {
60069
+ return {
60070
+ isComplete: false,
60071
+ missingGates: ["required_gates"],
60072
+ evidenceExists: true,
60073
+ invalid: false
60074
+ };
60075
+ }
60076
+ const missingGates = evidence.required_gates.filter((gate) => evidence.gates[gate] == null);
60077
+ return {
60078
+ isComplete: missingGates.length === 0,
60079
+ missingGates,
60080
+ evidenceExists: true,
60081
+ invalid: false
60082
+ };
60083
+ }
60084
+ async function getDurableGateEvidenceStatusForTask(directory, taskId) {
60085
+ if (!isValidTaskId(taskId)) {
60086
+ return {
60087
+ isComplete: false,
60088
+ missingGates: [],
60089
+ evidenceExists: false,
60090
+ invalid: false
60091
+ };
60092
+ }
60093
+ try {
60094
+ return getDurableGateEvidenceStatus(readTaskEvidenceRaw(directory, taskId));
60095
+ } catch {
60096
+ return {
60097
+ isComplete: false,
60098
+ missingGates: ["invalid_gate_evidence"],
60099
+ evidenceExists: true,
60100
+ invalid: true
60101
+ };
60102
+ }
60103
+ }
60104
+ function gateEvidenceToEntry(taskId, gate, type, evidence) {
60105
+ const gateRecord = evidence.gates[gate];
60106
+ if (!gateRecord) {
60107
+ return null;
60108
+ }
60109
+ const base = {
60110
+ task_id: taskId,
60111
+ timestamp: gateRecord.timestamp,
60112
+ agent: gateRecord.agent || gate,
60113
+ verdict: "pass",
60114
+ summary: `Gate evidence recorded by ${gate}`,
60115
+ metadata: { source: "durable_gate_evidence", gate }
60116
+ };
60117
+ if (type === "review") {
60118
+ return {
60119
+ ...base,
60120
+ type,
60121
+ risk: "low",
60122
+ issues: []
60123
+ };
60124
+ }
60125
+ if (type === "approval") {
60126
+ return {
60127
+ ...base,
60128
+ type
60129
+ };
60130
+ }
60131
+ return {
60132
+ ...base,
60133
+ type,
60134
+ tests_passed: 0,
60135
+ tests_failed: 0,
60136
+ failures: []
60137
+ };
60138
+ }
60139
+ function mergeDurableGateEntriesFromEvidence(taskId, entries, evidence) {
60140
+ if (!evidence?.gates) {
60141
+ return entries;
60142
+ }
60143
+ const merged = [...entries];
60144
+ for (const gate of Object.keys(evidence.gates).sort()) {
60145
+ const type = GATE_EVIDENCE_TYPE_BY_GATE[gate] ?? "approval";
60146
+ if ((type === "review" || type === "test") && merged.some((entry2) => entry2.type === type)) {
60147
+ continue;
60148
+ }
60149
+ const entry = gateEvidenceToEntry(taskId, gate, type, evidence);
60150
+ if (entry) {
60151
+ merged.push(entry);
60152
+ }
60153
+ }
60154
+ return merged;
60155
+ }
60156
+ var GATE_EVIDENCE_TYPE_BY_GATE;
60157
+ var init_gate_bridge = __esm(() => {
60158
+ init_gate_evidence();
60159
+ GATE_EVIDENCE_TYPE_BY_GATE = {
60160
+ reviewer: "review",
60161
+ test_engineer: "test"
60162
+ };
60163
+ });
60164
+
59890
60165
  // src/services/version-check.ts
59891
60166
  import { existsSync as existsSync14, mkdirSync as mkdirSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
59892
60167
  import { homedir as homedir5 } from "node:os";
@@ -60034,7 +60309,21 @@ async function checkEvidenceCompleteness(directory, plan) {
60034
60309
  }
60035
60310
  if (completedTaskIds.length > 0) {
60036
60311
  const evidenceTaskIds = new Set(await listEvidenceTaskIds(directory));
60037
- const missingEvidence = completedTaskIds.filter((id) => !evidenceTaskIds.has(id));
60312
+ const missingEvidence = [];
60313
+ for (const id of completedTaskIds) {
60314
+ const gateStatus = await getDurableGateEvidenceStatusForTask(directory, id);
60315
+ if (gateStatus.isComplete) {
60316
+ continue;
60317
+ }
60318
+ if (gateStatus.evidenceExists && gateStatus.missingGates.length > 0) {
60319
+ missingEvidence.push(id);
60320
+ continue;
60321
+ }
60322
+ if (evidenceTaskIds.has(id)) {
60323
+ continue;
60324
+ }
60325
+ missingEvidence.push(id);
60326
+ }
60038
60327
  if (missingEvidence.length === 0) {
60039
60328
  return {
60040
60329
  name: "Evidence",
@@ -60789,6 +61078,7 @@ var init_diagnose_service = __esm(() => {
60789
61078
  init_package();
60790
61079
  init_cache_paths();
60791
61080
  init_loader();
61081
+ init_gate_bridge();
60792
61082
  init_manager2();
60793
61083
  init_utils2();
60794
61084
  init_manager();
@@ -63355,8 +63645,7 @@ function getTaskStatus(task, bundle) {
63355
63645
  }
63356
63646
  return "pending";
63357
63647
  }
63358
- function isEvidenceComplete(bundle) {
63359
- const entries = _internals20.normalizeBundleEntries(bundle);
63648
+ function evidenceCompleteFromEntries(entries) {
63360
63649
  if (entries.length === 0) {
63361
63650
  return {
63362
63651
  isComplete: false,
@@ -63375,6 +63664,9 @@ function isEvidenceComplete(bundle) {
63375
63664
  missingEvidence: missing
63376
63665
  };
63377
63666
  }
63667
+ function isEvidenceComplete(bundle) {
63668
+ return evidenceCompleteFromEntries(_internals20.normalizeBundleEntries(bundle));
63669
+ }
63378
63670
  function getTaskBlockers(task, summary, status) {
63379
63671
  const blockers = [];
63380
63672
  if (task?.blocked_reason) {
@@ -63391,11 +63683,19 @@ function getTaskBlockers(task, summary, status) {
63391
63683
  async function buildTaskSummary(directory, task, taskId) {
63392
63684
  const result = await loadEvidence(directory, taskId);
63393
63685
  const bundle = result.status === "found" ? result.bundle : null;
63686
+ const gateEvidence = await readDurableGateEvidence(directory, taskId);
63394
63687
  const phase = task?.phase ?? 0;
63395
63688
  const status = _internals20.getTaskStatus(task, bundle);
63396
- const evidenceCheck = _internals20.isEvidenceComplete(bundle);
63689
+ const entries = mergeDurableGateEntriesFromEvidence(taskId, _internals20.normalizeBundleEntries(bundle), gateEvidence);
63690
+ let evidenceCheck = _internals20.evidenceCompleteFromEntries(entries);
63691
+ if (gateEvidence) {
63692
+ const gateStatus = getDurableGateEvidenceStatus(gateEvidence);
63693
+ evidenceCheck = gateStatus.isComplete ? { isComplete: true, missingEvidence: [] } : {
63694
+ isComplete: false,
63695
+ missingEvidence: gateStatus.missingGates.map((gate) => `gate:${gate}`)
63696
+ };
63697
+ }
63397
63698
  const blockers = _internals20.getTaskBlockers(task, evidenceCheck, status);
63398
- const entries = _internals20.normalizeBundleEntries(bundle);
63399
63699
  const hasReview = entries.some((e) => e.type === "review");
63400
63700
  const hasTest = entries.some((e) => e.type === "test");
63401
63701
  const hasApproval = entries.some((e) => e.type === "approval");
@@ -63573,6 +63873,7 @@ function isAutoSummaryEnabled(automationConfig) {
63573
63873
  }
63574
63874
  var VALID_EVIDENCE_TYPES2, REQUIRED_EVIDENCE_TYPES, EVIDENCE_SUMMARY_VERSION = "1.0.0", _internals20;
63575
63875
  var init_evidence_summary_service = __esm(() => {
63876
+ init_gate_bridge();
63576
63877
  init_manager2();
63577
63878
  init_manager();
63578
63879
  init_utils();
@@ -63590,6 +63891,7 @@ var init_evidence_summary_service = __esm(() => {
63590
63891
  isAutoSummaryEnabled,
63591
63892
  normalizeBundleEntries,
63592
63893
  getTaskStatus,
63894
+ evidenceCompleteFromEntries,
63593
63895
  isEvidenceComplete,
63594
63896
  getTaskBlockers,
63595
63897
  buildTaskSummary,
@@ -71200,7 +71502,22 @@ async function runEvidenceCheck(dir) {
71200
71502
  };
71201
71503
  }
71202
71504
  const evidenceTaskIds = new Set(await listEvidenceTaskIds(dir));
71203
- const missingEvidence = completedTaskIds.filter((id) => !evidenceTaskIds.has(id));
71505
+ const missingEvidence = [];
71506
+ for (const id of completedTaskIds) {
71507
+ const gateStatus = await getDurableGateEvidenceStatusForTask(dir, id);
71508
+ if (gateStatus.isComplete) {
71509
+ continue;
71510
+ }
71511
+ if (gateStatus.evidenceExists && gateStatus.missingGates.length > 0) {
71512
+ missingEvidence.push(id);
71513
+ continue;
71514
+ }
71515
+ if (evidenceTaskIds.has(id)) {
71516
+ continue;
71517
+ }
71518
+ missingEvidence.push(id);
71519
+ }
71520
+ const completedWithEvidence = completedTaskIds.length - missingEvidence.length;
71204
71521
  if (missingEvidence.length > 0) {
71205
71522
  return {
71206
71523
  type: "evidence",
@@ -71208,7 +71525,7 @@ async function runEvidenceCheck(dir) {
71208
71525
  message: `${missingEvidence.length} completed task(s) missing evidence`,
71209
71526
  details: {
71210
71527
  totalCompleted: completedTaskIds.length,
71211
- totalWithEvidence: evidenceTaskIds.size,
71528
+ totalWithEvidence: completedWithEvidence,
71212
71529
  missingTasks: missingEvidence.slice(0, 10),
71213
71530
  missingCount: missingEvidence.length
71214
71531
  },
@@ -71221,7 +71538,7 @@ async function runEvidenceCheck(dir) {
71221
71538
  message: `All ${completedTaskIds.length} completed tasks have evidence`,
71222
71539
  details: {
71223
71540
  totalCompleted: completedTaskIds.length,
71224
- totalWithEvidence: evidenceTaskIds.size
71541
+ totalWithEvidence: completedWithEvidence
71225
71542
  },
71226
71543
  durationMs: Date.now() - startTime
71227
71544
  };
@@ -71452,6 +71769,7 @@ async function handlePreflightCommand(directory, _args) {
71452
71769
  }
71453
71770
  var MIN_CHECK_TIMEOUT_MS = 5000, MAX_CHECK_TIMEOUT_MS = 300000, DEFAULT_CONFIG, _internals34;
71454
71771
  var init_preflight_service = __esm(() => {
71772
+ init_gate_bridge();
71455
71773
  init_manager2();
71456
71774
  init_manager();
71457
71775
  init_lint();
@@ -76397,121 +76715,14 @@ Do NOT share other agents' responses at this stage.
76397
76715
  ### MODE: DEEP_DIVE
76398
76716
  Activates when: architect receives \`[MODE: DEEP_DIVE profile=X max_explorers=N output=X update_main=X allow_dirty=X] <scope>\` signal from the deep-dive command handler.
76399
76717
 
76400
- Purpose: Perform a read-only deep audit of the specified codebase scope using parallel explorer waves, always 2 parallel reviewers, and sequential critic challenge. This mode does NOT mutate source code, does NOT delegate to coder, and does NOT call declare_scope.
76401
-
76402
- #### STEP 0 PARSE HEADER
76403
- Parse the MODE: DEEP_DIVE header to extract:
76404
- - \`scope\`: the codebase area to audit (e.g., "auth", "payment flow", "src/hooks/")
76405
- - \`profile\`: one of standard | security | ux | architecture | full (default: standard)
76406
- - \`max_explorers\`: integer 1..8 (default: 6, or 8 for full profile)
76407
- - \`output\`: markdown | json (default: markdown)
76408
- - \`update_main\`: boolean (default: true) — whether to fetch/ff-only main before starting
76409
- - \`allow_dirty\`: boolean (default: false) — whether to proceed with uncommitted changes
76410
-
76411
- If the header is malformed or missing required fields, report the error and stop.
76412
-
76413
- #### STEP 1 — REPO READINESS
76414
- 1. Check git working tree status. If dirty and \`allow_dirty\` is false, warn the user and ask whether to proceed. Do NOT proceed automatically.
76415
- 2. If \`update_main\` is true and tree is clean: check current branch. If not on \`main\`, report current branch to user and ASK FOR CONFIRMATION before switching. Only after explicit user approval: \`git fetch origin main && git checkout main && git merge --ff-only origin/main\`. If ff-only fails, warn the user and ask before proceeding.
76416
- 3. Record the current HEAD commit hash for the report.
76417
-
76418
- #### STEP 2 — SCOPE RESOLUTION
76419
- Use the following tools to map the audit scope:
76420
- 1. \`repo_map\` with action "build" to establish the code graph
76421
- 2. \`repo_map\` with action "localization" for the scope target
76422
- 3. \`symbols\` and \`batch_symbols\` on key files identified by localization
76423
- 4. \`imports\` to trace dependency boundaries
76424
- 5. \`doc_scan\` if documentation coverage is relevant
76425
- 6. \`knowledge_recall\` with query matching the scope domain
76426
-
76427
- Produce a SCOPE MAP: list of files, modules, and interfaces within the audit boundary. Cap at 50 files total.
76428
-
76429
- #### STEP 3 — EXPLORER MISSIONS (Parallel Waves)
76430
- Dispatch explorer waves using parallel Task calls. Each wave contains up to \`max_explorers\` missions.
76431
-
76432
- **File caps per mission:**
76433
- - 8 files maximum per mission
76434
- - ~3500 total lines across all files in a mission
76435
- - Group files by import proximity (files that import each other go in the same mission)
76436
-
76437
- **Profile-based lane selection — each profile activates specific lanes:**
76438
-
76439
- | Lane | Template | standard | security | ux | architecture | full |
76440
- |------|----------|----------|----------|----|-------------|------|
76441
- | SCOPE_MAP | Map structure, exports, boundaries | ✓ | ✓ | ✓ | ✓ | ✓ |
76442
- | WIRING_DATAFLOW | Trace data flow, API contracts, state propagation | ✓ | ✓ | | ✓ | ✓ |
76443
- | RUNTIME_BEHAVIOR | Error handling, edge cases, lifecycle, async patterns | ✓ | | | ✓ | ✓ |
76444
- | UX_FLOW | User-facing behavior, accessibility, responsiveness | | | ✓ | | ✓ |
76445
- | SECURITY_TRUST | Auth boundaries, input validation, trust transitions | | ✓ | | | ✓ |
76446
- | TEST_COVERAGE | Coverage gaps, flaky tests, missing assertions | ✓ | | | | ✓ |
76447
- | PERFORMANCE_RELIABILITY | Resource leaks, N+1 queries, race conditions | | | | ✓ | ✓ |
76448
- | DOCS_CONFIG_DEPLOYMENT | Config consistency, docs accuracy, deployment drift | | | | | ✓ |
76449
-
76450
- Each explorer mission receives:
76451
- - Lane template name and description
76452
- - Assigned files (8 max, grouped by import proximity)
76453
- - The scope map context from Step 2
76454
- - Instruction: "You are performing a [LANE] audit. Report findings as candidate observations with severity (INFO/LOW/MEDIUM/HIGH/CRITICAL), location, and evidence."
76455
-
76456
- Explorer missions are dispatched in parallel waves. Wait for ALL missions in a wave to complete before dispatching the next wave.
76457
-
76458
- Explorers generate CANDIDATE FINDINGS only — they do NOT make verdicts. All findings are unverified until Step 5.
76459
-
76460
- #### STEP 4 — NORMALIZE CANDIDATES
76461
- 1. Collect all candidate findings from all explorer missions.
76462
- 2. Deduplicate: merge findings that reference the same location and issue.
76463
- 3. Assign DD-C001 through DD-CNNN identifiers to unique findings.
76464
- 4. Cap at 10 findings per shard (see Step 5 for sharding).
76465
- 5. Sort by severity (CRITICAL → HIGH → MEDIUM → LOW → INFO).
76466
-
76467
- #### STEP 5 — ALWAYS 2 PARALLEL REVIEWERS
76468
- Split the verified candidates into 2 shards of ≤10 candidates each. Dispatch 2 parallel \`{{AGENT_PREFIX}}reviewer\` calls.
76469
-
76470
- Each reviewer receives:
76471
- - Their shard of candidates (up to 10)
76472
- - The scope map context
76473
- - The original scope description
76474
- - Instruction: "Verify or reject each candidate finding. For each: verdict (VERIFIED / REJECTED / NEEDS_MORE_EVIDENCE), confidence (0-1), and brief reasoning."
76475
-
76476
- Reviewers MUST NOT suggest fixes — they verify findings only.
76477
-
76478
- #### STEP 5b — REVIEWER MERGE/DEDUP
76479
- After both reviewers return, perform a lightweight sync pass:
76480
- 1. Cross-reference findings between reviewers — flag correlations
76481
- 2. Deduplicate any findings both reviewers verified independently
76482
- 3. For NEEDS_MORE_EVIDENCE findings: if the other reviewer verified a related finding, merge
76483
- 4. Produce a unified findings list with verified/rejected status
76484
-
76485
- #### STEP 6 — CRITIC CHALLENGE (HIGH/CRITICAL only)
76486
- For verified findings rated HIGH or CRITICAL, dispatch sequential critic passes:
76487
-
76488
- **Pass 1 — False-positive / root-cause challenge:**
76489
- - \`{{AGENT_PREFIX}}critic\` receives each HIGH/CRITICAL finding
76490
- - Challenge: "Is this a false positive? Is the root cause correctly identified? Provide verdict: SURVIVES / DOWNGRADE / REJECT"
76491
- - Only findings that SURVIVE proceed to Pass 2
76492
-
76493
- **Pass 2 — Impact / severity challenge:**
76494
- - \`{{AGENT_PREFIX}}critic\` receives surviving findings
76495
- - Challenge: "Is the severity correctly rated? Could this be lower impact than claimed? Provide verdict: SURVIVES / DOWNGRADE / REJECT"
76496
- - Final severity is the critic's assessed severity
76497
-
76498
- CRITICAL: Do NOT challenge MEDIUM/LOW/INFO findings. Only HIGH and CRITICAL go through critic review.
76499
-
76500
- #### STEP 7 — FINAL REPORT
76501
- Assemble and present the audit report:
76502
-
76503
- 1. **Wiring Map**: Visual summary of the scope's module structure and data flow
76504
- 2. **Functionality Assessment**: High-level summary of what the scope does and how well
76505
- 3. **Verified Findings Table**: DD-ID, severity, location, description, evidence
76506
- 4. **Rejected Candidates**: Brief list with rejection reasons
76507
- 5. **Enhancements**: Non-blocking improvement suggestions
76508
- 6. **Recommended Implementation Phases**: If findings suggest follow-up work, outline phases
76509
- 7. **JSON Block** (when output=json): Structured machine-readable findings
76510
-
76511
- IMPORTANT CONSTRAINTS for MODE: DEEP_DIVE:
76512
- - Do NOT mutate source code under any circumstances
76718
+ Purpose: Read-only deep audit of the specified codebase scope using parallel explorer waves, always 2 parallel reviewers, and sequential critic challenge. This mode does NOT mutate source code, does NOT delegate to coder, and does NOT call declare_scope.
76719
+
76720
+ ACTION: Load skill \`file:.opencode/skills/deep-dive/SKILL.md\` immediately and follow its protocol.
76721
+
76722
+ HARD CONSTRAINTS (apply regardless of skill load success):
76513
76723
  - Do NOT delegate to coder
76514
76724
  - Do NOT call declare_scope
76725
+ - Do NOT mutate source code
76515
76726
  - Do NOT create or modify any files outside .swarm/
76516
76727
  - No final finding may appear in the report without reviewer verification
76517
76728
  - Explorers generate candidate findings only — reviewers verify or reject
@@ -84599,7 +84810,7 @@ __export(exports_project_context, {
84599
84810
  LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
84600
84811
  });
84601
84812
  import * as fs113 from "node:fs";
84602
- import * as path147 from "node:path";
84813
+ import * as path148 from "node:path";
84603
84814
  function detectFileExists2(directory, pattern) {
84604
84815
  if (pattern.includes("*") || pattern.includes("?")) {
84605
84816
  try {
@@ -84611,7 +84822,7 @@ function detectFileExists2(directory, pattern) {
84611
84822
  }
84612
84823
  }
84613
84824
  try {
84614
- fs113.accessSync(path147.join(directory, pattern));
84825
+ fs113.accessSync(path148.join(directory, pattern));
84615
84826
  return true;
84616
84827
  } catch {
84617
84828
  return false;
@@ -84620,7 +84831,7 @@ function detectFileExists2(directory, pattern) {
84620
84831
  function selectTestCommandFromScriptsTest(backend, directory) {
84621
84832
  let pkgRaw;
84622
84833
  try {
84623
- pkgRaw = fs113.readFileSync(path147.join(directory, "package.json"), "utf-8");
84834
+ pkgRaw = fs113.readFileSync(path148.join(directory, "package.json"), "utf-8");
84624
84835
  } catch {
84625
84836
  return null;
84626
84837
  }
@@ -84729,7 +84940,7 @@ var init_project_context = __esm(() => {
84729
84940
  init_package();
84730
84941
  init_agents2();
84731
84942
  init_critic();
84732
- import * as path148 from "node:path";
84943
+ import * as path149 from "node:path";
84733
84944
 
84734
84945
  // src/background/index.ts
84735
84946
  init_event_bus();
@@ -84992,6 +85203,10 @@ class PlanSyncWorker {
84992
85203
  const planMtimeMs = Math.floor(planStats.mtimeMs);
84993
85204
  const markerContent = fs37.readFileSync(markerPath, "utf8");
84994
85205
  const marker = JSON.parse(markerContent);
85206
+ if (marker.in_progress === true) {
85207
+ log("[PlanSyncWorker] Skipping unauthorized-write check - plan write in progress");
85208
+ return;
85209
+ }
84995
85210
  const markerTimestampMs = new Date(marker.timestamp).getTime();
84996
85211
  if (planMtimeMs > markerTimestampMs + 5000) {
84997
85212
  log("[PlanSyncWorker] WARNING: plan.json may have been written outside save_plan/savePlan - unauthorized direct write suspected", { planMtimeMs, markerTimestampMs });
@@ -97595,6 +97810,10 @@ var DEFAULT_MEMORY_CONFIG = {
97595
97810
  enabled: false,
97596
97811
  provider: "local-jsonl",
97597
97812
  storageDir: ".swarm/memory",
97813
+ sqlite: {
97814
+ path: ".swarm/memory/memory.db",
97815
+ busyTimeoutMs: 5000
97816
+ },
97598
97817
  recall: {
97599
97818
  defaultMaxItems: 8,
97600
97819
  defaultTokenBudget: 1200,
@@ -97634,6 +97853,10 @@ function resolveMemoryConfig(input) {
97634
97853
  return {
97635
97854
  ...DEFAULT_MEMORY_CONFIG,
97636
97855
  ...input ?? {},
97856
+ sqlite: {
97857
+ ...DEFAULT_MEMORY_CONFIG.sqlite,
97858
+ ...input?.sqlite ?? {}
97859
+ },
97637
97860
  recall: {
97638
97861
  ...DEFAULT_MEMORY_CONFIG.recall,
97639
97862
  ...input?.recall ?? {},
@@ -97671,7 +97894,7 @@ class MemoryDisabledError extends Error {
97671
97894
  // src/memory/gateway.ts
97672
97895
  import { createHash as createHash9 } from "node:crypto";
97673
97896
  import { existsSync as existsSync50, readFileSync as readFileSync39 } from "node:fs";
97674
- import * as path96 from "node:path";
97897
+ import * as path97 from "node:path";
97675
97898
 
97676
97899
  // src/memory/local-jsonl-provider.ts
97677
97900
  init_utils2();
@@ -98373,6 +98596,367 @@ function toRecallBundle(input) {
98373
98596
  };
98374
98597
  }
98375
98598
 
98599
+ // src/memory/sqlite-provider.ts
98600
+ init_utils2();
98601
+ import { randomUUID as randomUUID9 } from "node:crypto";
98602
+ import { mkdirSync as mkdirSync24 } from "node:fs";
98603
+ import { createRequire as createRequire3 } from "node:module";
98604
+ import * as path96 from "node:path";
98605
+ var _DatabaseCtor2 = null;
98606
+ function loadDatabaseCtor2() {
98607
+ if (_DatabaseCtor2)
98608
+ return _DatabaseCtor2;
98609
+ const req = createRequire3(import.meta.url);
98610
+ _DatabaseCtor2 = req("bun:sqlite").Database;
98611
+ return _DatabaseCtor2;
98612
+ }
98613
+ var MIGRATIONS2 = [
98614
+ {
98615
+ version: 1,
98616
+ name: "create_memory_provider_tables",
98617
+ sql: `
98618
+ CREATE TABLE IF NOT EXISTS memory_items (
98619
+ id TEXT PRIMARY KEY,
98620
+ scope_key TEXT NOT NULL,
98621
+ kind TEXT NOT NULL,
98622
+ updated_at TEXT NOT NULL,
98623
+ expires_at TEXT,
98624
+ superseded_by TEXT,
98625
+ deleted INTEGER NOT NULL DEFAULT 0,
98626
+ record_json TEXT NOT NULL
98627
+ );
98628
+ CREATE INDEX IF NOT EXISTS idx_memory_items_scope_kind
98629
+ ON memory_items(scope_key, kind);
98630
+ CREATE INDEX IF NOT EXISTS idx_memory_items_updated_at
98631
+ ON memory_items(updated_at);
98632
+
98633
+ CREATE TABLE IF NOT EXISTS memory_proposals (
98634
+ id TEXT PRIMARY KEY,
98635
+ status TEXT NOT NULL,
98636
+ created_at TEXT NOT NULL,
98637
+ proposal_json TEXT NOT NULL
98638
+ );
98639
+ CREATE INDEX IF NOT EXISTS idx_memory_proposals_status_created
98640
+ ON memory_proposals(status, created_at);
98641
+
98642
+ CREATE TABLE IF NOT EXISTS memory_events (
98643
+ id TEXT PRIMARY KEY,
98644
+ operation TEXT NOT NULL,
98645
+ target_id TEXT NOT NULL,
98646
+ reason TEXT,
98647
+ timestamp TEXT NOT NULL,
98648
+ event_json TEXT
98649
+ );
98650
+
98651
+ CREATE TABLE IF NOT EXISTS memory_recall_usage (
98652
+ id TEXT PRIMARY KEY,
98653
+ bundle_id TEXT NOT NULL,
98654
+ timestamp TEXT NOT NULL,
98655
+ usage_json TEXT NOT NULL
98656
+ );
98657
+ CREATE INDEX IF NOT EXISTS idx_memory_recall_usage_bundle
98658
+ ON memory_recall_usage(bundle_id);
98659
+ `
98660
+ }
98661
+ ];
98662
+
98663
+ class SQLiteMemoryProvider {
98664
+ name = "sqlite";
98665
+ rootDirectory;
98666
+ config;
98667
+ initialized = false;
98668
+ db = null;
98669
+ memories = new Map;
98670
+ proposals = new Map;
98671
+ constructor(rootDirectory, config3 = {}) {
98672
+ this.rootDirectory = rootDirectory;
98673
+ this.config = {
98674
+ ...DEFAULT_MEMORY_CONFIG,
98675
+ ...config3,
98676
+ sqlite: {
98677
+ ...DEFAULT_MEMORY_CONFIG.sqlite,
98678
+ ...config3.sqlite ?? {}
98679
+ },
98680
+ recall: {
98681
+ ...DEFAULT_MEMORY_CONFIG.recall,
98682
+ ...config3.recall ?? {},
98683
+ injection: {
98684
+ ...DEFAULT_MEMORY_CONFIG.recall.injection,
98685
+ ...config3.recall?.injection ?? {}
98686
+ }
98687
+ },
98688
+ writes: {
98689
+ ...DEFAULT_MEMORY_CONFIG.writes,
98690
+ ...config3.writes ?? {}
98691
+ },
98692
+ redaction: {
98693
+ ...DEFAULT_MEMORY_CONFIG.redaction,
98694
+ ...config3.redaction ?? {}
98695
+ }
98696
+ };
98697
+ }
98698
+ databasePath() {
98699
+ const relativePath = this.config.sqlite.path.replace(/^\.swarm[/\\]?/, "");
98700
+ return validateSwarmPath(this.rootDirectory, relativePath);
98701
+ }
98702
+ async initialize() {
98703
+ if (this.initialized)
98704
+ return;
98705
+ const dbPath = this.databasePath();
98706
+ mkdirSync24(path96.dirname(dbPath), { recursive: true });
98707
+ const Db = loadDatabaseCtor2();
98708
+ this.db = new Db(dbPath);
98709
+ this.db.run("PRAGMA journal_mode = WAL;");
98710
+ this.db.run("PRAGMA synchronous = NORMAL;");
98711
+ const busyTimeoutMs = Math.min(60000, Math.max(0, Math.trunc(this.config.sqlite.busyTimeoutMs)));
98712
+ this.db.run(`PRAGMA busy_timeout = ${busyTimeoutMs};`);
98713
+ this.db.run("PRAGMA foreign_keys = ON;");
98714
+ this.runMigrations();
98715
+ const memoryLoad = this.loadMemories();
98716
+ const proposalLoad = this.loadProposals();
98717
+ this.memories = new Map(memoryLoad.records.map((record3) => [record3.id, record3]));
98718
+ this.proposals = new Map(proposalLoad.records.map((proposal) => [proposal.id, proposal]));
98719
+ this.initialized = true;
98720
+ if (memoryLoad.invalidCount > 0) {
98721
+ await this.event("invalid_load", "memory_items", `${memoryLoad.invalidCount} invalid SQLite memory row(s) skipped`);
98722
+ }
98723
+ if (proposalLoad.invalidCount > 0) {
98724
+ await this.event("invalid_load", "memory_proposals", `${proposalLoad.invalidCount} invalid SQLite proposal row(s) skipped`);
98725
+ }
98726
+ }
98727
+ async upsert(record3) {
98728
+ await this.initialize();
98729
+ const existing = this.memories.get(record3.id);
98730
+ if (existing?.metadata.deleted === true) {
98731
+ throw new MemoryValidationError("memory is tombstoned and cannot be upserted");
98732
+ }
98733
+ const next = validateMemoryRecordRules({
98734
+ ...record3,
98735
+ createdAt: existing?.createdAt ?? record3.createdAt
98736
+ }, { rejectDurableSecrets: this.config.redaction.rejectDurableSecrets });
98737
+ this.memories.set(next.id, next);
98738
+ this.writeMemory(next);
98739
+ await this.event("upsert", next.id);
98740
+ return next;
98741
+ }
98742
+ async get(id) {
98743
+ await this.initialize();
98744
+ return this.memories.get(id) ?? null;
98745
+ }
98746
+ async delete(id, reason) {
98747
+ await this.initialize();
98748
+ const existing = this.memories.get(id);
98749
+ if (!existing)
98750
+ return;
98751
+ if (this.config.hardDelete) {
98752
+ this.memories.delete(id);
98753
+ this.requireDb().run("DELETE FROM memory_items WHERE id = ?", [id]);
98754
+ } else {
98755
+ const tombstone = {
98756
+ ...existing,
98757
+ updatedAt: new Date().toISOString(),
98758
+ metadata: { ...existing.metadata, deleted: true, deleteReason: reason }
98759
+ };
98760
+ this.memories.set(id, tombstone);
98761
+ this.writeMemory(tombstone);
98762
+ }
98763
+ await this.event("delete", id, reason);
98764
+ }
98765
+ async recall(request) {
98766
+ return (await this.recallWithDiagnostics(request)).items;
98767
+ }
98768
+ async recallWithDiagnostics(request) {
98769
+ await this.initialize();
98770
+ const records = await this.list({
98771
+ scopes: request.scopes,
98772
+ kinds: request.kinds,
98773
+ includeExpired: request.includeExpired
98774
+ });
98775
+ const result = scoreMemoryRecordsWithDiagnostics(records, request);
98776
+ return {
98777
+ items: result.items.slice(0, request.maxItems),
98778
+ diagnostics: {
98779
+ ...result.diagnostics,
98780
+ returnedCount: Math.min(result.diagnostics.returnedCount, request.maxItems)
98781
+ }
98782
+ };
98783
+ }
98784
+ async recordRecallUsage(event) {
98785
+ await this.initialize();
98786
+ this.requireDb().run(`INSERT INTO memory_recall_usage (
98787
+ id,
98788
+ bundle_id,
98789
+ timestamp,
98790
+ usage_json
98791
+ ) VALUES (?, ?, ?, ?)`, [randomUUID9(), event.bundleId, event.timestamp, JSON.stringify(event)]);
98792
+ await this.event("recall", event.bundleId, JSON.stringify(event));
98793
+ }
98794
+ async list(filter = {}) {
98795
+ await this.initialize();
98796
+ let records = Array.from(this.memories.values());
98797
+ if (filter.scopes && filter.scopes.length > 0) {
98798
+ records = records.filter((record3) => scopeAllowed(record3.scope, filter.scopes ?? []));
98799
+ }
98800
+ if (filter.kinds && filter.kinds.length > 0) {
98801
+ records = records.filter((record3) => filter.kinds?.includes(record3.kind));
98802
+ }
98803
+ if (!filter.includeExpired) {
98804
+ const now = Date.now();
98805
+ records = records.filter((record3) => {
98806
+ if (!record3.expiresAt)
98807
+ return true;
98808
+ const expires = Date.parse(record3.expiresAt);
98809
+ return !Number.isFinite(expires) || expires > now;
98810
+ });
98811
+ }
98812
+ records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
98813
+ records.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
98814
+ return records.slice(0, filter.limit ?? records.length);
98815
+ }
98816
+ async createProposal(proposal) {
98817
+ await this.initialize();
98818
+ const next = validateMemoryProposal(proposal);
98819
+ if (next.proposedRecord) {
98820
+ validateMemoryRecordRules(next.proposedRecord, {
98821
+ rejectDurableSecrets: this.config.redaction.rejectDurableSecrets
98822
+ });
98823
+ }
98824
+ this.proposals.set(next.id, next);
98825
+ this.requireDb().run(`INSERT OR REPLACE INTO memory_proposals (
98826
+ id,
98827
+ status,
98828
+ created_at,
98829
+ proposal_json
98830
+ ) VALUES (?, ?, ?, ?)`, [next.id, next.status, next.createdAt, JSON.stringify(next)]);
98831
+ await this.event("proposal", next.id);
98832
+ return next;
98833
+ }
98834
+ async listProposals(filter = {}) {
98835
+ await this.initialize();
98836
+ let proposals = Array.from(this.proposals.values());
98837
+ if (filter.status) {
98838
+ proposals = proposals.filter((proposal) => proposal.status === filter.status);
98839
+ }
98840
+ proposals.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
98841
+ return proposals.slice(0, filter.limit ?? proposals.length);
98842
+ }
98843
+ close() {
98844
+ if (!this.db)
98845
+ return;
98846
+ this.db.close();
98847
+ this.db = null;
98848
+ this.initialized = false;
98849
+ }
98850
+ runMigrations() {
98851
+ const db = this.requireDb();
98852
+ db.run(`CREATE TABLE IF NOT EXISTS schema_migrations (
98853
+ version INTEGER PRIMARY KEY,
98854
+ name TEXT NOT NULL,
98855
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
98856
+ )`);
98857
+ const row = db.query("SELECT MAX(version) as version FROM schema_migrations").get();
98858
+ const currentVersion = row?.version ?? 0;
98859
+ for (const migration of MIGRATIONS2) {
98860
+ if (migration.version <= currentVersion)
98861
+ continue;
98862
+ const apply = db.transaction(() => {
98863
+ for (const statement of splitSql(migration.sql)) {
98864
+ db.run(statement);
98865
+ }
98866
+ db.run("INSERT INTO schema_migrations (version, name) VALUES (?, ?)", [
98867
+ migration.version,
98868
+ migration.name
98869
+ ]);
98870
+ this.insertEvent("migration", String(migration.version), migration.name);
98871
+ });
98872
+ apply();
98873
+ }
98874
+ }
98875
+ loadMemories() {
98876
+ const rows = this.requireDb().query("SELECT id, record_json FROM memory_items ORDER BY updated_at ASC").all();
98877
+ const records = [];
98878
+ let invalidCount = 0;
98879
+ for (const row of rows) {
98880
+ try {
98881
+ records.push(validateMemoryRecordRules(JSON.parse(row.record_json), {
98882
+ rejectDurableSecrets: this.config.redaction.rejectDurableSecrets
98883
+ }));
98884
+ } catch {
98885
+ invalidCount++;
98886
+ }
98887
+ }
98888
+ return { records, invalidCount };
98889
+ }
98890
+ loadProposals() {
98891
+ const rows = this.requireDb().query("SELECT id, proposal_json FROM memory_proposals ORDER BY created_at ASC").all();
98892
+ const records = [];
98893
+ let invalidCount = 0;
98894
+ for (const row of rows) {
98895
+ try {
98896
+ const proposal = validateMemoryProposal(JSON.parse(row.proposal_json));
98897
+ if (proposal.proposedRecord) {
98898
+ validateMemoryRecordRules(proposal.proposedRecord, {
98899
+ rejectDurableSecrets: this.config.redaction.rejectDurableSecrets
98900
+ });
98901
+ }
98902
+ records.push(proposal);
98903
+ } catch {
98904
+ invalidCount++;
98905
+ }
98906
+ }
98907
+ return { records, invalidCount };
98908
+ }
98909
+ writeMemory(record3) {
98910
+ this.requireDb().run(`INSERT OR REPLACE INTO memory_items (
98911
+ id,
98912
+ scope_key,
98913
+ kind,
98914
+ updated_at,
98915
+ expires_at,
98916
+ superseded_by,
98917
+ deleted,
98918
+ record_json
98919
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
98920
+ record3.id,
98921
+ JSON.stringify(record3.scope),
98922
+ record3.kind,
98923
+ record3.updatedAt,
98924
+ record3.expiresAt ?? null,
98925
+ record3.supersededBy ?? null,
98926
+ record3.metadata.deleted === true ? 1 : 0,
98927
+ JSON.stringify(record3)
98928
+ ]);
98929
+ }
98930
+ async event(operation, targetId, reason) {
98931
+ this.insertEvent(operation, targetId, reason);
98932
+ }
98933
+ insertEvent(operation, targetId, reason) {
98934
+ this.requireDb().run(`INSERT INTO memory_events (
98935
+ id,
98936
+ operation,
98937
+ target_id,
98938
+ reason,
98939
+ timestamp,
98940
+ event_json
98941
+ ) VALUES (?, ?, ?, ?, ?, ?)`, [
98942
+ randomUUID9(),
98943
+ operation,
98944
+ targetId,
98945
+ reason ?? null,
98946
+ new Date().toISOString(),
98947
+ reason ? JSON.stringify({ reason }) : null
98948
+ ]);
98949
+ }
98950
+ requireDb() {
98951
+ if (!this.db)
98952
+ throw new Error("SQLite memory provider is not initialized");
98953
+ return this.db;
98954
+ }
98955
+ }
98956
+ function splitSql(sql) {
98957
+ return sql.split(";").map((statement) => statement.trim()).filter(Boolean);
98958
+ }
98959
+
98376
98960
  // src/memory/gateway.ts
98377
98961
  class MemoryGateway {
98378
98962
  context;
@@ -98382,16 +98966,19 @@ class MemoryGateway {
98382
98966
  constructor(context, options = {}) {
98383
98967
  this.context = context;
98384
98968
  this.config = resolveMemoryConfig(options.config ?? DEFAULT_MEMORY_CONFIG);
98385
- this.provider = options.provider ?? new LocalJsonlMemoryProvider(context.directory, this.config);
98969
+ this.provider = options.provider ?? createConfiguredMemoryProvider(context.directory, this.config);
98386
98970
  this.now = options.now ?? (() => new Date);
98387
98971
  }
98388
98972
  isEnabled() {
98389
98973
  return this.config.enabled;
98390
98974
  }
98975
+ async dispose() {
98976
+ await this.provider.close?.();
98977
+ }
98391
98978
  deriveAllowedScopes() {
98392
- const resolvedRoot = path96.resolve(this.context.directory);
98393
- const repoId = createStableId(readGitRemoteUrl(resolvedRoot) ?? path96.basename(resolvedRoot));
98394
- const workspaceId = createStableId(path96.dirname(resolvedRoot));
98979
+ const resolvedRoot = path97.resolve(this.context.directory);
98980
+ const repoId = createStableId(readGitRemoteUrl(resolvedRoot) ?? path97.basename(resolvedRoot));
98981
+ const workspaceId = createStableId(path97.dirname(resolvedRoot));
98395
98982
  const scopes = [
98396
98983
  { type: "workspace", workspaceId },
98397
98984
  {
@@ -98591,6 +99178,12 @@ class MemoryGateway {
98591
99178
  function createMemoryGateway(context, options = {}) {
98592
99179
  return new MemoryGateway(context, options);
98593
99180
  }
99181
+ function createConfiguredMemoryProvider(directory, config3) {
99182
+ if (config3.provider === "sqlite") {
99183
+ return new SQLiteMemoryProvider(directory, config3);
99184
+ }
99185
+ return new LocalJsonlMemoryProvider(directory, config3);
99186
+ }
98594
99187
  function sourceFromEvidence(evidenceRefs, context) {
98595
99188
  const first = evidenceRefs[0];
98596
99189
  if (!first) {
@@ -98612,7 +99205,7 @@ var gitRemoteUrlCache = new Map;
98612
99205
  function readGitRemoteUrl(directory) {
98613
99206
  if (gitRemoteUrlCache.has(directory))
98614
99207
  return gitRemoteUrlCache.get(directory);
98615
- const gitConfigPath = path96.join(directory, ".git", "config");
99208
+ const gitConfigPath = path97.join(directory, ".git", "config");
98616
99209
  if (!existsSync50(gitConfigPath)) {
98617
99210
  gitRemoteUrlCache.set(directory, undefined);
98618
99211
  return;
@@ -98660,7 +99253,7 @@ function scopeKey(scope) {
98660
99253
  workspaceId: scope.workspaceId,
98661
99254
  projectId: scope.projectId,
98662
99255
  repoId: scope.repoId,
98663
- repoRoot: scope.repoRoot ? path96.resolve(scope.repoRoot) : undefined,
99256
+ repoRoot: scope.repoRoot ? path97.resolve(scope.repoRoot) : undefined,
98664
99257
  runId: scope.runId,
98665
99258
  agentId: scope.agentId
98666
99259
  });
@@ -98907,12 +99500,12 @@ function buildScopesFromInput(input) {
98907
99500
  // src/memory/run-log.ts
98908
99501
  init_utils2();
98909
99502
  import { appendFile as appendFile12, mkdir as mkdir19 } from "node:fs/promises";
98910
- import * as path97 from "node:path";
99503
+ import * as path98 from "node:path";
98911
99504
  async function appendMemoryRunLog(directory, runId, event) {
98912
99505
  const safeRunId = sanitizeRunId(runId);
98913
- const relativePath = path97.join("runs", safeRunId, "memory.jsonl");
99506
+ const relativePath = path98.join("runs", safeRunId, "memory.jsonl");
98914
99507
  const filePath = validateSwarmPath(directory, relativePath);
98915
- await mkdir19(path97.dirname(filePath), { recursive: true });
99508
+ await mkdir19(path98.dirname(filePath), { recursive: true });
98916
99509
  await appendFile12(filePath, `${JSON.stringify({
98917
99510
  ...event,
98918
99511
  runId: safeRunId,
@@ -99690,16 +100283,16 @@ init_telemetry();
99690
100283
 
99691
100284
  // src/prm/replay.ts
99692
100285
  import { promises as fs68 } from "node:fs";
99693
- import path98 from "node:path";
100286
+ import path99 from "node:path";
99694
100287
  function isPathSafe2(targetPath, basePath) {
99695
- const resolvedTarget = path98.resolve(targetPath);
99696
- const resolvedBase = path98.resolve(basePath);
99697
- const rel = path98.relative(resolvedBase, resolvedTarget);
99698
- return !rel.startsWith("..") && !path98.isAbsolute(rel);
100288
+ const resolvedTarget = path99.resolve(targetPath);
100289
+ const resolvedBase = path99.resolve(basePath);
100290
+ const rel = path99.relative(resolvedBase, resolvedTarget);
100291
+ return !rel.startsWith("..") && !path99.isAbsolute(rel);
99699
100292
  }
99700
100293
  function isWithinReplaysDir(targetPath) {
99701
- const resolved = path98.resolve(targetPath);
99702
- const parts2 = resolved.split(path98.sep);
100294
+ const resolved = path99.resolve(targetPath);
100295
+ const parts2 = resolved.split(path99.sep);
99703
100296
  for (let i2 = 0;i2 < parts2.length - 1; i2++) {
99704
100297
  if (parts2[i2] === ".swarm" && parts2[i2 + 1] === "replays") {
99705
100298
  return true;
@@ -99712,10 +100305,10 @@ function sanitizeFilename(input) {
99712
100305
  }
99713
100306
  async function startReplayRecording(sessionID, directory) {
99714
100307
  try {
99715
- const replayDir = path98.join(directory, ".swarm", "replays");
100308
+ const replayDir = path99.join(directory, ".swarm", "replays");
99716
100309
  const safeSessionID = sanitizeFilename(sessionID);
99717
100310
  const filename = `${safeSessionID}-${Date.now()}.jsonl`;
99718
- const filepath = path98.join(replayDir, filename);
100311
+ const filepath = path99.join(replayDir, filename);
99719
100312
  if (!isPathSafe2(filepath, replayDir)) {
99720
100313
  console.warn(`[replay] Invalid path detected - path traversal attempt blocked for session ${sessionID}`);
99721
100314
  return null;
@@ -100096,7 +100689,7 @@ init_telemetry();
100096
100689
  init_dist();
100097
100690
  init_create_tool();
100098
100691
  import * as fs69 from "node:fs";
100099
- import * as path99 from "node:path";
100692
+ import * as path100 from "node:path";
100100
100693
  init_path_security();
100101
100694
  var WINDOWS_RESERVED_NAMES2 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
100102
100695
  function containsWindowsAttacks2(str) {
@@ -100113,14 +100706,14 @@ function containsWindowsAttacks2(str) {
100113
100706
  }
100114
100707
  function isPathInWorkspace2(filePath, workspace) {
100115
100708
  try {
100116
- const resolvedPath = path99.resolve(workspace, filePath);
100709
+ const resolvedPath = path100.resolve(workspace, filePath);
100117
100710
  if (!fs69.existsSync(resolvedPath)) {
100118
100711
  return true;
100119
100712
  }
100120
100713
  const realWorkspace = fs69.realpathSync(workspace);
100121
100714
  const realResolvedPath = fs69.realpathSync(resolvedPath);
100122
- const relativePath = path99.relative(realWorkspace, realResolvedPath);
100123
- if (relativePath.startsWith("..") || path99.isAbsolute(relativePath)) {
100715
+ const relativePath = path100.relative(realWorkspace, realResolvedPath);
100716
+ if (relativePath.startsWith("..") || path100.isAbsolute(relativePath)) {
100124
100717
  return false;
100125
100718
  }
100126
100719
  return true;
@@ -100129,7 +100722,7 @@ function isPathInWorkspace2(filePath, workspace) {
100129
100722
  }
100130
100723
  }
100131
100724
  function processFile2(file3, cwd, exportedOnly) {
100132
- const ext = path99.extname(file3);
100725
+ const ext = path100.extname(file3);
100133
100726
  if (containsControlChars(file3)) {
100134
100727
  return {
100135
100728
  file: file3,
@@ -100162,7 +100755,7 @@ function processFile2(file3, cwd, exportedOnly) {
100162
100755
  errorType: "path-outside-workspace"
100163
100756
  };
100164
100757
  }
100165
- const fullPath = path99.join(cwd, file3);
100758
+ const fullPath = path100.join(cwd, file3);
100166
100759
  if (!fs69.existsSync(fullPath)) {
100167
100760
  return {
100168
100761
  file: file3,
@@ -100454,15 +101047,15 @@ init_task_id();
100454
101047
  init_create_tool();
100455
101048
  init_resolve_working_directory();
100456
101049
  import * as fs70 from "node:fs";
100457
- import * as path100 from "node:path";
101050
+ import * as path101 from "node:path";
100458
101051
  var EVIDENCE_DIR = ".swarm/evidence";
100459
101052
  function isValidTaskId3(taskId) {
100460
101053
  return isStrictTaskId(taskId);
100461
101054
  }
100462
101055
  function isPathWithinSwarm(filePath, workspaceRoot) {
100463
- const normalizedWorkspace = path100.resolve(workspaceRoot);
100464
- const swarmPath = path100.join(normalizedWorkspace, ".swarm", "evidence");
100465
- const normalizedPath = path100.resolve(filePath);
101056
+ const normalizedWorkspace = path101.resolve(workspaceRoot);
101057
+ const swarmPath = path101.join(normalizedWorkspace, ".swarm", "evidence");
101058
+ const normalizedPath = path101.resolve(filePath);
100466
101059
  return normalizedPath.startsWith(swarmPath);
100467
101060
  }
100468
101061
  function readEvidenceFile(evidencePath) {
@@ -100543,7 +101136,7 @@ var check_gate_status = createSwarmTool({
100543
101136
  };
100544
101137
  return JSON.stringify(errorResult, null, 2);
100545
101138
  }
100546
- const evidencePath = path100.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
101139
+ const evidencePath = path101.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
100547
101140
  if (!isPathWithinSwarm(evidencePath, directory)) {
100548
101141
  const errorResult = {
100549
101142
  taskId: taskIdInput,
@@ -100640,7 +101233,7 @@ init_state();
100640
101233
  init_create_tool();
100641
101234
  init_resolve_working_directory();
100642
101235
  import * as fs71 from "node:fs";
100643
- import * as path101 from "node:path";
101236
+ import * as path102 from "node:path";
100644
101237
  function extractMatches(regex, text) {
100645
101238
  return Array.from(text.matchAll(regex));
100646
101239
  }
@@ -100792,10 +101385,10 @@ async function executeCompletionVerify(args2, directory) {
100792
101385
  let hasFileReadFailure = false;
100793
101386
  for (const filePath of fileTargets) {
100794
101387
  const normalizedPath = filePath.replace(/\\/g, "/");
100795
- const resolvedPath = path101.resolve(directory, normalizedPath);
100796
- const projectRoot = path101.resolve(directory);
100797
- const relative21 = path101.relative(projectRoot, resolvedPath);
100798
- const withinProject = relative21 === "" || !relative21.startsWith("..") && !path101.isAbsolute(relative21);
101388
+ const resolvedPath = path102.resolve(directory, normalizedPath);
101389
+ const projectRoot = path102.resolve(directory);
101390
+ const relative21 = path102.relative(projectRoot, resolvedPath);
101391
+ const withinProject = relative21 === "" || !relative21.startsWith("..") && !path102.isAbsolute(relative21);
100799
101392
  if (!withinProject) {
100800
101393
  blockedTasks.push({
100801
101394
  task_id: task.id,
@@ -100850,8 +101443,8 @@ async function executeCompletionVerify(args2, directory) {
100850
101443
  blockedTasks
100851
101444
  };
100852
101445
  try {
100853
- const evidenceDir = path101.join(directory, ".swarm", "evidence", `${phase}`);
100854
- const evidencePath = path101.join(evidenceDir, "completion-verify.json");
101446
+ const evidenceDir = path102.join(directory, ".swarm", "evidence", `${phase}`);
101447
+ const evidencePath = path102.join(evidenceDir, "completion-verify.json");
100855
101448
  fs71.mkdirSync(evidenceDir, { recursive: true });
100856
101449
  const evidenceBundle = {
100857
101450
  schema_version: "1.0.0",
@@ -100928,11 +101521,11 @@ var completion_verify = createSwarmTool({
100928
101521
  // src/tools/complexity-hotspots.ts
100929
101522
  init_zod();
100930
101523
  import * as fs73 from "node:fs";
100931
- import * as path103 from "node:path";
101524
+ import * as path104 from "node:path";
100932
101525
 
100933
101526
  // src/quality/metrics.ts
100934
101527
  import * as fs72 from "node:fs";
100935
- import * as path102 from "node:path";
101528
+ import * as path103 from "node:path";
100936
101529
  var MAX_FILE_SIZE_BYTES4 = 256 * 1024;
100937
101530
  var MIN_DUPLICATION_LINES = 10;
100938
101531
  function estimateCyclomaticComplexity(content) {
@@ -100984,7 +101577,7 @@ async function computeComplexityDelta(files, workingDir) {
100984
101577
  let totalComplexity = 0;
100985
101578
  const analyzedFiles = [];
100986
101579
  for (const file3 of files) {
100987
- const fullPath = path102.isAbsolute(file3) ? file3 : path102.join(workingDir, file3);
101580
+ const fullPath = path103.isAbsolute(file3) ? file3 : path103.join(workingDir, file3);
100988
101581
  if (!fs72.existsSync(fullPath)) {
100989
101582
  continue;
100990
101583
  }
@@ -101107,7 +101700,7 @@ function countGoExports(content) {
101107
101700
  function getExportCountForFile(filePath) {
101108
101701
  try {
101109
101702
  const content = fs72.readFileSync(filePath, "utf-8");
101110
- const ext = path102.extname(filePath).toLowerCase();
101703
+ const ext = path103.extname(filePath).toLowerCase();
101111
101704
  switch (ext) {
101112
101705
  case ".ts":
101113
101706
  case ".tsx":
@@ -101133,7 +101726,7 @@ async function computePublicApiDelta(files, workingDir) {
101133
101726
  let totalExports = 0;
101134
101727
  const analyzedFiles = [];
101135
101728
  for (const file3 of files) {
101136
- const fullPath = path102.isAbsolute(file3) ? file3 : path102.join(workingDir, file3);
101729
+ const fullPath = path103.isAbsolute(file3) ? file3 : path103.join(workingDir, file3);
101137
101730
  if (!fs72.existsSync(fullPath)) {
101138
101731
  continue;
101139
101732
  }
@@ -101167,7 +101760,7 @@ async function computeDuplicationRatio(files, workingDir) {
101167
101760
  let duplicateLines = 0;
101168
101761
  const analyzedFiles = [];
101169
101762
  for (const file3 of files) {
101170
- const fullPath = path102.isAbsolute(file3) ? file3 : path102.join(workingDir, file3);
101763
+ const fullPath = path103.isAbsolute(file3) ? file3 : path103.join(workingDir, file3);
101171
101764
  if (!fs72.existsSync(fullPath)) {
101172
101765
  continue;
101173
101766
  }
@@ -101200,8 +101793,8 @@ function countCodeLines(content) {
101200
101793
  return lines.length;
101201
101794
  }
101202
101795
  function isTestFile(filePath) {
101203
- const basename15 = path102.basename(filePath);
101204
- const _ext = path102.extname(filePath).toLowerCase();
101796
+ const basename15 = path103.basename(filePath);
101797
+ const _ext = path103.extname(filePath).toLowerCase();
101205
101798
  const testPatterns = [
101206
101799
  ".test.",
101207
101800
  ".spec.",
@@ -101282,8 +101875,8 @@ function matchGlobSegment(globSegments, pathSegments) {
101282
101875
  }
101283
101876
  return gIndex === globSegments.length && pIndex === pathSegments.length;
101284
101877
  }
101285
- function matchesGlobSegment(path103, glob) {
101286
- const normalizedPath = path103.replace(/\\/g, "/");
101878
+ function matchesGlobSegment(path104, glob) {
101879
+ const normalizedPath = path104.replace(/\\/g, "/");
101287
101880
  const normalizedGlob = glob.replace(/\\/g, "/");
101288
101881
  if (normalizedPath.includes("//")) {
101289
101882
  return false;
@@ -101314,8 +101907,8 @@ function simpleGlobToRegex2(glob) {
101314
101907
  function hasGlobstar(glob) {
101315
101908
  return glob.includes("**");
101316
101909
  }
101317
- function globMatches(path103, glob) {
101318
- const normalizedPath = path103.replace(/\\/g, "/");
101910
+ function globMatches(path104, glob) {
101911
+ const normalizedPath = path104.replace(/\\/g, "/");
101319
101912
  if (!glob || glob === "") {
101320
101913
  if (normalizedPath.includes("//")) {
101321
101914
  return false;
@@ -101351,7 +101944,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
101351
101944
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
101352
101945
  let testLines = 0;
101353
101946
  let codeLines = 0;
101354
- const srcDir = path102.join(workingDir, "src");
101947
+ const srcDir = path103.join(workingDir, "src");
101355
101948
  if (fs72.existsSync(srcDir)) {
101356
101949
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
101357
101950
  codeLines += lines;
@@ -101359,14 +101952,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
101359
101952
  }
101360
101953
  const possibleSrcDirs = ["lib", "app", "source", "core"];
101361
101954
  for (const dir of possibleSrcDirs) {
101362
- const dirPath = path102.join(workingDir, dir);
101955
+ const dirPath = path103.join(workingDir, dir);
101363
101956
  if (fs72.existsSync(dirPath)) {
101364
101957
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
101365
101958
  codeLines += lines;
101366
101959
  });
101367
101960
  }
101368
101961
  }
101369
- const testsDir = path102.join(workingDir, "tests");
101962
+ const testsDir = path103.join(workingDir, "tests");
101370
101963
  if (fs72.existsSync(testsDir)) {
101371
101964
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
101372
101965
  testLines += lines;
@@ -101374,7 +101967,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
101374
101967
  }
101375
101968
  const possibleTestDirs = ["test", "__tests__", "specs"];
101376
101969
  for (const dir of possibleTestDirs) {
101377
- const dirPath = path102.join(workingDir, dir);
101970
+ const dirPath = path103.join(workingDir, dir);
101378
101971
  if (fs72.existsSync(dirPath) && dirPath !== testsDir) {
101379
101972
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
101380
101973
  testLines += lines;
@@ -101389,7 +101982,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
101389
101982
  try {
101390
101983
  const entries = fs72.readdirSync(dirPath, { withFileTypes: true });
101391
101984
  for (const entry of entries) {
101392
- const fullPath = path102.join(dirPath, entry.name);
101985
+ const fullPath = path103.join(dirPath, entry.name);
101393
101986
  if (entry.isDirectory()) {
101394
101987
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
101395
101988
  continue;
@@ -101397,7 +101990,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
101397
101990
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
101398
101991
  } else if (entry.isFile()) {
101399
101992
  const relativePath = fullPath.replace(`${dirPath}/`, "");
101400
- const ext = path102.extname(entry.name).toLowerCase();
101993
+ const ext = path103.extname(entry.name).toLowerCase();
101401
101994
  const validExts = [
101402
101995
  ".ts",
101403
101996
  ".tsx",
@@ -101648,7 +102241,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
101648
102241
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
101649
102242
  const filteredChurn = new Map;
101650
102243
  for (const [file3, count] of churnMap) {
101651
- const ext = path103.extname(file3).toLowerCase();
102244
+ const ext = path104.extname(file3).toLowerCase();
101652
102245
  if (extSet.has(ext)) {
101653
102246
  filteredChurn.set(file3, count);
101654
102247
  }
@@ -101659,7 +102252,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
101659
102252
  for (const [file3, churnCount] of filteredChurn) {
101660
102253
  let fullPath = file3;
101661
102254
  if (!fs73.existsSync(fullPath)) {
101662
- fullPath = path103.join(cwd, file3);
102255
+ fullPath = path104.join(cwd, file3);
101663
102256
  }
101664
102257
  const complexity = getComplexityForFile2(fullPath);
101665
102258
  if (complexity !== null) {
@@ -101827,7 +102420,7 @@ ${body2}`);
101827
102420
 
101828
102421
  // src/council/council-evidence-writer.ts
101829
102422
  init_task_file();
101830
- import { appendFileSync as appendFileSync12, existsSync as existsSync55, mkdirSync as mkdirSync25, readFileSync as readFileSync45 } from "node:fs";
102423
+ import { appendFileSync as appendFileSync12, existsSync as existsSync55, mkdirSync as mkdirSync26, readFileSync as readFileSync45 } from "node:fs";
101831
102424
  import { join as join88 } from "node:path";
101832
102425
  var EVIDENCE_DIR2 = ".swarm/evidence";
101833
102426
  var VALID_TASK_ID = /^\d+\.\d+(\.\d+)*$/;
@@ -101866,7 +102459,7 @@ async function writeCouncilEvidence(workingDir, synthesis) {
101866
102459
  throw new Error(`writeCouncilEvidence: invalid taskId "${synthesis.taskId}" — must match N.M or N.M.P format`);
101867
102460
  }
101868
102461
  const dir = join88(workingDir, EVIDENCE_DIR2);
101869
- mkdirSync25(dir, { recursive: true });
102462
+ mkdirSync26(dir, { recursive: true });
101870
102463
  const filePath = taskEvidencePath(workingDir, synthesis.taskId);
101871
102464
  await _internals45.withTaskEvidenceLock(workingDir, synthesis.taskId, COUNCIL_AGENT_ID, async () => {
101872
102465
  const existingRoot = Object.create(null);
@@ -101904,7 +102497,7 @@ async function writeCouncilEvidence(workingDir, synthesis) {
101904
102497
  });
101905
102498
  try {
101906
102499
  const councilDir = join88(workingDir, ".swarm", "council");
101907
- mkdirSync25(councilDir, { recursive: true });
102500
+ mkdirSync26(councilDir, { recursive: true });
101908
102501
  const auditLine = JSON.stringify({
101909
102502
  round: synthesis.roundNumber,
101910
102503
  verdict: synthesis.overallVerdict,
@@ -102233,12 +102826,12 @@ function buildFinalCouncilFeedback(projectSummary, verdict, vetoedBy, requiredFi
102233
102826
  }
102234
102827
 
102235
102828
  // src/council/criteria-store.ts
102236
- import { existsSync as existsSync56, mkdirSync as mkdirSync26, readFileSync as readFileSync46, writeFileSync as writeFileSync17 } from "node:fs";
102829
+ import { existsSync as existsSync56, mkdirSync as mkdirSync27, readFileSync as readFileSync46, writeFileSync as writeFileSync17 } from "node:fs";
102237
102830
  import { join as join89 } from "node:path";
102238
102831
  var COUNCIL_DIR = ".swarm/council";
102239
102832
  function writeCriteria(workingDir, taskId, criteria) {
102240
102833
  const dir = join89(workingDir, COUNCIL_DIR);
102241
- mkdirSync26(dir, { recursive: true });
102834
+ mkdirSync27(dir, { recursive: true });
102242
102835
  const payload = {
102243
102836
  taskId,
102244
102837
  criteria,
@@ -102400,7 +102993,7 @@ var submit_council_verdicts = createSwarmTool({
102400
102993
  init_zod();
102401
102994
  init_loader();
102402
102995
  import * as fs74 from "node:fs";
102403
- import * as path104 from "node:path";
102996
+ import * as path105 from "node:path";
102404
102997
 
102405
102998
  // src/council/general-council-advisory.ts
102406
102999
  var ADVISORY_HEADER = "[general_council] (advisory; not blocking)";
@@ -102828,10 +103421,10 @@ var convene_general_council = createSwarmTool({
102828
103421
  const round1 = input.round1Responses;
102829
103422
  const round2 = input.round2Responses ?? [];
102830
103423
  const result = synthesizeGeneralCouncil(input.question, input.mode, round1, round2);
102831
- const evidenceDir = path104.join(workingDir, ".swarm", "council", "general");
103424
+ const evidenceDir = path105.join(workingDir, ".swarm", "council", "general");
102832
103425
  const safeTimestamp = result.timestamp.replace(/[:.]/g, "-");
102833
103426
  const evidenceFile = `${safeTimestamp}-${input.mode}.json`;
102834
- const evidencePath = path104.join(evidenceDir, evidenceFile);
103427
+ const evidencePath = path105.join(evidenceDir, evidenceFile);
102835
103428
  try {
102836
103429
  await fs74.promises.mkdir(evidenceDir, { recursive: true });
102837
103430
  await fs74.promises.writeFile(evidencePath, JSON.stringify(result, null, 2));
@@ -103076,7 +103669,7 @@ init_state();
103076
103669
  init_task_id();
103077
103670
  init_create_tool();
103078
103671
  import * as fs75 from "node:fs";
103079
- import * as path105 from "node:path";
103672
+ import * as path106 from "node:path";
103080
103673
  function validateTaskIdFormat2(taskId) {
103081
103674
  return validateTaskIdFormat(taskId);
103082
103675
  }
@@ -103150,8 +103743,8 @@ async function executeDeclareScope(args2, fallbackDir) {
103150
103743
  };
103151
103744
  }
103152
103745
  }
103153
- normalizedDir = path105.normalize(args2.working_directory);
103154
- const pathParts = normalizedDir.split(path105.sep);
103746
+ normalizedDir = path106.normalize(args2.working_directory);
103747
+ const pathParts = normalizedDir.split(path106.sep);
103155
103748
  if (pathParts.includes("..")) {
103156
103749
  return {
103157
103750
  success: false,
@@ -103161,10 +103754,10 @@ async function executeDeclareScope(args2, fallbackDir) {
103161
103754
  ]
103162
103755
  };
103163
103756
  }
103164
- const resolvedDir = path105.resolve(normalizedDir);
103757
+ const resolvedDir = path106.resolve(normalizedDir);
103165
103758
  try {
103166
103759
  const realPath = fs75.realpathSync(resolvedDir);
103167
- const planPath2 = path105.join(realPath, ".swarm", "plan.json");
103760
+ const planPath2 = path106.join(realPath, ".swarm", "plan.json");
103168
103761
  if (!fs75.existsSync(planPath2)) {
103169
103762
  return {
103170
103763
  success: false,
@@ -103185,9 +103778,9 @@ async function executeDeclareScope(args2, fallbackDir) {
103185
103778
  }
103186
103779
  if (normalizedDir && fallbackDir) {
103187
103780
  try {
103188
- const canonicalWorkingDir = fs75.realpathSync(path105.resolve(normalizedDir));
103189
- const canonicalProjectRoot = fs75.realpathSync(path105.resolve(fallbackDir));
103190
- if (canonicalWorkingDir.startsWith(canonicalProjectRoot + path105.sep)) {
103781
+ const canonicalWorkingDir = fs75.realpathSync(path106.resolve(normalizedDir));
103782
+ const canonicalProjectRoot = fs75.realpathSync(path106.resolve(fallbackDir));
103783
+ if (canonicalWorkingDir.startsWith(canonicalProjectRoot + path106.sep)) {
103191
103784
  return {
103192
103785
  success: false,
103193
103786
  message: `working_directory "${normalizedDir}" is a subdirectory of the project root. Use the project root "${fallbackDir}" instead.`,
@@ -103209,7 +103802,7 @@ async function executeDeclareScope(args2, fallbackDir) {
103209
103802
  };
103210
103803
  }
103211
103804
  const directory = normalizedDir || fallbackDir;
103212
- const planPath = path105.resolve(directory, ".swarm", "plan.json");
103805
+ const planPath = path106.resolve(directory, ".swarm", "plan.json");
103213
103806
  if (!fs75.existsSync(planPath)) {
103214
103807
  return {
103215
103808
  success: false,
@@ -103249,8 +103842,8 @@ async function executeDeclareScope(args2, fallbackDir) {
103249
103842
  const normalizeErrors = [];
103250
103843
  const dir = directory;
103251
103844
  const mergedFiles = rawMergedFiles.map((file3) => {
103252
- if (path105.isAbsolute(file3)) {
103253
- const relativePath = path105.relative(dir, file3).replace(/\\/g, "/");
103845
+ if (path106.isAbsolute(file3)) {
103846
+ const relativePath = path106.relative(dir, file3).replace(/\\/g, "/");
103254
103847
  if (relativePath.startsWith("..")) {
103255
103848
  normalizeErrors.push(`Path '${file3}' resolves outside the project directory`);
103256
103849
  return file3;
@@ -103311,7 +103904,7 @@ var declare_scope = createSwarmTool({
103311
103904
  init_zod();
103312
103905
  import * as child_process7 from "node:child_process";
103313
103906
  import * as fs76 from "node:fs";
103314
- import * as path106 from "node:path";
103907
+ import * as path107 from "node:path";
103315
103908
  init_create_tool();
103316
103909
  var MAX_DIFF_LINES = 500;
103317
103910
  var DIFF_TIMEOUT_MS = 30000;
@@ -103340,20 +103933,20 @@ function validateBase(base) {
103340
103933
  function validatePaths(paths) {
103341
103934
  if (!paths)
103342
103935
  return null;
103343
- for (const path107 of paths) {
103344
- if (!path107 || path107.length === 0) {
103936
+ for (const path108 of paths) {
103937
+ if (!path108 || path108.length === 0) {
103345
103938
  return "empty path not allowed";
103346
103939
  }
103347
- if (path107.length > MAX_PATH_LENGTH) {
103940
+ if (path108.length > MAX_PATH_LENGTH) {
103348
103941
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
103349
103942
  }
103350
- if (SHELL_METACHARACTERS2.test(path107)) {
103943
+ if (SHELL_METACHARACTERS2.test(path108)) {
103351
103944
  return "path contains shell metacharacters";
103352
103945
  }
103353
- if (path107.startsWith("-")) {
103946
+ if (path108.startsWith("-")) {
103354
103947
  return 'path cannot start with "-" (option-like arguments not allowed)';
103355
103948
  }
103356
- if (CONTROL_CHAR_PATTERN2.test(path107)) {
103949
+ if (CONTROL_CHAR_PATTERN2.test(path108)) {
103357
103950
  return "path contains control characters";
103358
103951
  }
103359
103952
  }
@@ -103461,8 +104054,8 @@ var diff = createSwarmTool({
103461
104054
  if (parts2.length >= 3) {
103462
104055
  const additions = parseInt(parts2[0], 10) || 0;
103463
104056
  const deletions = parseInt(parts2[1], 10) || 0;
103464
- const path107 = parts2[2];
103465
- files.push({ path: path107, additions, deletions });
104057
+ const path108 = parts2[2];
104058
+ files.push({ path: path108, additions, deletions });
103466
104059
  }
103467
104060
  }
103468
104061
  const contractChanges = [];
@@ -103502,7 +104095,7 @@ var diff = createSwarmTool({
103502
104095
  } else if (base === "unstaged") {
103503
104096
  const oldRef = `:${file3.path}`;
103504
104097
  oldContent = fileExistsInRef(oldRef) ? getContentFromRef(oldRef) : "";
103505
- newContent = fs76.readFileSync(path106.join(directory, file3.path), "utf-8");
104098
+ newContent = fs76.readFileSync(path107.join(directory, file3.path), "utf-8");
103506
104099
  } else {
103507
104100
  const oldRef = `${base}:${file3.path}`;
103508
104101
  oldContent = fileExistsInRef(oldRef) ? getContentFromRef(oldRef) : "";
@@ -103577,7 +104170,7 @@ var diff = createSwarmTool({
103577
104170
  init_zod();
103578
104171
  import * as child_process8 from "node:child_process";
103579
104172
  import * as fs77 from "node:fs";
103580
- import * as path107 from "node:path";
104173
+ import * as path108 from "node:path";
103581
104174
  init_create_tool();
103582
104175
  init_resolve_working_directory();
103583
104176
  var diff_summary = createSwarmTool({
@@ -103630,7 +104223,7 @@ var diff_summary = createSwarmTool({
103630
104223
  }
103631
104224
  try {
103632
104225
  let oldContent;
103633
- const newContent = fs77.readFileSync(path107.join(workingDir, filePath), "utf-8");
104226
+ const newContent = fs77.readFileSync(path108.join(workingDir, filePath), "utf-8");
103634
104227
  if (fileExistsInHead) {
103635
104228
  oldContent = child_process8.execFileSync("git", ["show", `HEAD:${filePath}`], {
103636
104229
  encoding: "utf-8",
@@ -103859,7 +104452,7 @@ init_zod();
103859
104452
  init_create_tool();
103860
104453
  init_path_security();
103861
104454
  import * as fs78 from "node:fs";
103862
- import * as path108 from "node:path";
104455
+ import * as path109 from "node:path";
103863
104456
  var MAX_FILE_SIZE_BYTES6 = 1024 * 1024;
103864
104457
  var MAX_EVIDENCE_FILES = 1000;
103865
104458
  var EVIDENCE_DIR3 = ".swarm/evidence";
@@ -103886,9 +104479,9 @@ function validateRequiredTypes(input) {
103886
104479
  return null;
103887
104480
  }
103888
104481
  function isPathWithinSwarm2(filePath, cwd) {
103889
- const normalizedCwd = path108.resolve(cwd);
103890
- const swarmPath = path108.join(normalizedCwd, ".swarm");
103891
- const normalizedPath = path108.resolve(filePath);
104482
+ const normalizedCwd = path109.resolve(cwd);
104483
+ const swarmPath = path109.join(normalizedCwd, ".swarm");
104484
+ const normalizedPath = path109.resolve(filePath);
103892
104485
  return normalizedPath.startsWith(swarmPath);
103893
104486
  }
103894
104487
  function parseCompletedTasks(planContent) {
@@ -103918,10 +104511,10 @@ function readEvidenceFiles(evidenceDir, _cwd) {
103918
104511
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
103919
104512
  continue;
103920
104513
  }
103921
- const filePath = path108.join(evidenceDir, filename);
104514
+ const filePath = path109.join(evidenceDir, filename);
103922
104515
  try {
103923
- const resolvedPath = path108.resolve(filePath);
103924
- const evidenceDirResolved = path108.resolve(evidenceDir);
104516
+ const resolvedPath = path109.resolve(filePath);
104517
+ const evidenceDirResolved = path109.resolve(evidenceDir);
103925
104518
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
103926
104519
  continue;
103927
104520
  }
@@ -104039,7 +104632,7 @@ var evidence_check = createSwarmTool({
104039
104632
  return JSON.stringify(errorResult, null, 2);
104040
104633
  }
104041
104634
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
104042
- const planPath = path108.join(cwd, PLAN_FILE);
104635
+ const planPath = path109.join(cwd, PLAN_FILE);
104043
104636
  if (!isPathWithinSwarm2(planPath, cwd)) {
104044
104637
  const errorResult = {
104045
104638
  error: "plan file path validation failed",
@@ -104071,7 +104664,7 @@ var evidence_check = createSwarmTool({
104071
104664
  };
104072
104665
  return JSON.stringify(result2, null, 2);
104073
104666
  }
104074
- const evidenceDir = path108.join(cwd, EVIDENCE_DIR3);
104667
+ const evidenceDir = path109.join(cwd, EVIDENCE_DIR3);
104075
104668
  const evidence = readEvidenceFiles(evidenceDir, cwd);
104076
104669
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
104077
104670
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -104089,7 +104682,7 @@ var evidence_check = createSwarmTool({
104089
104682
  init_zod();
104090
104683
  init_create_tool();
104091
104684
  import * as fs79 from "node:fs";
104092
- import * as path109 from "node:path";
104685
+ import * as path110 from "node:path";
104093
104686
  var EXT_MAP = {
104094
104687
  python: ".py",
104095
104688
  py: ".py",
@@ -104170,12 +104763,12 @@ var extract_code_blocks = createSwarmTool({
104170
104763
  if (prefix) {
104171
104764
  filename = `${prefix}_${filename}`;
104172
104765
  }
104173
- let filepath = path109.join(targetDir, filename);
104174
- const base = path109.basename(filepath, path109.extname(filepath));
104175
- const ext = path109.extname(filepath);
104766
+ let filepath = path110.join(targetDir, filename);
104767
+ const base = path110.basename(filepath, path110.extname(filepath));
104768
+ const ext = path110.extname(filepath);
104176
104769
  let counter = 1;
104177
104770
  while (fs79.existsSync(filepath)) {
104178
- filepath = path109.join(targetDir, `${base}_${counter}${ext}`);
104771
+ filepath = path110.join(targetDir, `${base}_${counter}${ext}`);
104179
104772
  counter++;
104180
104773
  }
104181
104774
  try {
@@ -104433,7 +105026,7 @@ init_zod();
104433
105026
  init_create_tool();
104434
105027
  init_path_security();
104435
105028
  import * as fs80 from "node:fs";
104436
- import * as path110 from "node:path";
105029
+ import * as path111 from "node:path";
104437
105030
  var MAX_FILE_PATH_LENGTH2 = 500;
104438
105031
  var MAX_SYMBOL_LENGTH = 256;
104439
105032
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
@@ -104481,7 +105074,7 @@ function validateSymbolInput(symbol3) {
104481
105074
  return null;
104482
105075
  }
104483
105076
  function isBinaryFile2(filePath, buffer) {
104484
- const ext = path110.extname(filePath).toLowerCase();
105077
+ const ext = path111.extname(filePath).toLowerCase();
104485
105078
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
104486
105079
  return false;
104487
105080
  }
@@ -104505,15 +105098,15 @@ function parseImports(content, targetFile, targetSymbol) {
104505
105098
  const imports = [];
104506
105099
  let _resolvedTarget;
104507
105100
  try {
104508
- _resolvedTarget = path110.resolve(targetFile);
105101
+ _resolvedTarget = path111.resolve(targetFile);
104509
105102
  } catch {
104510
105103
  _resolvedTarget = targetFile;
104511
105104
  }
104512
- const targetBasename = path110.basename(targetFile, path110.extname(targetFile));
105105
+ const targetBasename = path111.basename(targetFile, path111.extname(targetFile));
104513
105106
  const targetWithExt = targetFile;
104514
105107
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
104515
- const normalizedTargetWithExt = path110.normalize(targetWithExt).replace(/\\/g, "/");
104516
- const normalizedTargetWithoutExt = path110.normalize(targetWithoutExt).replace(/\\/g, "/");
105108
+ const normalizedTargetWithExt = path111.normalize(targetWithExt).replace(/\\/g, "/");
105109
+ const normalizedTargetWithoutExt = path111.normalize(targetWithoutExt).replace(/\\/g, "/");
104517
105110
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
104518
105111
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
104519
105112
  const modulePath = match[1] || match[2] || match[3];
@@ -104536,9 +105129,9 @@ function parseImports(content, targetFile, targetSymbol) {
104536
105129
  }
104537
105130
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
104538
105131
  let isMatch = false;
104539
- const _targetDir = path110.dirname(targetFile);
104540
- const targetExt = path110.extname(targetFile);
104541
- const targetBasenameNoExt = path110.basename(targetFile, targetExt);
105132
+ const _targetDir = path111.dirname(targetFile);
105133
+ const targetExt = path111.extname(targetFile);
105134
+ const targetBasenameNoExt = path111.basename(targetFile, targetExt);
104542
105135
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
104543
105136
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
104544
105137
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -104606,10 +105199,10 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
104606
105199
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
104607
105200
  for (const entry of entries) {
104608
105201
  if (SKIP_DIRECTORIES4.has(entry)) {
104609
- stats.skippedDirs.push(path110.join(dir, entry));
105202
+ stats.skippedDirs.push(path111.join(dir, entry));
104610
105203
  continue;
104611
105204
  }
104612
- const fullPath = path110.join(dir, entry);
105205
+ const fullPath = path111.join(dir, entry);
104613
105206
  let stat8;
104614
105207
  try {
104615
105208
  stat8 = fs80.statSync(fullPath);
@@ -104623,7 +105216,7 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
104623
105216
  if (stat8.isDirectory()) {
104624
105217
  findSourceFiles2(fullPath, files, stats);
104625
105218
  } else if (stat8.isFile()) {
104626
- const ext = path110.extname(fullPath).toLowerCase();
105219
+ const ext = path111.extname(fullPath).toLowerCase();
104627
105220
  if (SUPPORTED_EXTENSIONS3.includes(ext)) {
104628
105221
  files.push(fullPath);
104629
105222
  }
@@ -104680,7 +105273,7 @@ var imports = createSwarmTool({
104680
105273
  return JSON.stringify(errorResult, null, 2);
104681
105274
  }
104682
105275
  try {
104683
- const targetFile = path110.resolve(file3);
105276
+ const targetFile = path111.resolve(file3);
104684
105277
  if (!fs80.existsSync(targetFile)) {
104685
105278
  const errorResult = {
104686
105279
  error: `target file not found: ${file3}`,
@@ -104702,7 +105295,7 @@ var imports = createSwarmTool({
104702
105295
  };
104703
105296
  return JSON.stringify(errorResult, null, 2);
104704
105297
  }
104705
- const baseDir = path110.dirname(targetFile);
105298
+ const baseDir = path111.dirname(targetFile);
104706
105299
  const scanStats = {
104707
105300
  skippedDirs: [],
104708
105301
  skippedFiles: 0,
@@ -104787,7 +105380,7 @@ var imports = createSwarmTool({
104787
105380
  });
104788
105381
  // src/tools/knowledge-ack.ts
104789
105382
  init_zod();
104790
- import { randomUUID as randomUUID9 } from "node:crypto";
105383
+ import { randomUUID as randomUUID10 } from "node:crypto";
104791
105384
  init_state();
104792
105385
  init_logger();
104793
105386
  init_create_tool();
@@ -104820,7 +105413,7 @@ var knowledge_ack = createSwarmTool({
104820
105413
  let sessionId = ctx?.sessionID;
104821
105414
  if (!sessionId) {
104822
105415
  warn("[knowledge-ack] No sessionID in tool context — dedup disabled for this acknowledgment");
104823
- sessionId = randomUUID9();
105416
+ sessionId = randomUUID10();
104824
105417
  }
104825
105418
  const dedupKey = buildAckDedupKey(sessionId, a.id, a.result);
104826
105419
  if (swarmState.knowledgeAckDedup.has(dedupKey)) {
@@ -104850,7 +105443,7 @@ init_knowledge_store();
104850
105443
  init_knowledge_validator();
104851
105444
  init_manager();
104852
105445
  init_create_tool();
104853
- import { randomUUID as randomUUID10 } from "node:crypto";
105446
+ import { randomUUID as randomUUID11 } from "node:crypto";
104854
105447
  var VALID_CATEGORIES2 = [
104855
105448
  "process",
104856
105449
  "architecture",
@@ -104924,7 +105517,7 @@ var knowledge_add = createSwarmTool({
104924
105517
  project_name = plan?.title ?? "";
104925
105518
  } catch {}
104926
105519
  const entry = {
104927
- id: randomUUID10(),
105520
+ id: randomUUID11(),
104928
105521
  tier: "swarm",
104929
105522
  lesson,
104930
105523
  category,
@@ -105327,22 +105920,22 @@ init_schema();
105327
105920
  init_qa_gate_profile();
105328
105921
  init_manager2();
105329
105922
  import * as fs85 from "node:fs";
105330
- import * as path115 from "node:path";
105923
+ import * as path116 from "node:path";
105331
105924
 
105332
105925
  // src/full-auto/phase-approval.ts
105333
105926
  init_utils2();
105334
105927
  init_logger();
105335
105928
  init_state2();
105336
105929
  import * as fs81 from "node:fs";
105337
- import * as path111 from "node:path";
105930
+ import * as path112 from "node:path";
105338
105931
  var APPROVAL_TTL_MS = 24 * 60 * 60 * 1000;
105339
105932
  function readEvidenceDir(directory, phase) {
105340
105933
  try {
105341
- const dirPath = validateSwarmPath(directory, path111.posix.join("evidence", String(phase)));
105934
+ const dirPath = validateSwarmPath(directory, path112.posix.join("evidence", String(phase)));
105342
105935
  if (!fs81.existsSync(dirPath))
105343
105936
  return [];
105344
105937
  const entries = fs81.readdirSync(dirPath);
105345
- return entries.filter((e) => e.startsWith("full-auto-") && e.endsWith(".json")).map((e) => path111.join(dirPath, e));
105938
+ return entries.filter((e) => e.startsWith("full-auto-") && e.endsWith(".json")).map((e) => path112.join(dirPath, e));
105346
105939
  } catch {
105347
105940
  return [];
105348
105941
  }
@@ -105481,16 +106074,16 @@ init_plan_schema();
105481
106074
  init_ledger();
105482
106075
  init_manager();
105483
106076
  import * as fs82 from "node:fs";
105484
- import * as path112 from "node:path";
106077
+ import * as path113 from "node:path";
105485
106078
  async function writeCheckpoint(directory) {
105486
106079
  try {
105487
106080
  const plan = await loadPlan(directory);
105488
106081
  if (!plan)
105489
106082
  return;
105490
- const swarmDir = path112.join(directory, ".swarm");
106083
+ const swarmDir = path113.join(directory, ".swarm");
105491
106084
  fs82.mkdirSync(swarmDir, { recursive: true });
105492
- const jsonPath = path112.join(swarmDir, "SWARM_PLAN.json");
105493
- const mdPath = path112.join(swarmDir, "SWARM_PLAN.md");
106085
+ const jsonPath = path113.join(swarmDir, "SWARM_PLAN.json");
106086
+ const mdPath = path113.join(swarmDir, "SWARM_PLAN.md");
105494
106087
  fs82.writeFileSync(jsonPath, JSON.stringify(plan, null, 2), "utf8");
105495
106088
  const md = derivePlanMarkdown(plan);
105496
106089
  fs82.writeFileSync(mdPath, md, "utf8");
@@ -105509,15 +106102,15 @@ init_telemetry();
105509
106102
  // src/turbo/lean/phase-ready.ts
105510
106103
  init_file_locks();
105511
106104
  import * as fs84 from "node:fs";
105512
- import * as path114 from "node:path";
106105
+ import * as path115 from "node:path";
105513
106106
 
105514
106107
  // src/turbo/lean/evidence.ts
105515
106108
  init_bun_compat();
105516
106109
  import { rmSync as rmSync6 } from "node:fs";
105517
106110
  import * as fs83 from "node:fs/promises";
105518
- import * as path113 from "node:path";
106111
+ import * as path114 from "node:path";
105519
106112
  function leanTurboEvidenceDir(directory, phase) {
105520
- return path113.join(directory, ".swarm", "evidence", String(phase), "lean-turbo");
106113
+ return path114.join(directory, ".swarm", "evidence", String(phase), "lean-turbo");
105521
106114
  }
105522
106115
  function validateLaneId(laneId) {
105523
106116
  if (laneId.length === 0) {
@@ -105539,16 +106132,16 @@ function validateLaneId(laneId) {
105539
106132
  function laneEvidencePath(directory, phase, laneId) {
105540
106133
  validateLaneId(laneId);
105541
106134
  const expectedDir = leanTurboEvidenceDir(directory, phase);
105542
- const resolvedPath = path113.resolve(path113.join(expectedDir, `${laneId}.json`));
105543
- const resolvedDir = path113.resolve(expectedDir);
105544
- if (!resolvedPath.startsWith(resolvedDir + path113.sep) && resolvedPath !== resolvedDir) {
106135
+ const resolvedPath = path114.resolve(path114.join(expectedDir, `${laneId}.json`));
106136
+ const resolvedDir = path114.resolve(expectedDir);
106137
+ if (!resolvedPath.startsWith(resolvedDir + path114.sep) && resolvedPath !== resolvedDir) {
105545
106138
  throw new Error(`Invalid laneId: path traversal detected (got "${laneId}")`);
105546
106139
  }
105547
106140
  return resolvedPath;
105548
106141
  }
105549
106142
  async function atomicWriteJson(filePath, data) {
105550
106143
  const content = JSON.stringify(data, null, 2);
105551
- const dir = path113.dirname(filePath);
106144
+ const dir = path114.dirname(filePath);
105552
106145
  await fs83.mkdir(dir, { recursive: true });
105553
106146
  const tempPath = `${filePath}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
105554
106147
  try {
@@ -105562,7 +106155,7 @@ async function atomicWriteJson(filePath, data) {
105562
106155
  }
105563
106156
  }
105564
106157
  function phaseEvidencePath(directory, phase) {
105565
- return path113.join(leanTurboEvidenceDir(directory, phase), "lean-turbo-phase.json");
106158
+ return path114.join(leanTurboEvidenceDir(directory, phase), "lean-turbo-phase.json");
105566
106159
  }
105567
106160
  async function writeLaneEvidence(directory, phase, evidence) {
105568
106161
  const targetPath = laneEvidencePath(directory, phase, evidence.laneId);
@@ -105606,7 +106199,7 @@ async function listLaneEvidence(directory, phase) {
105606
106199
  if (entry === "lean-turbo-phase.json") {
105607
106200
  continue;
105608
106201
  }
105609
- const filePath = path113.join(evidenceDir, entry);
106202
+ const filePath = path114.join(evidenceDir, entry);
105610
106203
  let content;
105611
106204
  try {
105612
106205
  content = await fs83.readFile(filePath, "utf-8");
@@ -105630,7 +106223,7 @@ var DEFAULT_CONFIG2 = {
105630
106223
  };
105631
106224
  function defaultReadPlanJson(dir) {
105632
106225
  try {
105633
- const planPath = path114.join(dir, ".swarm", "plan.json");
106226
+ const planPath = path115.join(dir, ".swarm", "plan.json");
105634
106227
  if (!fs84.existsSync(planPath))
105635
106228
  return null;
105636
106229
  const raw = fs84.readFileSync(planPath, "utf-8");
@@ -105645,7 +106238,7 @@ function defaultReadPlanJson(dir) {
105645
106238
  }
105646
106239
  function readReviewerEvidenceFromFile(directory, phase) {
105647
106240
  try {
105648
- const evidencePath = path114.join(directory, ".swarm", "evidence", String(phase), "lean-turbo-reviewer.json");
106241
+ const evidencePath = path115.join(directory, ".swarm", "evidence", String(phase), "lean-turbo-reviewer.json");
105649
106242
  if (!fs84.existsSync(evidencePath)) {
105650
106243
  return null;
105651
106244
  }
@@ -105665,7 +106258,7 @@ function readReviewerEvidenceFromFile(directory, phase) {
105665
106258
  }
105666
106259
  function readCriticEvidenceFromFile(directory, phase) {
105667
106260
  try {
105668
- const evidencePath = path114.join(directory, ".swarm", "evidence", String(phase), "lean-turbo-critic.json");
106261
+ const evidencePath = path115.join(directory, ".swarm", "evidence", String(phase), "lean-turbo-critic.json");
105669
106262
  if (!fs84.existsSync(evidencePath)) {
105670
106263
  return null;
105671
106264
  }
@@ -105684,7 +106277,7 @@ function readCriticEvidenceFromFile(directory, phase) {
105684
106277
  }
105685
106278
  }
105686
106279
  function listLaneEvidenceSync(directory, phase) {
105687
- const evidenceDir = path114.join(directory, ".swarm", "evidence", String(phase), "lean-turbo");
106280
+ const evidenceDir = path115.join(directory, ".swarm", "evidence", String(phase), "lean-turbo");
105688
106281
  let entries;
105689
106282
  try {
105690
106283
  entries = fs84.readdirSync(evidenceDir);
@@ -105754,7 +106347,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
105754
106347
  ...DEFAULT_CONFIG2,
105755
106348
  ...actualConfig
105756
106349
  };
105757
- const statePath = path114.join(directory, ".swarm", "turbo-state.json");
106350
+ const statePath = path115.join(directory, ".swarm", "turbo-state.json");
105758
106351
  if (!fs84.existsSync(statePath)) {
105759
106352
  return {
105760
106353
  ok: false,
@@ -105942,7 +106535,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
105942
106535
  }
105943
106536
  }
105944
106537
  if (mergedConfig.integrated_diff_required) {
105945
- const evidencePath = path114.join(directory, ".swarm", "evidence", String(phase), "lean-turbo-phase.json");
106538
+ const evidencePath = path115.join(directory, ".swarm", "evidence", String(phase), "lean-turbo-phase.json");
105946
106539
  let hasDiff = false;
105947
106540
  try {
105948
106541
  const content = fs84.readFileSync(evidencePath, "utf-8");
@@ -106237,7 +106830,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
106237
106830
  let driftCheckEnabled = true;
106238
106831
  let driftHasSpecMd = false;
106239
106832
  try {
106240
- const specMdPath = path115.join(dir, ".swarm", "spec.md");
106833
+ const specMdPath = path116.join(dir, ".swarm", "spec.md");
106241
106834
  driftHasSpecMd = fs85.existsSync(specMdPath);
106242
106835
  const gatePlan = await loadPlan(dir);
106243
106836
  if (gatePlan) {
@@ -106258,7 +106851,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
106258
106851
  } else {
106259
106852
  let phaseType;
106260
106853
  try {
106261
- const planPath = path115.join(dir, ".swarm", "plan.json");
106854
+ const planPath = path116.join(dir, ".swarm", "plan.json");
106262
106855
  if (fs85.existsSync(planPath)) {
106263
106856
  const planRaw = fs85.readFileSync(planPath, "utf-8");
106264
106857
  const plan = JSON.parse(planRaw);
@@ -106270,7 +106863,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
106270
106863
  warnings.push(`Phase ${phase} is annotated as 'non-code'. Drift verification was skipped per phase type annotation.`);
106271
106864
  } else {
106272
106865
  try {
106273
- const driftEvidencePath = path115.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
106866
+ const driftEvidencePath = path116.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
106274
106867
  let driftVerdictFound = false;
106275
106868
  let driftVerdictApproved = false;
106276
106869
  try {
@@ -106308,7 +106901,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
106308
106901
  let incompleteTaskCount = 0;
106309
106902
  let planParseable = false;
106310
106903
  try {
106311
- const planPath = path115.join(dir, ".swarm", "plan.json");
106904
+ const planPath = path116.join(dir, ".swarm", "plan.json");
106312
106905
  if (fs85.existsSync(planPath)) {
106313
106906
  const planRaw = fs85.readFileSync(planPath, "utf-8");
106314
106907
  const plan = JSON.parse(planRaw);
@@ -106375,7 +106968,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
106375
106968
  const overrides = session2?.qaGateSessionOverrides ?? {};
106376
106969
  const effective = getEffectiveGates(profile, overrides);
106377
106970
  if (effective.hallucination_guard === true) {
106378
- const hgPath = path115.join(dir, ".swarm", "evidence", String(phase), "hallucination-guard.json");
106971
+ const hgPath = path116.join(dir, ".swarm", "evidence", String(phase), "hallucination-guard.json");
106379
106972
  let hgVerdictFound = false;
106380
106973
  let hgVerdictApproved = false;
106381
106974
  try {
@@ -106447,7 +107040,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
106447
107040
  const overrides = session2?.qaGateSessionOverrides ?? {};
106448
107041
  const effective = getEffectiveGates(profile, overrides);
106449
107042
  if (effective.mutation_test === true) {
106450
- const mgPath = path115.join(dir, ".swarm", "evidence", String(phase), "mutation-gate.json");
107043
+ const mgPath = path116.join(dir, ".swarm", "evidence", String(phase), "mutation-gate.json");
106451
107044
  let mgVerdictFound = false;
106452
107045
  let mgVerdict;
106453
107046
  try {
@@ -106521,7 +107114,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
106521
107114
  const effective = getEffectiveGates(profile, overrides);
106522
107115
  if (effective.council_mode === true) {
106523
107116
  councilModeEnabled = true;
106524
- const pcPath = path115.join(dir, ".swarm", "evidence", String(phase), "phase-council.json");
107117
+ const pcPath = path116.join(dir, ".swarm", "evidence", String(phase), "phase-council.json");
106525
107118
  let pcVerdictFound = false;
106526
107119
  let _pcVerdict;
106527
107120
  let pcQuorumSize;
@@ -106729,7 +107322,7 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
106729
107322
  const effective = getEffectiveGates(profile, overrides);
106730
107323
  if (effective.final_council === true) {
106731
107324
  finalCouncilEnabled = true;
106732
- const fcPath = path115.join(dir, ".swarm", "evidence", "final-council.json");
107325
+ const fcPath = path116.join(dir, ".swarm", "evidence", "final-council.json");
106733
107326
  let fcVerdictFound = false;
106734
107327
  let _fcVerdict;
106735
107328
  try {
@@ -106917,7 +107510,7 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
106917
107510
  }
106918
107511
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
106919
107512
  try {
106920
- const projectName = path115.basename(dir);
107513
+ const projectName = path116.basename(dir);
106921
107514
  const curationResult = await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
106922
107515
  if (curationResult) {
106923
107516
  const sessionState = swarmState.agentSessions.get(sessionID);
@@ -107287,7 +107880,7 @@ init_utils();
107287
107880
  init_bun_compat();
107288
107881
  init_create_tool();
107289
107882
  import * as fs86 from "node:fs";
107290
- import * as path116 from "node:path";
107883
+ import * as path117 from "node:path";
107291
107884
  var MAX_OUTPUT_BYTES5 = 52428800;
107292
107885
  var AUDIT_TIMEOUT_MS = 120000;
107293
107886
  function isValidEcosystem(value) {
@@ -107315,16 +107908,16 @@ function validateArgs3(args2) {
107315
107908
  function detectEcosystems(directory) {
107316
107909
  const ecosystems = [];
107317
107910
  const cwd = directory;
107318
- if (fs86.existsSync(path116.join(cwd, "package.json"))) {
107911
+ if (fs86.existsSync(path117.join(cwd, "package.json"))) {
107319
107912
  ecosystems.push("npm");
107320
107913
  }
107321
- if (fs86.existsSync(path116.join(cwd, "pyproject.toml")) || fs86.existsSync(path116.join(cwd, "requirements.txt"))) {
107914
+ if (fs86.existsSync(path117.join(cwd, "pyproject.toml")) || fs86.existsSync(path117.join(cwd, "requirements.txt"))) {
107322
107915
  ecosystems.push("pip");
107323
107916
  }
107324
- if (fs86.existsSync(path116.join(cwd, "Cargo.toml"))) {
107917
+ if (fs86.existsSync(path117.join(cwd, "Cargo.toml"))) {
107325
107918
  ecosystems.push("cargo");
107326
107919
  }
107327
- if (fs86.existsSync(path116.join(cwd, "go.mod"))) {
107920
+ if (fs86.existsSync(path117.join(cwd, "go.mod"))) {
107328
107921
  ecosystems.push("go");
107329
107922
  }
107330
107923
  try {
@@ -107333,13 +107926,13 @@ function detectEcosystems(directory) {
107333
107926
  ecosystems.push("dotnet");
107334
107927
  }
107335
107928
  } catch {}
107336
- if (fs86.existsSync(path116.join(cwd, "Gemfile")) || fs86.existsSync(path116.join(cwd, "Gemfile.lock"))) {
107929
+ if (fs86.existsSync(path117.join(cwd, "Gemfile")) || fs86.existsSync(path117.join(cwd, "Gemfile.lock"))) {
107337
107930
  ecosystems.push("ruby");
107338
107931
  }
107339
- if (fs86.existsSync(path116.join(cwd, "pubspec.yaml"))) {
107932
+ if (fs86.existsSync(path117.join(cwd, "pubspec.yaml"))) {
107340
107933
  ecosystems.push("dart");
107341
107934
  }
107342
- if (fs86.existsSync(path116.join(cwd, "composer.lock"))) {
107935
+ if (fs86.existsSync(path117.join(cwd, "composer.lock"))) {
107343
107936
  ecosystems.push("composer");
107344
107937
  }
107345
107938
  return ecosystems;
@@ -108475,7 +109068,7 @@ var pkg_audit = createSwarmTool({
108475
109068
  init_zod();
108476
109069
  init_manager2();
108477
109070
  import * as fs87 from "node:fs";
108478
- import * as path117 from "node:path";
109071
+ import * as path118 from "node:path";
108479
109072
  init_utils();
108480
109073
  init_create_tool();
108481
109074
  var MAX_FILE_SIZE = 1024 * 1024;
@@ -108598,7 +109191,7 @@ function isScaffoldFile(filePath) {
108598
109191
  if (SCAFFOLD_PATH_PATTERNS.some((pattern) => pattern.test(normalizedPath))) {
108599
109192
  return true;
108600
109193
  }
108601
- const filename = path117.basename(filePath);
109194
+ const filename = path118.basename(filePath);
108602
109195
  if (SCAFFOLD_FILENAME_PATTERNS.some((pattern) => pattern.test(filename))) {
108603
109196
  return true;
108604
109197
  }
@@ -108615,7 +109208,7 @@ function isAllowedByGlobs(filePath, allowGlobs) {
108615
109208
  if (regex.test(normalizedPath)) {
108616
109209
  return true;
108617
109210
  }
108618
- const filename = path117.basename(filePath);
109211
+ const filename = path118.basename(filePath);
108619
109212
  const filenameRegex = new RegExp(`^${regexPattern}$`, "i");
108620
109213
  if (filenameRegex.test(filename)) {
108621
109214
  return true;
@@ -108624,7 +109217,7 @@ function isAllowedByGlobs(filePath, allowGlobs) {
108624
109217
  return false;
108625
109218
  }
108626
109219
  function isParserSupported(filePath) {
108627
- const ext = path117.extname(filePath).toLowerCase();
109220
+ const ext = path118.extname(filePath).toLowerCase();
108628
109221
  return SUPPORTED_PARSER_EXTENSIONS.has(ext);
108629
109222
  }
108630
109223
  function isPlanFile(filePath) {
@@ -108871,9 +109464,9 @@ async function placeholderScan(input, directory) {
108871
109464
  let filesScanned = 0;
108872
109465
  const filesWithFindings = new Set;
108873
109466
  for (const filePath of changed_files) {
108874
- const fullPath = path117.isAbsolute(filePath) ? filePath : path117.resolve(directory, filePath);
108875
- const resolvedDirectory = path117.resolve(directory);
108876
- if (!fullPath.startsWith(resolvedDirectory + path117.sep) && fullPath !== resolvedDirectory) {
109467
+ const fullPath = path118.isAbsolute(filePath) ? filePath : path118.resolve(directory, filePath);
109468
+ const resolvedDirectory = path118.resolve(directory);
109469
+ if (!fullPath.startsWith(resolvedDirectory + path118.sep) && fullPath !== resolvedDirectory) {
108877
109470
  continue;
108878
109471
  }
108879
109472
  if (!fs87.existsSync(fullPath)) {
@@ -108882,7 +109475,7 @@ async function placeholderScan(input, directory) {
108882
109475
  if (isAllowedByGlobs(filePath, allow_globs)) {
108883
109476
  continue;
108884
109477
  }
108885
- const relativeFilePath = path117.relative(directory, fullPath).replace(/\\/g, "/");
109478
+ const relativeFilePath = path118.relative(directory, fullPath).replace(/\\/g, "/");
108886
109479
  if (FILE_ALLOWLIST.some((allowed) => relativeFilePath.endsWith(allowed))) {
108887
109480
  continue;
108888
109481
  }
@@ -108954,7 +109547,7 @@ var placeholder_scan = createSwarmTool({
108954
109547
  });
108955
109548
  // src/tools/pre-check-batch.ts
108956
109549
  import * as fs91 from "node:fs";
108957
- import * as path121 from "node:path";
109550
+ import * as path122 from "node:path";
108958
109551
  init_zod();
108959
109552
  init_manager2();
108960
109553
  init_utils();
@@ -109095,7 +109688,7 @@ init_zod();
109095
109688
  init_manager2();
109096
109689
  init_detector();
109097
109690
  import * as fs90 from "node:fs";
109098
- import * as path120 from "node:path";
109691
+ import * as path121 from "node:path";
109099
109692
  import { extname as extname20 } from "node:path";
109100
109693
 
109101
109694
  // src/sast/rules/c.ts
@@ -109811,7 +110404,7 @@ function executeRulesSync(filePath, content, language) {
109811
110404
  // src/sast/semgrep.ts
109812
110405
  import * as child_process9 from "node:child_process";
109813
110406
  import * as fs88 from "node:fs";
109814
- import * as path118 from "node:path";
110407
+ import * as path119 from "node:path";
109815
110408
  var semgrepAvailableCache = null;
109816
110409
  var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
109817
110410
  var DEFAULT_TIMEOUT_MS3 = 30000;
@@ -109998,7 +110591,7 @@ async function runSemgrep(options) {
109998
110591
  }
109999
110592
  function getRulesDirectory(projectRoot) {
110000
110593
  if (projectRoot) {
110001
- return path118.resolve(projectRoot, DEFAULT_RULES_DIR);
110594
+ return path119.resolve(projectRoot, DEFAULT_RULES_DIR);
110002
110595
  }
110003
110596
  return DEFAULT_RULES_DIR;
110004
110597
  }
@@ -110019,24 +110612,24 @@ init_create_tool();
110019
110612
  init_utils2();
110020
110613
  import * as crypto10 from "node:crypto";
110021
110614
  import * as fs89 from "node:fs";
110022
- import * as path119 from "node:path";
110615
+ import * as path120 from "node:path";
110023
110616
  var BASELINE_SCHEMA_VERSION = "1.0.0";
110024
110617
  var MAX_BASELINE_FINDINGS = 2000;
110025
110618
  var MAX_BASELINE_BYTES = 2 * 1048576;
110026
110619
  var LOCK_RETRY_DELAYS_MS = [50, 100, 200, 400, 800];
110027
110620
  function normalizeFindingPath(directory, file3) {
110028
- const resolved = path119.isAbsolute(file3) ? file3 : path119.resolve(directory, file3);
110029
- const rel = path119.relative(path119.resolve(directory), resolved);
110621
+ const resolved = path120.isAbsolute(file3) ? file3 : path120.resolve(directory, file3);
110622
+ const rel = path120.relative(path120.resolve(directory), resolved);
110030
110623
  return rel.replace(/\\/g, "/");
110031
110624
  }
110032
110625
  function baselineRelPath(phase) {
110033
- return path119.join("evidence", String(phase), "sast-baseline.json");
110626
+ return path120.join("evidence", String(phase), "sast-baseline.json");
110034
110627
  }
110035
110628
  function tempRelPath(phase) {
110036
- return path119.join("evidence", String(phase), `sast-baseline.json.tmp.${Date.now()}.${process.pid}`);
110629
+ return path120.join("evidence", String(phase), `sast-baseline.json.tmp.${Date.now()}.${process.pid}`);
110037
110630
  }
110038
110631
  function lockRelPath(phase) {
110039
- return path119.join("evidence", String(phase), "sast-baseline.json.lock");
110632
+ return path120.join("evidence", String(phase), "sast-baseline.json.lock");
110040
110633
  }
110041
110634
  function getLine(lines, idx) {
110042
110635
  if (idx < 0 || idx >= lines.length)
@@ -110157,8 +110750,8 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
110157
110750
  message: e instanceof Error ? e.message : "Path validation failed"
110158
110751
  };
110159
110752
  }
110160
- fs89.mkdirSync(path119.dirname(baselinePath), { recursive: true });
110161
- fs89.mkdirSync(path119.dirname(tempPath), { recursive: true });
110753
+ fs89.mkdirSync(path120.dirname(baselinePath), { recursive: true });
110754
+ fs89.mkdirSync(path120.dirname(tempPath), { recursive: true });
110162
110755
  const releaseLock = await acquireLock2(lockPath);
110163
110756
  try {
110164
110757
  let existing = null;
@@ -110431,9 +111024,9 @@ async function sastScan(input, directory, config3) {
110431
111024
  _filesSkipped++;
110432
111025
  continue;
110433
111026
  }
110434
- const resolvedPath = path120.isAbsolute(filePath) ? filePath : path120.resolve(directory, filePath);
110435
- const resolvedDirectory = path120.resolve(directory);
110436
- if (!resolvedPath.startsWith(resolvedDirectory + path120.sep) && resolvedPath !== resolvedDirectory) {
111027
+ const resolvedPath = path121.isAbsolute(filePath) ? filePath : path121.resolve(directory, filePath);
111028
+ const resolvedDirectory = path121.resolve(directory);
111029
+ if (!resolvedPath.startsWith(resolvedDirectory + path121.sep) && resolvedPath !== resolvedDirectory) {
110437
111030
  _filesSkipped++;
110438
111031
  continue;
110439
111032
  }
@@ -110748,18 +111341,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
110748
111341
  let resolved;
110749
111342
  const isWinAbs = isWindowsAbsolutePath(inputPath);
110750
111343
  if (isWinAbs) {
110751
- resolved = path121.win32.resolve(inputPath);
110752
- } else if (path121.isAbsolute(inputPath)) {
110753
- resolved = path121.resolve(inputPath);
111344
+ resolved = path122.win32.resolve(inputPath);
111345
+ } else if (path122.isAbsolute(inputPath)) {
111346
+ resolved = path122.resolve(inputPath);
110754
111347
  } else {
110755
- resolved = path121.resolve(baseDir, inputPath);
111348
+ resolved = path122.resolve(baseDir, inputPath);
110756
111349
  }
110757
- const workspaceResolved = path121.resolve(workspaceDir);
111350
+ const workspaceResolved = path122.resolve(workspaceDir);
110758
111351
  let relative25;
110759
111352
  if (isWinAbs) {
110760
- relative25 = path121.win32.relative(workspaceResolved, resolved);
111353
+ relative25 = path122.win32.relative(workspaceResolved, resolved);
110761
111354
  } else {
110762
- relative25 = path121.relative(workspaceResolved, resolved);
111355
+ relative25 = path122.relative(workspaceResolved, resolved);
110763
111356
  }
110764
111357
  if (relative25.startsWith("..")) {
110765
111358
  return "path traversal detected";
@@ -110824,7 +111417,7 @@ async function runLintOnFiles(linter, files, workspaceDir) {
110824
111417
  if (typeof file3 !== "string") {
110825
111418
  continue;
110826
111419
  }
110827
- const resolvedPath = path121.resolve(file3);
111420
+ const resolvedPath = path122.resolve(file3);
110828
111421
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
110829
111422
  if (validationError) {
110830
111423
  continue;
@@ -110981,7 +111574,7 @@ async function runSecretscanWithFiles(files, directory) {
110981
111574
  skippedFiles++;
110982
111575
  continue;
110983
111576
  }
110984
- const resolvedPath = path121.resolve(file3);
111577
+ const resolvedPath = path122.resolve(file3);
110985
111578
  const validationError = validatePath(resolvedPath, directory, directory);
110986
111579
  if (validationError) {
110987
111580
  skippedFiles++;
@@ -110999,7 +111592,7 @@ async function runSecretscanWithFiles(files, directory) {
110999
111592
  };
111000
111593
  }
111001
111594
  for (const file3 of validatedFiles) {
111002
- const ext = path121.extname(file3).toLowerCase();
111595
+ const ext = path122.extname(file3).toLowerCase();
111003
111596
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
111004
111597
  skippedFiles++;
111005
111598
  continue;
@@ -111218,7 +111811,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
111218
111811
  const preexistingFindings = [];
111219
111812
  for (const finding of findings) {
111220
111813
  const filePath = finding.location.file;
111221
- const normalised = path121.relative(directory, filePath).replace(/\\/g, "/");
111814
+ const normalised = path122.relative(directory, filePath).replace(/\\/g, "/");
111222
111815
  const changedLines = changedLineRanges.get(normalised);
111223
111816
  if (changedLines?.has(finding.location.line)) {
111224
111817
  newFindings.push(finding);
@@ -111269,7 +111862,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
111269
111862
  warn(`pre_check_batch: Invalid file path: ${file3}`);
111270
111863
  continue;
111271
111864
  }
111272
- changedFiles.push(path121.resolve(directory, file3));
111865
+ changedFiles.push(path122.resolve(directory, file3));
111273
111866
  }
111274
111867
  if (changedFiles.length === 0) {
111275
111868
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -111470,9 +112063,9 @@ var pre_check_batch = createSwarmTool({
111470
112063
  };
111471
112064
  return JSON.stringify(errorResult, null, 2);
111472
112065
  }
111473
- const resolvedDirectory = path121.resolve(typedArgs.directory);
111474
- const workspaceAnchor = path121.resolve(directory);
111475
- if (resolvedDirectory !== workspaceAnchor && resolvedDirectory.startsWith(workspaceAnchor + path121.sep)) {
112066
+ const resolvedDirectory = path122.resolve(typedArgs.directory);
112067
+ const workspaceAnchor = path122.resolve(directory);
112068
+ if (resolvedDirectory !== workspaceAnchor && resolvedDirectory.startsWith(workspaceAnchor + path122.sep)) {
111476
112069
  const subDirError = `directory "${typedArgs.directory}" is a subdirectory of the project root — pre_check_batch requires the project root directory "${workspaceAnchor}"`;
111477
112070
  const subDirResult = {
111478
112071
  gates_passed: false,
@@ -111523,7 +112116,7 @@ var pre_check_batch = createSwarmTool({
111523
112116
  });
111524
112117
  // src/tools/repo-map.ts
111525
112118
  init_zod();
111526
- import * as path122 from "node:path";
112119
+ import * as path123 from "node:path";
111527
112120
  init_path_security();
111528
112121
  init_create_tool();
111529
112122
  var VALID_ACTIONS = [
@@ -111548,7 +112141,7 @@ function validateFile(p) {
111548
112141
  return "file contains control characters";
111549
112142
  if (containsPathTraversal(p))
111550
112143
  return "file contains path traversal";
111551
- if (path122.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p)) {
112144
+ if (path123.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p)) {
111552
112145
  return "file must be a workspace-relative path, not absolute";
111553
112146
  }
111554
112147
  return null;
@@ -111571,8 +112164,8 @@ function ok(action, payload) {
111571
112164
  }
111572
112165
  function toRelativeGraphPath(input, workspaceRoot) {
111573
112166
  const normalized = input.replace(/\\/g, "/");
111574
- if (path122.isAbsolute(normalized)) {
111575
- const rel = path122.relative(workspaceRoot, normalized).replace(/\\/g, "/");
112167
+ if (path123.isAbsolute(normalized)) {
112168
+ const rel = path123.relative(workspaceRoot, normalized).replace(/\\/g, "/");
111576
112169
  return normalizeGraphPath2(rel);
111577
112170
  }
111578
112171
  return normalizeGraphPath2(normalized);
@@ -111717,7 +112310,7 @@ var repo_map = createSwarmTool({
111717
112310
  init_zod();
111718
112311
  init_create_tool();
111719
112312
  import * as fs92 from "node:fs";
111720
- import * as path123 from "node:path";
112313
+ import * as path124 from "node:path";
111721
112314
  var SPEC_FILE = ".swarm/spec.md";
111722
112315
  var EVIDENCE_DIR4 = ".swarm/evidence";
111723
112316
  var OBLIGATION_KEYWORDS = ["MUST", "SHOULD", "SHALL"];
@@ -111786,7 +112379,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
111786
112379
  return [];
111787
112380
  }
111788
112381
  for (const entry of entries) {
111789
- const entryPath = path123.join(evidenceDir, entry);
112382
+ const entryPath = path124.join(evidenceDir, entry);
111790
112383
  try {
111791
112384
  const stat8 = fs92.statSync(entryPath);
111792
112385
  if (!stat8.isDirectory()) {
@@ -111802,11 +112395,11 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
111802
112395
  if (entryPhase !== String(phase)) {
111803
112396
  continue;
111804
112397
  }
111805
- const evidenceFilePath = path123.join(entryPath, "evidence.json");
112398
+ const evidenceFilePath = path124.join(entryPath, "evidence.json");
111806
112399
  try {
111807
- const resolvedPath = path123.resolve(evidenceFilePath);
111808
- const evidenceDirResolved = path123.resolve(evidenceDir);
111809
- if (!resolvedPath.startsWith(evidenceDirResolved + path123.sep)) {
112400
+ const resolvedPath = path124.resolve(evidenceFilePath);
112401
+ const evidenceDirResolved = path124.resolve(evidenceDir);
112402
+ if (!resolvedPath.startsWith(evidenceDirResolved + path124.sep)) {
111810
112403
  continue;
111811
112404
  }
111812
112405
  const stat8 = fs92.lstatSync(evidenceFilePath);
@@ -111840,7 +112433,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
111840
112433
  if (Array.isArray(diffEntry.files_changed)) {
111841
112434
  for (const file3 of diffEntry.files_changed) {
111842
112435
  if (typeof file3 === "string") {
111843
- touchedFiles.add(path123.resolve(cwd, file3));
112436
+ touchedFiles.add(path124.resolve(cwd, file3));
111844
112437
  }
111845
112438
  }
111846
112439
  }
@@ -111853,8 +112446,8 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
111853
112446
  }
111854
112447
  function searchFileForKeywords(filePath, keywords, cwd) {
111855
112448
  try {
111856
- const resolvedPath = path123.resolve(filePath);
111857
- const cwdResolved = path123.resolve(cwd);
112449
+ const resolvedPath = path124.resolve(filePath);
112450
+ const cwdResolved = path124.resolve(cwd);
111858
112451
  if (!resolvedPath.startsWith(cwdResolved)) {
111859
112452
  return false;
111860
112453
  }
@@ -111988,7 +112581,7 @@ var req_coverage = createSwarmTool({
111988
112581
  }, null, 2);
111989
112582
  }
111990
112583
  const cwd = inputDirectory || directory;
111991
- const specPath = path123.join(cwd, SPEC_FILE);
112584
+ const specPath = path124.join(cwd, SPEC_FILE);
111992
112585
  let specContent;
111993
112586
  try {
111994
112587
  specContent = fs92.readFileSync(specPath, "utf-8");
@@ -112015,7 +112608,7 @@ var req_coverage = createSwarmTool({
112015
112608
  message: "No FR requirements found in spec.md"
112016
112609
  }, null, 2);
112017
112610
  }
112018
- const evidenceDir = path123.join(cwd, EVIDENCE_DIR4);
112611
+ const evidenceDir = path124.join(cwd, EVIDENCE_DIR4);
112019
112612
  const touchedFiles = readTouchedFiles(evidenceDir, phase, cwd);
112020
112613
  const analyzedRequirements = [];
112021
112614
  let coveredCount = 0;
@@ -112041,7 +112634,7 @@ var req_coverage = createSwarmTool({
112041
112634
  requirements: analyzedRequirements
112042
112635
  };
112043
112636
  const reportFilename = `req-coverage-phase-${phase}.json`;
112044
- const reportPath = path123.join(evidenceDir, reportFilename);
112637
+ const reportPath = path124.join(evidenceDir, reportFilename);
112045
112638
  try {
112046
112639
  if (!fs92.existsSync(evidenceDir)) {
112047
112640
  fs92.mkdirSync(evidenceDir, { recursive: true });
@@ -112129,11 +112722,14 @@ init_qa_gate_profile();
112129
112722
  init_file_locks();
112130
112723
  import * as crypto11 from "node:crypto";
112131
112724
  import * as fs93 from "node:fs";
112132
- import * as path124 from "node:path";
112725
+ import * as path125 from "node:path";
112133
112726
  init_ledger();
112134
112727
  init_manager();
112135
112728
  init_state();
112136
112729
  init_create_tool();
112730
+ function executionProfilesEqual(a, b) {
112731
+ return a.parallelization_enabled === b.parallelization_enabled && a.max_concurrent_tasks === b.max_concurrent_tasks && a.council_parallel === b.council_parallel && a.locked === b.locked;
112732
+ }
112137
112733
  function detectPlaceholderContent(args2) {
112138
112734
  const issues = [];
112139
112735
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -112207,8 +112803,8 @@ async function executeSavePlan(args2, fallbackDir) {
112207
112803
  };
112208
112804
  }
112209
112805
  if (args2.working_directory && fallbackDir) {
112210
- const resolvedTarget = path124.resolve(args2.working_directory);
112211
- const resolvedRoot = path124.resolve(fallbackDir);
112806
+ const resolvedTarget = path125.resolve(args2.working_directory);
112807
+ const resolvedRoot = path125.resolve(fallbackDir);
112212
112808
  let fallbackExists = false;
112213
112809
  try {
112214
112810
  fs93.accessSync(resolvedRoot, fs93.constants.F_OK);
@@ -112217,7 +112813,7 @@ async function executeSavePlan(args2, fallbackDir) {
112217
112813
  fallbackExists = false;
112218
112814
  }
112219
112815
  if (fallbackExists) {
112220
- const isSubdirectory = resolvedTarget.startsWith(resolvedRoot + path124.sep);
112816
+ const isSubdirectory = resolvedTarget.startsWith(resolvedRoot + path125.sep);
112221
112817
  if (isSubdirectory) {
112222
112818
  return {
112223
112819
  success: false,
@@ -112233,7 +112829,7 @@ async function executeSavePlan(args2, fallbackDir) {
112233
112829
  let specMtime;
112234
112830
  let specHash;
112235
112831
  if (process.env.SWARM_SKIP_SPEC_GATE !== "1") {
112236
- const specPath = path124.join(targetWorkspace, ".swarm", "spec.md");
112832
+ const specPath = path125.join(targetWorkspace, ".swarm", "spec.md");
112237
112833
  try {
112238
112834
  const stat8 = await fs93.promises.stat(specPath);
112239
112835
  specMtime = stat8.mtime.toISOString();
@@ -112249,7 +112845,7 @@ async function executeSavePlan(args2, fallbackDir) {
112249
112845
  }
112250
112846
  }
112251
112847
  if (process.env.SWARM_SKIP_GATE_SELECTION !== "1") {
112252
- const contextPath = path124.join(targetWorkspace, ".swarm", "context.md");
112848
+ const contextPath = path125.join(targetWorkspace, ".swarm", "context.md");
112253
112849
  let contextContent = "";
112254
112850
  try {
112255
112851
  contextContent = await fs93.promises.readFile(contextPath, "utf8");
@@ -112298,18 +112894,51 @@ async function executeSavePlan(args2, fallbackDir) {
112298
112894
  }
112299
112895
  }
112300
112896
  }
112301
- if (existing.execution_profile?.locked) {
112302
- if (args2.execution_profile !== undefined && !args2.reset_statuses) {
112897
+ if (args2.confirm_identity_change !== true) {
112898
+ const existingId = derivePlanId(existing);
112899
+ const incomingId = derivePlanId({
112900
+ swarm: args2.swarm_id,
112901
+ title: args2.title
112902
+ });
112903
+ if (existingId !== incomingId) {
112303
112904
  return {
112304
112905
  success: false,
112305
- message: "EXECUTION_PROFILE_LOCKED: The execution_profile for this plan is locked and cannot be changed.",
112906
+ message: "PLAN_IDENTITY_MISMATCH: The incoming plan identity does not match the existing plan. " + "To overwrite with a new identity, set confirm_identity_change: true.",
112306
112907
  errors: [
112307
- "execution_profile.locked is true — to change the profile you must first unlock it via a separate plan revision that explicitly sets locked: false, or reset the plan with reset_statuses."
112908
+ `Existing plan identity: ${existingId} (swarm: "${existing.swarm}", title: "${existing.title}")`,
112909
+ `Incoming plan identity: ${incomingId} (swarm: "${args2.swarm_id}", title: "${args2.title}")`
112308
112910
  ],
112309
- recovery_guidance: "Remove the execution_profile field from this save_plan call to preserve the locked profile, " + "or use reset_statuses: true to start fresh (this clears the lock). " + "Never modify execution_profile directly in plan.json."
112911
+ recovery_guidance: "Verify the title and swarm_id match the intended plan. " + "If the identity change is intentional, retry with confirm_identity_change: true. " + "Never write .swarm/plan.json or .swarm/plan.md directly."
112310
112912
  };
112311
112913
  }
112312
- if (!args2.reset_statuses) {
112914
+ }
112915
+ if (existing.execution_profile?.locked) {
112916
+ if (args2.execution_profile !== undefined && !args2.reset_statuses) {
112917
+ const requestedProfile = ExecutionProfileSchema.safeParse({
112918
+ ...existing.execution_profile,
112919
+ ...args2.execution_profile
112920
+ });
112921
+ if (!requestedProfile.success) {
112922
+ return {
112923
+ success: false,
112924
+ message: "Invalid execution_profile: schema validation failed",
112925
+ errors: requestedProfile.error.issues.map((i2) => `${i2.path.join(".")}: ${i2.message}`),
112926
+ recovery_guidance: "Check execution_profile fields: parallelization_enabled (boolean), " + "max_concurrent_tasks (integer 1-64), council_parallel (boolean), locked (boolean)."
112927
+ };
112928
+ }
112929
+ if (executionProfilesEqual(existing.execution_profile, requestedProfile.data)) {
112930
+ preservedExecutionProfile = existing.execution_profile;
112931
+ } else {
112932
+ return {
112933
+ success: false,
112934
+ message: "EXECUTION_PROFILE_LOCKED: The execution_profile for this plan is locked and cannot be changed.",
112935
+ errors: [
112936
+ "execution_profile.locked is true — to change the profile you must first unlock it via a separate plan revision that explicitly sets locked: false, or reset the plan with reset_statuses."
112937
+ ],
112938
+ recovery_guidance: "Remove the execution_profile field from this save_plan call to preserve the locked profile, " + "or use reset_statuses: true to start fresh (this clears the lock). " + "Never modify execution_profile directly in plan.json."
112939
+ };
112940
+ }
112941
+ } else if (!args2.reset_statuses) {
112313
112942
  preservedExecutionProfile = existing.execution_profile;
112314
112943
  }
112315
112944
  } else {
@@ -112481,7 +113110,7 @@ async function executeSavePlan(args2, fallbackDir) {
112481
113110
  });
112482
113111
  const savedPlan = await loadPlanJsonOnly(dir);
112483
113112
  if (savedPlan) {
112484
- await takeSnapshotEvent(dir, savedPlan).catch(() => {});
113113
+ await takeSnapshotWithRetry(dir, savedPlan);
112485
113114
  }
112486
113115
  if (resolvedProfile !== undefined && savedPlan) {
112487
113116
  const planId = derivePlanId(plan);
@@ -112506,7 +113135,7 @@ async function executeSavePlan(args2, fallbackDir) {
112506
113135
  }
112507
113136
  await writeCheckpoint(dir).catch(() => {});
112508
113137
  try {
112509
- const markerPath = path124.join(dir, ".swarm", ".plan-write-marker");
113138
+ const markerPath = path125.join(dir, ".swarm", ".plan-write-marker");
112510
113139
  const marker = JSON.stringify({
112511
113140
  source: "save_plan",
112512
113141
  timestamp: new Date().toISOString(),
@@ -112529,7 +113158,7 @@ async function executeSavePlan(args2, fallbackDir) {
112529
113158
  return {
112530
113159
  success: true,
112531
113160
  message: "Plan saved successfully",
112532
- plan_path: path124.join(dir, ".swarm", "plan.json"),
113161
+ plan_path: path125.join(dir, ".swarm", "plan.json"),
112533
113162
  phases_count: plan.phases.length,
112534
113163
  tasks_count: tasksCount,
112535
113164
  ...resolvedProfile !== undefined ? { execution_profile: resolvedProfile } : {},
@@ -112579,6 +113208,7 @@ var save_plan = createSwarmTool({
112579
113208
  removed_task_ids: exports_external.array(exports_external.string()).optional().describe("Task IDs that are present in the prior plan but intentionally being " + "removed by this save. Every task missing from `phases` MUST be enumerated " + "here, otherwise save_plan rejects with PLAN_TASK_REMOVAL_NOT_ACKNOWLEDGED. " + "Tasks not yet finished (status pending/in_progress/blocked) MUST NOT be " + "removed without explicit user confirmation."),
112580
113209
  removal_reason: exports_external.string().optional().describe("Required when removed_task_ids is non-empty. Human-readable reason recorded " + "on each task_removed ledger event."),
112581
113210
  confirm_destructive_reset: exports_external.boolean().optional().describe("Required when reset_statuses is true AND at least one task is missing from " + "the new plan. Set true to acknowledge that the destructive reset drops " + "unfinished work. When set together with reset_statuses, save_plan auto-" + "populates removed_task_ids from the missing set."),
113211
+ confirm_identity_change: exports_external.boolean().optional().describe("When true, allows overwriting an existing plan that has a different " + "identity (swarm_id + title). Without this flag, save_plan rejects " + "with PLAN_IDENTITY_MISMATCH if the identity differs."),
112582
113212
  execution_profile: exports_external.object({
112583
113213
  parallelization_enabled: exports_external.boolean().optional().describe("When true, enables parallel task dispatch for this plan. Default false (serial)."),
112584
113214
  max_concurrent_tasks: exports_external.number().int().min(1).max(64).optional().describe("Maximum tasks that may run concurrently when parallelization is enabled. Default 1."),
@@ -112594,7 +113224,7 @@ var save_plan = createSwarmTool({
112594
113224
  init_zod();
112595
113225
  init_manager2();
112596
113226
  import * as fs94 from "node:fs";
112597
- import * as path125 from "node:path";
113227
+ import * as path126 from "node:path";
112598
113228
 
112599
113229
  // src/sbom/detectors/index.ts
112600
113230
  init_utils();
@@ -113444,7 +114074,7 @@ function findManifestFiles(rootDir) {
113444
114074
  try {
113445
114075
  const entries = fs94.readdirSync(dir, { withFileTypes: true });
113446
114076
  for (const entry of entries) {
113447
- const fullPath = path125.join(dir, entry.name);
114077
+ const fullPath = path126.join(dir, entry.name);
113448
114078
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
113449
114079
  continue;
113450
114080
  }
@@ -113453,7 +114083,7 @@ function findManifestFiles(rootDir) {
113453
114083
  } else if (entry.isFile()) {
113454
114084
  for (const pattern of patterns) {
113455
114085
  if (simpleGlobToRegex(pattern).test(entry.name)) {
113456
- manifestFiles.push(path125.relative(rootDir, fullPath));
114086
+ manifestFiles.push(path126.relative(rootDir, fullPath));
113457
114087
  break;
113458
114088
  }
113459
114089
  }
@@ -113471,11 +114101,11 @@ function findManifestFilesInDirs(directories, workingDir) {
113471
114101
  try {
113472
114102
  const entries = fs94.readdirSync(dir, { withFileTypes: true });
113473
114103
  for (const entry of entries) {
113474
- const fullPath = path125.join(dir, entry.name);
114104
+ const fullPath = path126.join(dir, entry.name);
113475
114105
  if (entry.isFile()) {
113476
114106
  for (const pattern of patterns) {
113477
114107
  if (simpleGlobToRegex(pattern).test(entry.name)) {
113478
- found.push(path125.relative(workingDir, fullPath));
114108
+ found.push(path126.relative(workingDir, fullPath));
113479
114109
  break;
113480
114110
  }
113481
114111
  }
@@ -113488,11 +114118,11 @@ function findManifestFilesInDirs(directories, workingDir) {
113488
114118
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
113489
114119
  const dirs = new Set;
113490
114120
  for (const file3 of changedFiles) {
113491
- let currentDir = path125.dirname(file3);
114121
+ let currentDir = path126.dirname(file3);
113492
114122
  while (true) {
113493
- if (currentDir && currentDir !== "." && currentDir !== path125.sep) {
113494
- dirs.add(path125.join(workingDir, currentDir));
113495
- const parent = path125.dirname(currentDir);
114123
+ if (currentDir && currentDir !== "." && currentDir !== path126.sep) {
114124
+ dirs.add(path126.join(workingDir, currentDir));
114125
+ const parent = path126.dirname(currentDir);
113496
114126
  if (parent === currentDir)
113497
114127
  break;
113498
114128
  currentDir = parent;
@@ -113576,7 +114206,7 @@ var sbom_generate = createSwarmTool({
113576
114206
  const changedFiles = obj.changed_files;
113577
114207
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
113578
114208
  const workingDir = directory;
113579
- const outputDir = path125.isAbsolute(relativeOutputDir) ? relativeOutputDir : path125.join(workingDir, relativeOutputDir);
114209
+ const outputDir = path126.isAbsolute(relativeOutputDir) ? relativeOutputDir : path126.join(workingDir, relativeOutputDir);
113580
114210
  let manifestFiles = [];
113581
114211
  if (scope === "all") {
113582
114212
  manifestFiles = findManifestFiles(workingDir);
@@ -113599,7 +114229,7 @@ var sbom_generate = createSwarmTool({
113599
114229
  const processedFiles = [];
113600
114230
  for (const manifestFile of manifestFiles) {
113601
114231
  try {
113602
- const fullPath = path125.isAbsolute(manifestFile) ? manifestFile : path125.join(workingDir, manifestFile);
114232
+ const fullPath = path126.isAbsolute(manifestFile) ? manifestFile : path126.join(workingDir, manifestFile);
113603
114233
  if (!fs94.existsSync(fullPath)) {
113604
114234
  continue;
113605
114235
  }
@@ -113616,7 +114246,7 @@ var sbom_generate = createSwarmTool({
113616
114246
  const bom = generateCycloneDX(allComponents);
113617
114247
  const bomJson = serializeCycloneDX(bom);
113618
114248
  const filename = generateSbomFilename();
113619
- const outputPath = path125.join(outputDir, filename);
114249
+ const outputPath = path126.join(outputDir, filename);
113620
114250
  fs94.writeFileSync(outputPath, bomJson, "utf-8");
113621
114251
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
113622
114252
  try {
@@ -113660,7 +114290,7 @@ var sbom_generate = createSwarmTool({
113660
114290
  init_zod();
113661
114291
  init_create_tool();
113662
114292
  import * as fs95 from "node:fs";
113663
- import * as path126 from "node:path";
114293
+ import * as path127 from "node:path";
113664
114294
  var SPEC_CANDIDATES = [
113665
114295
  "openapi.json",
113666
114296
  "openapi.yaml",
@@ -113692,12 +114322,12 @@ function normalizePath4(p) {
113692
114322
  }
113693
114323
  function discoverSpecFile(cwd, specFileArg) {
113694
114324
  if (specFileArg) {
113695
- const resolvedPath = path126.resolve(cwd, specFileArg);
113696
- const normalizedCwd = cwd.endsWith(path126.sep) ? cwd : cwd + path126.sep;
114325
+ const resolvedPath = path127.resolve(cwd, specFileArg);
114326
+ const normalizedCwd = cwd.endsWith(path127.sep) ? cwd : cwd + path127.sep;
113697
114327
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
113698
114328
  throw new Error("Invalid spec_file: path traversal detected");
113699
114329
  }
113700
- const ext = path126.extname(resolvedPath).toLowerCase();
114330
+ const ext = path127.extname(resolvedPath).toLowerCase();
113701
114331
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
113702
114332
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
113703
114333
  }
@@ -113711,7 +114341,7 @@ function discoverSpecFile(cwd, specFileArg) {
113711
114341
  return resolvedPath;
113712
114342
  }
113713
114343
  for (const candidate of SPEC_CANDIDATES) {
113714
- const candidatePath = path126.resolve(cwd, candidate);
114344
+ const candidatePath = path127.resolve(cwd, candidate);
113715
114345
  if (fs95.existsSync(candidatePath)) {
113716
114346
  const stats = fs95.statSync(candidatePath);
113717
114347
  if (stats.size <= MAX_SPEC_SIZE) {
@@ -113723,7 +114353,7 @@ function discoverSpecFile(cwd, specFileArg) {
113723
114353
  }
113724
114354
  function parseSpec(specFile) {
113725
114355
  const content = fs95.readFileSync(specFile, "utf-8");
113726
- const ext = path126.extname(specFile).toLowerCase();
114356
+ const ext = path127.extname(specFile).toLowerCase();
113727
114357
  if (ext === ".json") {
113728
114358
  return parseJsonSpec(content);
113729
114359
  }
@@ -113799,7 +114429,7 @@ function extractRoutes(cwd) {
113799
114429
  return;
113800
114430
  }
113801
114431
  for (const entry of entries) {
113802
- const fullPath = path126.join(dir, entry.name);
114432
+ const fullPath = path127.join(dir, entry.name);
113803
114433
  if (entry.isSymbolicLink()) {
113804
114434
  continue;
113805
114435
  }
@@ -113809,7 +114439,7 @@ function extractRoutes(cwd) {
113809
114439
  }
113810
114440
  walkDir(fullPath);
113811
114441
  } else if (entry.isFile()) {
113812
- const ext = path126.extname(entry.name).toLowerCase();
114442
+ const ext = path127.extname(entry.name).toLowerCase();
113813
114443
  const baseName = entry.name.toLowerCase();
113814
114444
  if (![".ts", ".js", ".mjs"].includes(ext)) {
113815
114445
  continue;
@@ -113977,7 +114607,7 @@ init_bun_compat();
113977
114607
  init_path_security();
113978
114608
  init_create_tool();
113979
114609
  import * as fs96 from "node:fs";
113980
- import * as path127 from "node:path";
114610
+ import * as path128 from "node:path";
113981
114611
  var DEFAULT_MAX_RESULTS = 100;
113982
114612
  var DEFAULT_MAX_LINES = 200;
113983
114613
  var REGEX_TIMEOUT_MS = 5000;
@@ -114013,11 +114643,11 @@ function containsWindowsAttacks3(str) {
114013
114643
  }
114014
114644
  function isPathInWorkspace3(filePath, workspace) {
114015
114645
  try {
114016
- const resolvedPath = path127.resolve(workspace, filePath);
114646
+ const resolvedPath = path128.resolve(workspace, filePath);
114017
114647
  const realWorkspace = fs96.realpathSync(workspace);
114018
114648
  const realResolvedPath = fs96.realpathSync(resolvedPath);
114019
- const relativePath = path127.relative(realWorkspace, realResolvedPath);
114020
- if (relativePath.startsWith("..") || path127.isAbsolute(relativePath)) {
114649
+ const relativePath = path128.relative(realWorkspace, realResolvedPath);
114650
+ if (relativePath.startsWith("..") || path128.isAbsolute(relativePath)) {
114021
114651
  return false;
114022
114652
  }
114023
114653
  return true;
@@ -114030,11 +114660,11 @@ function validatePathForRead2(filePath, workspace) {
114030
114660
  }
114031
114661
  function findRgInEnvPath() {
114032
114662
  const searchPath = process.env.PATH ?? "";
114033
- for (const dir of searchPath.split(path127.delimiter)) {
114663
+ for (const dir of searchPath.split(path128.delimiter)) {
114034
114664
  if (!dir)
114035
114665
  continue;
114036
114666
  const isWindows = process.platform === "win32";
114037
- const candidate = path127.join(dir, isWindows ? "rg.exe" : "rg");
114667
+ const candidate = path128.join(dir, isWindows ? "rg.exe" : "rg");
114038
114668
  if (fs96.existsSync(candidate))
114039
114669
  return candidate;
114040
114670
  }
@@ -114164,8 +114794,8 @@ function collectFiles(dir, workspace, includeGlobs, excludeGlobs) {
114164
114794
  try {
114165
114795
  const entries = fs96.readdirSync(dir, { withFileTypes: true });
114166
114796
  for (const entry of entries) {
114167
- const fullPath = path127.join(dir, entry.name);
114168
- const relativePath = path127.relative(workspace, fullPath);
114797
+ const fullPath = path128.join(dir, entry.name);
114798
+ const relativePath = path128.relative(workspace, fullPath);
114169
114799
  if (!validatePathForRead2(fullPath, workspace)) {
114170
114800
  continue;
114171
114801
  }
@@ -114206,7 +114836,7 @@ async function fallbackSearch(opts) {
114206
114836
  const matches = [];
114207
114837
  let total = 0;
114208
114838
  for (const file3 of files) {
114209
- const fullPath = path127.join(opts.workspace, file3);
114839
+ const fullPath = path128.join(opts.workspace, file3);
114210
114840
  if (!validatePathForRead2(fullPath, opts.workspace)) {
114211
114841
  continue;
114212
114842
  }
@@ -114574,7 +115204,7 @@ init_config();
114574
115204
  init_schema();
114575
115205
  init_create_tool();
114576
115206
  import { mkdir as mkdir21, rename as rename9, writeFile as writeFile16 } from "node:fs/promises";
114577
- import * as path128 from "node:path";
115207
+ import * as path129 from "node:path";
114578
115208
  var MAX_SPEC_BYTES = 256 * 1024;
114579
115209
  var spec_write = createSwarmTool({
114580
115210
  description: "Write the canonical project spec to .swarm/spec.md. Atomic write, size-bounded (256 KiB), heading-required. Honors spec_writer.allow_spec_write.",
@@ -114615,8 +115245,8 @@ var spec_write = createSwarmTool({
114615
115245
  reason: 'spec must contain at least one top-level "# Heading"'
114616
115246
  }, null, 2);
114617
115247
  }
114618
- const target = path128.join(directory, ".swarm", "spec.md");
114619
- await mkdir21(path128.dirname(target), { recursive: true });
115248
+ const target = path129.join(directory, ".swarm", "spec.md");
115249
+ await mkdir21(path129.dirname(target), { recursive: true });
114620
115250
  const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
114621
115251
  let finalContent = content;
114622
115252
  if (mode === "append") {
@@ -114645,13 +115275,13 @@ init_zod();
114645
115275
  init_loader();
114646
115276
  import {
114647
115277
  existsSync as existsSync74,
114648
- mkdirSync as mkdirSync32,
115278
+ mkdirSync as mkdirSync33,
114649
115279
  readFileSync as readFileSync64,
114650
115280
  renameSync as renameSync20,
114651
115281
  unlinkSync as unlinkSync16,
114652
115282
  writeFileSync as writeFileSync24
114653
115283
  } from "node:fs";
114654
- import path129 from "node:path";
115284
+ import path130 from "node:path";
114655
115285
  init_create_tool();
114656
115286
  init_resolve_working_directory();
114657
115287
  var VerdictSchema2 = exports_external.object({
@@ -114781,7 +115411,7 @@ var submit_phase_council_verdicts = createSwarmTool({
114781
115411
  }
114782
115412
  });
114783
115413
  function getPhaseMutationGapFinding(phaseNumber, workingDir) {
114784
- const mutationGatePath = path129.join(workingDir, ".swarm", "evidence", String(phaseNumber), "mutation-gate.json");
115414
+ const mutationGatePath = path130.join(workingDir, ".swarm", "evidence", String(phaseNumber), "mutation-gate.json");
114785
115415
  try {
114786
115416
  const raw = readFileSync64(mutationGatePath, "utf-8");
114787
115417
  const parsed = JSON.parse(raw);
@@ -114843,9 +115473,9 @@ function getPhaseMutationGapFinding(phaseNumber, workingDir) {
114843
115473
  }
114844
115474
  }
114845
115475
  function writePhaseCouncilEvidence(workingDir, synthesis) {
114846
- const evidenceDir = path129.join(workingDir, ".swarm", "evidence", String(synthesis.phaseNumber));
114847
- mkdirSync32(evidenceDir, { recursive: true });
114848
- const evidenceFile = path129.join(evidenceDir, "phase-council.json");
115476
+ const evidenceDir = path130.join(workingDir, ".swarm", "evidence", String(synthesis.phaseNumber));
115477
+ mkdirSync33(evidenceDir, { recursive: true });
115478
+ const evidenceFile = path130.join(evidenceDir, "phase-council.json");
114849
115479
  const evidenceBundle = {
114850
115480
  entries: [
114851
115481
  {
@@ -114998,16 +115628,20 @@ var swarm_memory_propose = createSwarmTool({
114998
115628
  }, {
114999
115629
  config: config3.memory
115000
115630
  });
115001
- const proposal = await gateway.propose(parsed.data);
115002
- return JSON.stringify({
115003
- success: proposal.status !== "rejected",
115004
- proposal_id: proposal.id,
115005
- status: proposal.status,
115006
- operation: proposal.operation,
115007
- memory_id: proposal.proposedRecord?.id,
115008
- rejection_reason: proposal.rejectionReason,
115009
- message: proposal.status === "pending" ? "Memory proposal created. Durable memory was not written." : "Memory proposal was captured with policy rejection metadata."
115010
- }, null, 2);
115631
+ try {
115632
+ const proposal = await gateway.propose(parsed.data);
115633
+ return JSON.stringify({
115634
+ success: proposal.status !== "rejected",
115635
+ proposal_id: proposal.id,
115636
+ status: proposal.status,
115637
+ operation: proposal.operation,
115638
+ memory_id: proposal.proposedRecord?.id,
115639
+ rejection_reason: proposal.rejectionReason,
115640
+ message: proposal.status === "pending" ? "Memory proposal created. Durable memory was not written." : "Memory proposal was captured with policy rejection metadata."
115641
+ }, null, 2);
115642
+ } finally {
115643
+ await gateway.dispose();
115644
+ }
115011
115645
  }
115012
115646
  });
115013
115647
  var _internals51 = {
@@ -115071,19 +115705,23 @@ var swarm_memory_recall = createSwarmTool({
115071
115705
  }, {
115072
115706
  config: config3.memory
115073
115707
  });
115074
- const bundle = await gateway.recall(parsed.data);
115075
- return JSON.stringify({
115076
- success: true,
115077
- bundle_id: bundle.id,
115078
- memory_ids: bundle.items.map((item) => item.record.id),
115079
- total: bundle.items.length,
115080
- token_estimate: bundle.tokenEstimate,
115081
- signals: bundle.items.map((item) => ({
115082
- memory_id: item.record.id,
115083
- ...item.signals
115084
- })),
115085
- prompt_block: bundle.promptBlock
115086
- }, null, 2);
115708
+ try {
115709
+ const bundle = await gateway.recall(parsed.data);
115710
+ return JSON.stringify({
115711
+ success: true,
115712
+ bundle_id: bundle.id,
115713
+ memory_ids: bundle.items.map((item) => item.record.id),
115714
+ total: bundle.items.length,
115715
+ token_estimate: bundle.tokenEstimate,
115716
+ signals: bundle.items.map((item) => ({
115717
+ memory_id: item.record.id,
115718
+ ...item.signals
115719
+ })),
115720
+ prompt_block: bundle.promptBlock
115721
+ }, null, 2);
115722
+ } finally {
115723
+ await gateway.dispose();
115724
+ }
115087
115725
  }
115088
115726
  });
115089
115727
  var RecallArgsSchema = exports_external.object({
@@ -115106,7 +115744,7 @@ init_zod();
115106
115744
  init_path_security();
115107
115745
  init_create_tool();
115108
115746
  import * as fs97 from "node:fs";
115109
- import * as path130 from "node:path";
115747
+ import * as path131 from "node:path";
115110
115748
  var WINDOWS_RESERVED_NAMES4 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
115111
115749
  function containsWindowsAttacks4(str) {
115112
115750
  if (/:[^\\/]/.test(str))
@@ -115120,14 +115758,14 @@ function containsWindowsAttacks4(str) {
115120
115758
  }
115121
115759
  function isPathInWorkspace4(filePath, workspace) {
115122
115760
  try {
115123
- const resolvedPath = path130.resolve(workspace, filePath);
115761
+ const resolvedPath = path131.resolve(workspace, filePath);
115124
115762
  if (!fs97.existsSync(resolvedPath)) {
115125
115763
  return true;
115126
115764
  }
115127
115765
  const realWorkspace = fs97.realpathSync(workspace);
115128
115766
  const realResolvedPath = fs97.realpathSync(resolvedPath);
115129
- const relativePath = path130.relative(realWorkspace, realResolvedPath);
115130
- if (relativePath.startsWith("..") || path130.isAbsolute(relativePath)) {
115767
+ const relativePath = path131.relative(realWorkspace, realResolvedPath);
115768
+ if (relativePath.startsWith("..") || path131.isAbsolute(relativePath)) {
115131
115769
  return false;
115132
115770
  }
115133
115771
  return true;
@@ -115335,7 +115973,7 @@ var suggestPatch = createSwarmTool({
115335
115973
  });
115336
115974
  continue;
115337
115975
  }
115338
- const fullPath = path130.resolve(directory, change.file);
115976
+ const fullPath = path131.resolve(directory, change.file);
115339
115977
  if (!fs97.existsSync(fullPath)) {
115340
115978
  errors5.push({
115341
115979
  success: false,
@@ -115639,11 +116277,11 @@ var lean_turbo_acquire_locks = createSwarmTool({
115639
116277
  init_zod();
115640
116278
  init_constants();
115641
116279
  import * as fs99 from "node:fs";
115642
- import * as path132 from "node:path";
116280
+ import * as path133 from "node:path";
115643
116281
 
115644
116282
  // src/turbo/lean/conflicts.ts
115645
116283
  import * as fs98 from "node:fs";
115646
- import * as path131 from "node:path";
116284
+ import * as path132 from "node:path";
115647
116285
  var DEFAULT_GLOBAL_FILES = [
115648
116286
  "package.json",
115649
116287
  "package-lock.json",
@@ -115770,7 +116408,7 @@ function isProtectedPath2(normalizedPath) {
115770
116408
  return false;
115771
116409
  }
115772
116410
  function readTaskScopes(directory, taskId) {
115773
- const scopePath = path131.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
116411
+ const scopePath = path132.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
115774
116412
  try {
115775
116413
  if (!fs98.existsSync(scopePath)) {
115776
116414
  return null;
@@ -116158,7 +116796,7 @@ function createEmptyPlan(phaseNumber, planId) {
116158
116796
  // src/tools/lean-turbo-plan-lanes.ts
116159
116797
  init_create_tool();
116160
116798
  function readPlanJson(directory) {
116161
- const planPath = path132.join(directory, ".swarm", "plan.json");
116799
+ const planPath = path133.join(directory, ".swarm", "plan.json");
116162
116800
  if (!fs99.existsSync(planPath)) {
116163
116801
  return null;
116164
116802
  }
@@ -116213,7 +116851,7 @@ init_config();
116213
116851
  // src/turbo/lean/reviewer.ts
116214
116852
  init_state();
116215
116853
  import * as fs100 from "node:fs/promises";
116216
- import * as path133 from "node:path";
116854
+ import * as path134 from "node:path";
116217
116855
  init_state3();
116218
116856
  var DEFAULT_CONFIG3 = {
116219
116857
  reviewerAgent: "",
@@ -116329,9 +116967,9 @@ function parseReviewerVerdict(responseText) {
116329
116967
  return { verdict, reason };
116330
116968
  }
116331
116969
  async function writeReviewerEvidence(directory, phase, verdict, reason) {
116332
- const evidenceDir = path133.join(directory, ".swarm", "evidence", String(phase));
116970
+ const evidenceDir = path134.join(directory, ".swarm", "evidence", String(phase));
116333
116971
  await fs100.mkdir(evidenceDir, { recursive: true });
116334
- const evidencePath = path133.join(evidenceDir, "lean-turbo-reviewer.json");
116972
+ const evidencePath = path134.join(evidenceDir, "lean-turbo-reviewer.json");
116335
116973
  const content = JSON.stringify({
116336
116974
  phase,
116337
116975
  verdict,
@@ -117136,7 +117774,7 @@ var lean_turbo_status = createSwarmTool({
117136
117774
  init_spec_schema();
117137
117775
  init_create_tool();
117138
117776
  import * as fs101 from "node:fs";
117139
- import * as path134 from "node:path";
117777
+ import * as path135 from "node:path";
117140
117778
  var SPEC_FILE_NAME = "spec.md";
117141
117779
  var SWARM_DIR2 = ".swarm";
117142
117780
  var OBLIGATION_KEYWORDS2 = ["MUST", "SHALL", "SHOULD", "MAY"];
@@ -117189,7 +117827,7 @@ var lint_spec = createSwarmTool({
117189
117827
  async execute(_args, directory) {
117190
117828
  const errors5 = [];
117191
117829
  const warnings = [];
117192
- const specPath = path134.join(directory, SWARM_DIR2, SPEC_FILE_NAME);
117830
+ const specPath = path135.join(directory, SWARM_DIR2, SPEC_FILE_NAME);
117193
117831
  if (!fs101.existsSync(specPath)) {
117194
117832
  const result2 = {
117195
117833
  valid: false,
@@ -117260,12 +117898,12 @@ var lint_spec = createSwarmTool({
117260
117898
  // src/tools/mutation-test.ts
117261
117899
  init_zod();
117262
117900
  import * as fs102 from "node:fs";
117263
- import * as path136 from "node:path";
117901
+ import * as path137 from "node:path";
117264
117902
 
117265
117903
  // src/mutation/engine.ts
117266
117904
  import { spawnSync as spawnSync3 } from "node:child_process";
117267
117905
  import { unlinkSync as unlinkSync17, writeFileSync as writeFileSync25 } from "node:fs";
117268
- import * as path135 from "node:path";
117906
+ import * as path136 from "node:path";
117269
117907
 
117270
117908
  // src/mutation/equivalence.ts
117271
117909
  function isStaticallyEquivalent(originalCode, mutatedCode) {
@@ -117411,7 +118049,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
117411
118049
  let patchFile;
117412
118050
  try {
117413
118051
  const safeId2 = patch.id.replace(/[^a-zA-Z0-9_-]/g, "_");
117414
- patchFile = path135.join(workingDir, `.mutation_patch_${safeId2}.diff`);
118052
+ patchFile = path136.join(workingDir, `.mutation_patch_${safeId2}.diff`);
117415
118053
  try {
117416
118054
  writeFileSync25(patchFile, patch.patch);
117417
118055
  } catch (writeErr) {
@@ -117815,7 +118453,7 @@ var mutation_test = createSwarmTool({
117815
118453
  ];
117816
118454
  for (const filePath of uniquePaths) {
117817
118455
  try {
117818
- const resolvedPath = path136.resolve(cwd, filePath);
118456
+ const resolvedPath = path137.resolve(cwd, filePath);
117819
118457
  sourceFiles.set(filePath, fs102.readFileSync(resolvedPath, "utf-8"));
117820
118458
  } catch {}
117821
118459
  }
@@ -117835,7 +118473,7 @@ init_zod();
117835
118473
  init_manager2();
117836
118474
  init_detector();
117837
118475
  import * as fs103 from "node:fs";
117838
- import * as path137 from "node:path";
118476
+ import * as path138 from "node:path";
117839
118477
  init_create_tool();
117840
118478
  var MAX_FILE_SIZE2 = 2 * 1024 * 1024;
117841
118479
  var BINARY_CHECK_BYTES = 8192;
@@ -117901,7 +118539,7 @@ async function syntaxCheck(input, directory, config3) {
117901
118539
  if (languages?.length) {
117902
118540
  const lowerLangs = languages.map((l) => l.toLowerCase());
117903
118541
  filesToCheck = filesToCheck.filter((file3) => {
117904
- const ext = path137.extname(file3.path).toLowerCase();
118542
+ const ext = path138.extname(file3.path).toLowerCase();
117905
118543
  const langDef = getLanguageForExtension(ext);
117906
118544
  const fileProfile = getProfileForFile(file3.path);
117907
118545
  const langId = fileProfile?.id || langDef?.id;
@@ -117914,7 +118552,7 @@ async function syntaxCheck(input, directory, config3) {
117914
118552
  let skippedCount = 0;
117915
118553
  for (const fileInfo of filesToCheck) {
117916
118554
  const { path: filePath } = fileInfo;
117917
- const fullPath = path137.isAbsolute(filePath) ? filePath : path137.join(directory, filePath);
118555
+ const fullPath = path138.isAbsolute(filePath) ? filePath : path138.join(directory, filePath);
117918
118556
  const result = {
117919
118557
  path: filePath,
117920
118558
  language: "",
@@ -117963,7 +118601,7 @@ async function syntaxCheck(input, directory, config3) {
117963
118601
  results.push(result);
117964
118602
  continue;
117965
118603
  }
117966
- const ext = path137.extname(filePath).toLowerCase();
118604
+ const ext = path138.extname(filePath).toLowerCase();
117967
118605
  const langDef = getLanguageForExtension(ext);
117968
118606
  result.language = profile?.id || langDef?.id || "unknown";
117969
118607
  const errors5 = extractSyntaxErrors(parser, content);
@@ -118061,7 +118699,7 @@ init_utils();
118061
118699
  init_create_tool();
118062
118700
  init_path_security();
118063
118701
  import * as fs104 from "node:fs";
118064
- import * as path138 from "node:path";
118702
+ import * as path139 from "node:path";
118065
118703
  var MAX_TEXT_LENGTH = 200;
118066
118704
  var MAX_FILE_SIZE_BYTES11 = 1024 * 1024;
118067
118705
  var SUPPORTED_EXTENSIONS4 = new Set([
@@ -118127,9 +118765,9 @@ function validatePathsInput(paths, cwd) {
118127
118765
  return { error: "paths contains path traversal", resolvedPath: null };
118128
118766
  }
118129
118767
  try {
118130
- const resolvedPath = path138.resolve(paths);
118131
- const normalizedCwd = path138.resolve(cwd);
118132
- const normalizedResolved = path138.resolve(resolvedPath);
118768
+ const resolvedPath = path139.resolve(paths);
118769
+ const normalizedCwd = path139.resolve(cwd);
118770
+ const normalizedResolved = path139.resolve(resolvedPath);
118133
118771
  if (!normalizedResolved.startsWith(normalizedCwd)) {
118134
118772
  return {
118135
118773
  error: "paths must be within the current working directory",
@@ -118145,7 +118783,7 @@ function validatePathsInput(paths, cwd) {
118145
118783
  }
118146
118784
  }
118147
118785
  function isSupportedExtension(filePath) {
118148
- const ext = path138.extname(filePath).toLowerCase();
118786
+ const ext = path139.extname(filePath).toLowerCase();
118149
118787
  return SUPPORTED_EXTENSIONS4.has(ext);
118150
118788
  }
118151
118789
  function findSourceFiles3(dir, files = []) {
@@ -118160,7 +118798,7 @@ function findSourceFiles3(dir, files = []) {
118160
118798
  if (SKIP_DIRECTORIES5.has(entry)) {
118161
118799
  continue;
118162
118800
  }
118163
- const fullPath = path138.join(dir, entry);
118801
+ const fullPath = path139.join(dir, entry);
118164
118802
  let stat8;
118165
118803
  try {
118166
118804
  stat8 = fs104.statSync(fullPath);
@@ -118272,7 +118910,7 @@ var todo_extract = createSwarmTool({
118272
118910
  filesToScan.push(scanPath);
118273
118911
  } else {
118274
118912
  const errorResult = {
118275
- error: `unsupported file extension: ${path138.extname(scanPath)}`,
118913
+ error: `unsupported file extension: ${path139.extname(scanPath)}`,
118276
118914
  total: 0,
118277
118915
  byPriority: { high: 0, medium: 0, low: 0 },
118278
118916
  entries: []
@@ -118321,17 +118959,17 @@ init_schema();
118321
118959
  init_qa_gate_profile();
118322
118960
  init_gate_evidence();
118323
118961
  import * as fs108 from "node:fs";
118324
- import * as path142 from "node:path";
118962
+ import * as path143 from "node:path";
118325
118963
 
118326
118964
  // src/hooks/diff-scope.ts
118327
118965
  init_bun_compat();
118328
118966
  import * as fs106 from "node:fs";
118329
- import * as path140 from "node:path";
118967
+ import * as path141 from "node:path";
118330
118968
 
118331
118969
  // src/utils/gitignore-warning.ts
118332
118970
  init_bun_compat();
118333
118971
  import * as fs105 from "node:fs";
118334
- import * as path139 from "node:path";
118972
+ import * as path140 from "node:path";
118335
118973
  var _internals58 = { bunSpawn };
118336
118974
  var _swarmGitExcludedChecked = false;
118337
118975
  function fileCoversSwarm(content) {
@@ -118405,10 +119043,10 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
118405
119043
  const excludeRelPath = excludePathRaw.trim();
118406
119044
  if (!excludeRelPath)
118407
119045
  return;
118408
- const excludePath = path139.isAbsolute(excludeRelPath) ? excludeRelPath : path139.join(directory, excludeRelPath);
119046
+ const excludePath = path140.isAbsolute(excludeRelPath) ? excludeRelPath : path140.join(directory, excludeRelPath);
118409
119047
  if (checkIgnoreExitCode !== 0) {
118410
119048
  try {
118411
- fs105.mkdirSync(path139.dirname(excludePath), { recursive: true });
119049
+ fs105.mkdirSync(path140.dirname(excludePath), { recursive: true });
118412
119050
  let existing = "";
118413
119051
  try {
118414
119052
  existing = fs105.readFileSync(excludePath, "utf8");
@@ -118452,7 +119090,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
118452
119090
  var _internals59 = { bunSpawn };
118453
119091
  function getDeclaredScope(taskId, directory) {
118454
119092
  try {
118455
- const planPath = path140.join(directory, ".swarm", "plan.json");
119093
+ const planPath = path141.join(directory, ".swarm", "plan.json");
118456
119094
  if (!fs106.existsSync(planPath))
118457
119095
  return null;
118458
119096
  const raw = fs106.readFileSync(planPath, "utf-8");
@@ -118558,7 +119196,7 @@ init_telemetry();
118558
119196
  // src/turbo/lean/task-completion.ts
118559
119197
  init_file_locks();
118560
119198
  import * as fs107 from "node:fs";
118561
- import * as path141 from "node:path";
119199
+ import * as path142 from "node:path";
118562
119200
  var _internals60 = {
118563
119201
  listActiveLocks,
118564
119202
  verifyLeanTurboTaskCompletion
@@ -118577,7 +119215,7 @@ var TIER_3_PATTERNS = [
118577
119215
  ];
118578
119216
  function matchesTier3Pattern(files) {
118579
119217
  for (const file3 of files) {
118580
- const fileName = path141.basename(file3);
119218
+ const fileName = path142.basename(file3);
118581
119219
  for (const pattern of TIER_3_PATTERNS) {
118582
119220
  if (pattern.test(fileName)) {
118583
119221
  return true;
@@ -118589,7 +119227,7 @@ function matchesTier3Pattern(files) {
118589
119227
  function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
118590
119228
  let persisted = null;
118591
119229
  try {
118592
- const statePath = path141.join(directory, ".swarm", "turbo-state.json");
119230
+ const statePath = path142.join(directory, ".swarm", "turbo-state.json");
118593
119231
  if (!fs107.existsSync(statePath)) {
118594
119232
  return {
118595
119233
  ok: false,
@@ -118673,11 +119311,11 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
118673
119311
  };
118674
119312
  }
118675
119313
  const phase = runState.phase ?? 0;
118676
- const evidencePath = path141.join(directory, ".swarm", "evidence", String(phase), "lean-turbo", `${lane.laneId}.json`);
118677
- const expectedDir = path141.join(directory, ".swarm", "evidence", String(phase), "lean-turbo");
118678
- const resolvedPath = path141.resolve(evidencePath);
118679
- const resolvedDir = path141.resolve(expectedDir);
118680
- if (!resolvedPath.startsWith(resolvedDir + path141.sep) && resolvedPath !== resolvedDir) {
119314
+ const evidencePath = path142.join(directory, ".swarm", "evidence", String(phase), "lean-turbo", `${lane.laneId}.json`);
119315
+ const expectedDir = path142.join(directory, ".swarm", "evidence", String(phase), "lean-turbo");
119316
+ const resolvedPath = path142.resolve(evidencePath);
119317
+ const resolvedDir = path142.resolve(expectedDir);
119318
+ if (!resolvedPath.startsWith(resolvedDir + path142.sep) && resolvedPath !== resolvedDir) {
118681
119319
  return {
118682
119320
  ok: false,
118683
119321
  reason: `Lane ID causes path traversal: ${lane.laneId}`,
@@ -118717,7 +119355,7 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
118717
119355
  }
118718
119356
  let filesTouched = [];
118719
119357
  try {
118720
- const planPath = path141.join(directory, ".swarm", "plan.json");
119358
+ const planPath = path142.join(directory, ".swarm", "plan.json");
118721
119359
  const planRaw = fs107.readFileSync(planPath, "utf-8");
118722
119360
  const plan = JSON.parse(planRaw);
118723
119361
  for (const planPhase of plan.phases ?? []) {
@@ -118801,7 +119439,7 @@ var TIER_3_PATTERNS2 = [
118801
119439
  ];
118802
119440
  function matchesTier3Pattern2(files) {
118803
119441
  for (const file3 of files) {
118804
- const fileName = path142.basename(file3);
119442
+ const fileName = path143.basename(file3);
118805
119443
  for (const pattern of TIER_3_PATTERNS2) {
118806
119444
  if (pattern.test(fileName)) {
118807
119445
  return true;
@@ -118840,7 +119478,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
118840
119478
  if (!skipStandardTurboBypass && hasActiveTurboMode()) {
118841
119479
  const resolvedDir2 = workingDirectory;
118842
119480
  try {
118843
- const planPath = path142.join(resolvedDir2, ".swarm", "plan.json");
119481
+ const planPath = path143.join(resolvedDir2, ".swarm", "plan.json");
118844
119482
  const planRaw = fs108.readFileSync(planPath, "utf-8");
118845
119483
  const plan = JSON.parse(planRaw);
118846
119484
  for (const planPhase of plan.phases ?? []) {
@@ -118918,7 +119556,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
118918
119556
  }
118919
119557
  if (resolvedDir) {
118920
119558
  try {
118921
- const planPath = path142.join(resolvedDir, ".swarm", "plan.json");
119559
+ const planPath = path143.join(resolvedDir, ".swarm", "plan.json");
118922
119560
  const planRaw = fs108.readFileSync(planPath, "utf-8");
118923
119561
  const plan = JSON.parse(planRaw);
118924
119562
  for (const planPhase of plan.phases ?? []) {
@@ -119155,8 +119793,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
119155
119793
  };
119156
119794
  }
119157
119795
  }
119158
- normalizedDir = path142.normalize(args2.working_directory);
119159
- const pathParts = normalizedDir.split(path142.sep);
119796
+ normalizedDir = path143.normalize(args2.working_directory);
119797
+ const pathParts = normalizedDir.split(path143.sep);
119160
119798
  if (pathParts.includes("..")) {
119161
119799
  return {
119162
119800
  success: false,
@@ -119166,10 +119804,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
119166
119804
  ]
119167
119805
  };
119168
119806
  }
119169
- const resolvedDir = path142.resolve(normalizedDir);
119807
+ const resolvedDir = path143.resolve(normalizedDir);
119170
119808
  try {
119171
119809
  const realPath = fs108.realpathSync(resolvedDir);
119172
- const planPath = path142.join(realPath, ".swarm", "plan.json");
119810
+ const planPath = path143.join(realPath, ".swarm", "plan.json");
119173
119811
  if (!fs108.existsSync(planPath)) {
119174
119812
  return {
119175
119813
  success: false,
@@ -119200,9 +119838,9 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
119200
119838
  directory = fallbackDir;
119201
119839
  }
119202
119840
  if (fallbackDir && directory !== fallbackDir) {
119203
- const canonicalDir = fs108.realpathSync(path142.resolve(directory));
119204
- const canonicalRoot = fs108.realpathSync(path142.resolve(fallbackDir));
119205
- if (canonicalDir.startsWith(canonicalRoot + path142.sep)) {
119841
+ const canonicalDir = fs108.realpathSync(path143.resolve(directory));
119842
+ const canonicalRoot = fs108.realpathSync(path143.resolve(fallbackDir));
119843
+ if (canonicalDir.startsWith(canonicalRoot + path143.sep)) {
119206
119844
  return {
119207
119845
  success: false,
119208
119846
  message: `Invalid working_directory: "${directory}" is a subdirectory of ` + `the project root "${fallbackDir}". Pass the project root path or ` + `omit working_directory entirely.`,
@@ -119214,8 +119852,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
119214
119852
  }
119215
119853
  if (args2.status === "in_progress") {
119216
119854
  try {
119217
- const evidencePath = path142.join(directory, ".swarm", "evidence", `${args2.task_id}.json`);
119218
- fs108.mkdirSync(path142.dirname(evidencePath), { recursive: true });
119855
+ const evidencePath = path143.join(directory, ".swarm", "evidence", `${args2.task_id}.json`);
119856
+ fs108.mkdirSync(path143.dirname(evidencePath), { recursive: true });
119219
119857
  const fd = fs108.openSync(evidencePath, "wx");
119220
119858
  let writeOk = false;
119221
119859
  try {
@@ -119239,7 +119877,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
119239
119877
  recoverTaskStateFromDelegations(args2.task_id, directory);
119240
119878
  let phaseRequiresReviewer = true;
119241
119879
  try {
119242
- const planPath = path142.join(directory, ".swarm", "plan.json");
119880
+ const planPath = path143.join(directory, ".swarm", "plan.json");
119243
119881
  const planRaw = fs108.readFileSync(planPath, "utf-8");
119244
119882
  const plan = JSON.parse(planRaw);
119245
119883
  const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
@@ -119551,7 +120189,7 @@ init_ledger();
119551
120189
  init_manager();
119552
120190
  init_create_tool();
119553
120191
  import fs109 from "node:fs";
119554
- import path143 from "node:path";
120192
+ import path144 from "node:path";
119555
120193
  function normalizeVerdict(verdict) {
119556
120194
  switch (verdict) {
119557
120195
  case "APPROVED":
@@ -119599,7 +120237,7 @@ async function executeWriteDriftEvidence(args2, directory) {
119599
120237
  entries: [evidenceEntry]
119600
120238
  };
119601
120239
  const filename = "drift-verifier.json";
119602
- const relativePath = path143.join("evidence", String(phase), filename);
120240
+ const relativePath = path144.join("evidence", String(phase), filename);
119603
120241
  let validatedPath;
119604
120242
  try {
119605
120243
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -119610,10 +120248,10 @@ async function executeWriteDriftEvidence(args2, directory) {
119610
120248
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
119611
120249
  }, null, 2);
119612
120250
  }
119613
- const evidenceDir = path143.dirname(validatedPath);
120251
+ const evidenceDir = path144.dirname(validatedPath);
119614
120252
  try {
119615
120253
  await fs109.promises.mkdir(evidenceDir, { recursive: true });
119616
- const tempPath = path143.join(evidenceDir, `.${filename}.tmp`);
120254
+ const tempPath = path144.join(evidenceDir, `.${filename}.tmp`);
119617
120255
  await fs109.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
119618
120256
  await fs109.promises.rename(tempPath, validatedPath);
119619
120257
  let snapshotInfo;
@@ -119709,7 +120347,7 @@ var write_drift_evidence = createSwarmTool({
119709
120347
  init_zod();
119710
120348
  init_loader();
119711
120349
  import fs110 from "node:fs";
119712
- import path144 from "node:path";
120350
+ import path145 from "node:path";
119713
120351
  init_utils2();
119714
120352
  init_manager();
119715
120353
  init_create_tool();
@@ -119797,7 +120435,7 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
119797
120435
  timestamp: synthesis.timestamp
119798
120436
  };
119799
120437
  const filename = "final-council.json";
119800
- const relativePath = path144.join("evidence", filename);
120438
+ const relativePath = path145.join("evidence", filename);
119801
120439
  let validatedPath;
119802
120440
  try {
119803
120441
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -119811,10 +120449,10 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
119811
120449
  const evidenceContent = {
119812
120450
  entries: [evidenceEntry]
119813
120451
  };
119814
- const evidenceDir = path144.dirname(validatedPath);
120452
+ const evidenceDir = path145.dirname(validatedPath);
119815
120453
  try {
119816
120454
  await fs110.promises.mkdir(evidenceDir, { recursive: true });
119817
- const tempPath = path144.join(evidenceDir, `.${filename}.tmp`);
120455
+ const tempPath = path145.join(evidenceDir, `.${filename}.tmp`);
119818
120456
  await fs110.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
119819
120457
  await fs110.promises.rename(tempPath, validatedPath);
119820
120458
  return JSON.stringify({
@@ -119873,7 +120511,7 @@ init_zod();
119873
120511
  init_utils2();
119874
120512
  init_create_tool();
119875
120513
  import fs111 from "node:fs";
119876
- import path145 from "node:path";
120514
+ import path146 from "node:path";
119877
120515
  function normalizeVerdict2(verdict) {
119878
120516
  switch (verdict) {
119879
120517
  case "APPROVED":
@@ -119921,7 +120559,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
119921
120559
  entries: [evidenceEntry]
119922
120560
  };
119923
120561
  const filename = "hallucination-guard.json";
119924
- const relativePath = path145.join("evidence", String(phase), filename);
120562
+ const relativePath = path146.join("evidence", String(phase), filename);
119925
120563
  let validatedPath;
119926
120564
  try {
119927
120565
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -119932,10 +120570,10 @@ async function executeWriteHallucinationEvidence(args2, directory) {
119932
120570
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
119933
120571
  }, null, 2);
119934
120572
  }
119935
- const evidenceDir = path145.dirname(validatedPath);
120573
+ const evidenceDir = path146.dirname(validatedPath);
119936
120574
  try {
119937
120575
  await fs111.promises.mkdir(evidenceDir, { recursive: true });
119938
- const tempPath = path145.join(evidenceDir, `.${filename}.tmp`);
120576
+ const tempPath = path146.join(evidenceDir, `.${filename}.tmp`);
119939
120577
  await fs111.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
119940
120578
  await fs111.promises.rename(tempPath, validatedPath);
119941
120579
  return JSON.stringify({
@@ -119984,7 +120622,7 @@ init_zod();
119984
120622
  init_utils2();
119985
120623
  init_create_tool();
119986
120624
  import fs112 from "node:fs";
119987
- import path146 from "node:path";
120625
+ import path147 from "node:path";
119988
120626
  function normalizeVerdict3(verdict) {
119989
120627
  switch (verdict) {
119990
120628
  case "PASS":
@@ -120058,7 +120696,7 @@ async function executeWriteMutationEvidence(args2, directory) {
120058
120696
  entries: [evidenceEntry]
120059
120697
  };
120060
120698
  const filename = "mutation-gate.json";
120061
- const relativePath = path146.join("evidence", String(phase), filename);
120699
+ const relativePath = path147.join("evidence", String(phase), filename);
120062
120700
  let validatedPath;
120063
120701
  try {
120064
120702
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -120069,10 +120707,10 @@ async function executeWriteMutationEvidence(args2, directory) {
120069
120707
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
120070
120708
  }, null, 2);
120071
120709
  }
120072
- const evidenceDir = path146.dirname(validatedPath);
120710
+ const evidenceDir = path147.dirname(validatedPath);
120073
120711
  try {
120074
120712
  await fs112.promises.mkdir(evidenceDir, { recursive: true });
120075
- const tempPath = path146.join(evidenceDir, `.${filename}.tmp`);
120713
+ const tempPath = path147.join(evidenceDir, `.${filename}.tmp`);
120076
120714
  await fs112.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
120077
120715
  await fs112.promises.rename(tempPath, validatedPath);
120078
120716
  return JSON.stringify({
@@ -120422,7 +121060,7 @@ async function initializeOpenCodeSwarm(ctx) {
120422
121060
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
120423
121061
  preflightTriggerManager = new PTM(automationConfig);
120424
121062
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
120425
- const swarmDir = path148.resolve(ctx.directory, ".swarm");
121063
+ const swarmDir = path149.resolve(ctx.directory, ".swarm");
120426
121064
  statusArtifact = new ASA(swarmDir);
120427
121065
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
120428
121066
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -120601,30 +121239,29 @@ async function initializeOpenCodeSwarm(ctx) {
120601
121239
  swarm_command: createSwarmCommandTool(agentDefinitionMap)
120602
121240
  },
120603
121241
  config: async (opencodeConfig) => {
120604
- if (!opencodeConfig.agent || typeof opencodeConfig.agent !== "object") {
120605
- opencodeConfig.agent = {};
120606
- }
120607
- if (!opencodeConfig.agent) {
120608
- opencodeConfig.agent = { ...agents };
120609
- } else {
120610
- Object.assign(opencodeConfig.agent, agents);
121242
+ const isObjectRecord = (value) => typeof value === "object" && value !== null;
121243
+ const pluginConfig = opencodeConfig;
121244
+ if (!isObjectRecord(pluginConfig.agent)) {
121245
+ pluginConfig.agent = {};
120611
121246
  }
121247
+ const agentConfig = pluginConfig.agent;
121248
+ Object.assign(agentConfig, agents);
120612
121249
  const autoSelect = config3?.auto_select_architect;
120613
121250
  if (autoSelect) {
120614
121251
  const hasArchitect = Object.keys(agents).some((name2) => stripKnownSwarmPrefix(name2) === "architect");
120615
121252
  if (hasArchitect) {
120616
121253
  for (const builtin of ["build", "plan"]) {
120617
- const existing = opencodeConfig.agent?.[builtin];
120618
- if (existing && typeof existing === "object" && existing.disable === true) {
121254
+ const existing = agentConfig[builtin];
121255
+ if (isObjectRecord(existing) && existing.disable === true) {
120619
121256
  continue;
120620
121257
  }
120621
- opencodeConfig.agent[builtin] = {
120622
- ...existing && typeof existing === "object" ? existing : {},
121258
+ agentConfig[builtin] = {
121259
+ ...isObjectRecord(existing) ? existing : {},
120623
121260
  disable: true
120624
121261
  };
120625
121262
  }
120626
121263
  if (autoSelect === true) {
120627
- const primaryArchitects = Object.entries(agents).filter(([name2, cfg]) => stripKnownSwarmPrefix(name2) === "architect" && cfg.mode === "primary");
121264
+ const primaryArchitects = Object.entries(agents).filter(([name2, cfg]) => stripKnownSwarmPrefix(name2) === "architect" && isObjectRecord(cfg) && cfg.mode === "primary");
120628
121265
  if (primaryArchitects.length > 1) {
120629
121266
  const names = primaryArchitects.map(([n]) => n).join(", ");
120630
121267
  addDeferredWarning(`[swarm] auto_select_architect is true but ${primaryArchitects.length} architect agents are primary (${names}). Consider setting auto_select_architect to a specific agent name.`);
@@ -120636,22 +121273,19 @@ async function initializeOpenCodeSwarm(ctx) {
120636
121273
  if (targetIsArchitect) {
120637
121274
  for (const [name2, cfg] of Object.entries(agents)) {
120638
121275
  if (stripKnownSwarmPrefix(name2) === "architect" && name2 !== targetName) {
120639
- if (opencodeConfig.agent && typeof opencodeConfig.agent === "object") {
120640
- opencodeConfig.agent[name2] = {
120641
- ...cfg && typeof cfg === "object" ? cfg : {},
120642
- mode: "subagent"
120643
- };
120644
- }
121276
+ agentConfig[name2] = {
121277
+ ...isObjectRecord(cfg) ? cfg : {},
121278
+ mode: "subagent"
121279
+ };
120645
121280
  }
120646
121281
  }
120647
- if (opencodeConfig.agent && typeof opencodeConfig.agent === "object") {
120648
- const targetExisting = opencodeConfig.agent[targetName];
120649
- opencodeConfig.agent[targetName] = {
120650
- ...targetExisting && typeof targetExisting === "object" ? targetExisting : {},
120651
- ...agents[targetName] && typeof agents[targetName] === "object" ? agents[targetName] : {},
120652
- mode: "primary"
120653
- };
120654
- }
121282
+ const targetExisting = agentConfig[targetName];
121283
+ const targetAgent = agents[targetName];
121284
+ agentConfig[targetName] = {
121285
+ ...isObjectRecord(targetExisting) ? targetExisting : {},
121286
+ ...isObjectRecord(targetAgent) ? targetAgent : {},
121287
+ mode: "primary"
121288
+ };
120655
121289
  } else {
120656
121290
  addDeferredWarning(`[swarm] auto_select_architect is set to "${targetName}" but that is not a known architect agent. No architect demotion applied.`);
120657
121291
  }
@@ -121006,7 +121640,7 @@ ${promptRaw}`;
121006
121640
  "ci-failure-resolver": "CI/CD failure resolution"
121007
121641
  };
121008
121642
  const skillPaths = topSkills.map((s) => {
121009
- const dirName = path148.basename(path148.dirname(s.skillPath));
121643
+ const dirName = path149.basename(path149.dirname(s.skillPath));
121010
121644
  const desc = SKILL_DESCRIPTIONS[dirName] ?? dirName;
121011
121645
  return `file:${s.skillPath} (-- ${desc})`;
121012
121646
  }).join(", ");
@@ -121015,7 +121649,7 @@ ${promptRaw}`;
121015
121649
 
121016
121650
  ${promptRaw}`;
121017
121651
  argsRecord.prompt = newPrompt;
121018
- const skillNames = topSkills.map((s) => `${path148.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
121652
+ const skillNames = topSkills.map((s) => `${path149.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
121019
121653
  console.warn(`[skill-propagation-gate] Injected skills: ${skillNames}`);
121020
121654
  for (const skill of topSkills) {
121021
121655
  try {