opencode-swarm 7.114.7 → 7.114.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/.opencode/skills/engineering-conventions/SKILL.md +33 -0
  2. package/README.md +15 -14
  3. package/dist/agents/template.d.ts +40 -20
  4. package/dist/cli/{curator-llm-factory-2xgznxyw.js → curator-llm-factory-ez48eq02.js} +7 -7
  5. package/dist/cli/{curator-qshyr2tc.js → curator-qj970412.js} +7 -7
  6. package/dist/cli/{evidence-summary-service-8g594znj.js → evidence-summary-service-fehwj116.js} +1 -1
  7. package/dist/cli/{guardrail-explain-4t8fnysh.js → guardrail-explain-x2vaxp6s.js} +8 -8
  8. package/dist/cli/{hive-promoter-5ngbmnrw.js → hive-promoter-ysk5edzw.js} +7 -7
  9. package/dist/cli/{index-7stsmndb.js → index-6f6y2rbp.js} +3 -3
  10. package/dist/cli/{index-kjbfry6m.js → index-8f2270hc.js} +1 -1
  11. package/dist/cli/{index-6ctejje2.js → index-cs5765s2.js} +86 -63
  12. package/dist/cli/{index-qrnhvhmg.js → index-n18yy0z1.js} +26 -2
  13. package/dist/cli/{index-91qesget.js → index-rk6qhyng.js} +2 -2
  14. package/dist/cli/{index-gmjrwz34.js → index-ry8nwsq6.js} +1 -1
  15. package/dist/cli/{index-vjvdvjd3.js → index-x12hvfpf.js} +3 -3
  16. package/dist/cli/{index-f6y341yk.js → index-yrrqs4b0.js} +82 -21
  17. package/dist/cli/{index-z01r5483.js → index-z718bgxe.js} +8 -8
  18. package/dist/cli/index.d.ts +34 -0
  19. package/dist/cli/index.js +105 -28
  20. package/dist/cli/{knowledge-escalator-qn7ew687.js → knowledge-escalator-5gwp1ar8.js} +3 -3
  21. package/dist/cli/{knowledge-events-k2xsz5bh.js → knowledge-events-m304swh6.js} +1 -1
  22. package/dist/cli/{knowledge-store-ksa1dr2z.js → knowledge-store-9babt8rd.js} +1 -1
  23. package/dist/cli/{knowledge-validator-2knz0d2t.js → knowledge-validator-t9sym59g.js} +4 -2
  24. package/dist/cli/{skill-generator-w6qd9mde.js → skill-generator-tav44xpm.js} +4 -4
  25. package/dist/config/skill-mirrors.d.ts +12 -0
  26. package/dist/hooks/adversarial-detector.d.ts +6 -0
  27. package/dist/hooks/delegation-gate.d.ts +19 -0
  28. package/dist/hooks/guardrails/index.d.ts +2 -2
  29. package/dist/hooks/knowledge-validator.d.ts +22 -0
  30. package/dist/index.d.ts +7 -0
  31. package/dist/index.js +74 -70
  32. package/dist/plan/ledger.d.ts +83 -20
  33. package/dist/types/delegation.d.ts +24 -0
  34. package/package.json +1 -1
  35. package/dist/graph/graph-builder.d.ts +0 -39
  36. package/dist/graph/graph-query.d.ts +0 -42
  37. package/dist/graph/graph-store.d.ts +0 -27
  38. package/dist/graph/import-extractor.d.ts +0 -44
  39. package/dist/graph/index.d.ts +0 -16
  40. package/dist/graph/symbol-extractor.d.ts +0 -17
  41. package/dist/graph/types.d.ts +0 -84
  42. package/dist/sandbox/win32/restricted-token-executor.d.ts +0 -9
@@ -68,7 +68,7 @@ import {
68
68
  savePlan,
69
69
  transientBackoff,
70
70
  validateProjectRoot
71
- } from "./index-f6y341yk.js";
71
+ } from "./index-yrrqs4b0.js";
72
72
  import {
73
73
  buildOpenSpecProjectionSync,
74
74
  detectSpeckit,
@@ -111,7 +111,7 @@ import {
111
111
  retireSkill,
112
112
  selectCandidateEntries,
113
113
  syncBundledProjectSkillsIfMissingAsync
114
- } from "./index-vjvdvjd3.js";
114
+ } from "./index-x12hvfpf.js";
115
115
  import {
116
116
  appendUnactionable,
117
117
  quarantineEntry,
@@ -121,10 +121,10 @@ import {
121
121
  validateActionability,
122
122
  validateActionableFields,
123
123
  validateLesson
124
- } from "./index-qrnhvhmg.js";
124
+ } from "./index-n18yy0z1.js";
125
125
  import {
126
126
  readRecentEscalations
127
- } from "./index-7stsmndb.js";
127
+ } from "./index-6f6y2rbp.js";
128
128
  import {
129
129
  effectiveRetrievalOutcomes,
130
130
  readKnowledgeCounterRollups,
@@ -132,7 +132,7 @@ import {
132
132
  recordHiveKnowledgeEvent,
133
133
  recordKnowledgeEvent,
134
134
  resolveKnowledgeEventsPath
135
- } from "./index-kjbfry6m.js";
135
+ } from "./index-8f2270hc.js";
136
136
  import {
137
137
  appendKnowledge,
138
138
  appendRejectedLesson,
@@ -156,7 +156,7 @@ import {
156
156
  rewriteKnowledge,
157
157
  transactFile,
158
158
  transactKnowledge
159
- } from "./index-91qesget.js";
159
+ } from "./index-rk6qhyng.js";
160
160
  import {
161
161
  KNOWLEDGE_SCHEMA_VERSION,
162
162
  OUTCOME_BLOCK_THRESHOLD,
@@ -3064,50 +3064,60 @@ class SQLiteMemoryProvider {
3064
3064
  mkdirSync4(path5.dirname(dbPath), { recursive: true });
3065
3065
  const Db = loadDatabaseCtor2();
3066
3066
  this.db = new Db(dbPath);
3067
- this.db.run("PRAGMA journal_mode = WAL;");
3068
- this.db.run("PRAGMA synchronous = NORMAL;");
3069
- const busyTimeoutMs = Math.min(60000, Math.max(0, Math.trunc(this.config.sqlite.busyTimeoutMs)));
3070
- this.db.run(`PRAGMA busy_timeout = ${busyTimeoutMs};`);
3071
- this.db.run("PRAGMA foreign_keys = ON;");
3072
- this.runMigrations();
3073
- this.backfillScopeKeys();
3074
- this.backfillRecallRunIds();
3075
- this.ftsAvailable = this.initializeFtsIndex();
3076
- this.initializeVecExtension();
3077
- if (this.config.embeddings.enabled && !this.embeddingProvider) {
3078
- try {
3079
- this.embeddingProvider = new LocalEmbeddingProvider({
3080
- model: this.config.embeddings.model,
3081
- dimension: this.config.embeddings.dimension,
3082
- version: this.config.embeddings.version
3083
- });
3084
- } catch (err) {
3085
- this.embeddingProvider = null;
3086
- warn("Failed to construct embedding provider \u2014 dense retrieval disabled", {
3087
- reason: err instanceof Error ? err.message : String(err)
3088
- });
3067
+ try {
3068
+ this.db.run("PRAGMA journal_mode = WAL;");
3069
+ this.db.run("PRAGMA synchronous = NORMAL;");
3070
+ const busyTimeoutMs = Math.min(60000, Math.max(0, Math.trunc(this.config.sqlite.busyTimeoutMs)));
3071
+ this.db.run(`PRAGMA busy_timeout = ${busyTimeoutMs};`);
3072
+ this.db.run("PRAGMA foreign_keys = ON;");
3073
+ this.runMigrations();
3074
+ this.backfillScopeKeys();
3075
+ this.backfillRecallRunIds();
3076
+ this.ftsAvailable = this.initializeFtsIndex();
3077
+ this.initializeVecExtension();
3078
+ if (this.config.embeddings.enabled && !this.embeddingProvider) {
3079
+ try {
3080
+ this.embeddingProvider = new LocalEmbeddingProvider({
3081
+ model: this.config.embeddings.model,
3082
+ dimension: this.config.embeddings.dimension,
3083
+ version: this.config.embeddings.version
3084
+ });
3085
+ } catch (err) {
3086
+ this.embeddingProvider = null;
3087
+ warn("Failed to construct embedding provider \u2014 dense retrieval disabled", {
3088
+ reason: err instanceof Error ? err.message : String(err)
3089
+ });
3090
+ }
3091
+ try {
3092
+ this.embeddingCache = new EmbeddingCache(this.config.embeddings.cacheSize);
3093
+ } catch (err) {
3094
+ this.embeddingCache = null;
3095
+ warn("Failed to construct embedding cache \u2014 recall works without cache", {
3096
+ reason: err instanceof Error ? err.message : String(err)
3097
+ });
3098
+ }
3089
3099
  }
3090
- try {
3091
- this.embeddingCache = new EmbeddingCache(this.config.embeddings.cacheSize);
3092
- } catch (err) {
3093
- this.embeddingCache = null;
3094
- warn("Failed to construct embedding cache \u2014 recall works without cache", {
3095
- reason: err instanceof Error ? err.message : String(err)
3096
- });
3100
+ this.lastAutomaticJsonlMigration = null;
3101
+ await this.migrateLegacyJsonlIfNeeded();
3102
+ const memoryLoad = this.loadMemories();
3103
+ const proposalLoad = this.loadProposals();
3104
+ this.memories = new Map(memoryLoad.records.map((record) => [record.id, record]));
3105
+ this.proposals = new Map(proposalLoad.records.map((proposal) => [proposal.id, proposal]));
3106
+ this.initialized = true;
3107
+ if (memoryLoad.invalidCount > 0) {
3108
+ await this.event("invalid_load", "memory_items", `${memoryLoad.invalidCount} invalid SQLite memory row(s) skipped`);
3097
3109
  }
3098
- }
3099
- this.lastAutomaticJsonlMigration = null;
3100
- await this.migrateLegacyJsonlIfNeeded();
3101
- const memoryLoad = this.loadMemories();
3102
- const proposalLoad = this.loadProposals();
3103
- this.memories = new Map(memoryLoad.records.map((record) => [record.id, record]));
3104
- this.proposals = new Map(proposalLoad.records.map((proposal) => [proposal.id, proposal]));
3105
- this.initialized = true;
3106
- if (memoryLoad.invalidCount > 0) {
3107
- await this.event("invalid_load", "memory_items", `${memoryLoad.invalidCount} invalid SQLite memory row(s) skipped`);
3108
- }
3109
- if (proposalLoad.invalidCount > 0) {
3110
- await this.event("invalid_load", "memory_proposals", `${proposalLoad.invalidCount} invalid SQLite proposal row(s) skipped`);
3110
+ if (proposalLoad.invalidCount > 0) {
3111
+ await this.event("invalid_load", "memory_proposals", `${proposalLoad.invalidCount} invalid SQLite proposal row(s) skipped`);
3112
+ }
3113
+ } catch (err) {
3114
+ try {
3115
+ this.db?.close();
3116
+ } catch {}
3117
+ this.db = null;
3118
+ this.ftsAvailable = false;
3119
+ this.initialized = false;
3120
+ throw err;
3111
3121
  }
3112
3122
  }
3113
3123
  async upsert(record) {
@@ -3132,7 +3142,20 @@ class SQLiteMemoryProvider {
3132
3142
  }
3133
3143
  async withTransaction(fn) {
3134
3144
  const db = this.requireDb();
3135
- return db.transaction(() => fn({}))();
3145
+ if (db.inTransaction) {
3146
+ return await fn({});
3147
+ }
3148
+ db.run("BEGIN IMMEDIATE");
3149
+ try {
3150
+ const result = await fn({});
3151
+ db.run("COMMIT");
3152
+ return result;
3153
+ } catch (err) {
3154
+ try {
3155
+ db.run("ROLLBACK");
3156
+ } catch {}
3157
+ throw err;
3158
+ }
3136
3159
  }
3137
3160
  async delete(id, reason) {
3138
3161
  await this.initialize();
@@ -9192,7 +9215,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9192
9215
  }
9193
9216
  if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
9194
9217
  try {
9195
- const skillModule = await import("./skill-generator-w6qd9mde.js");
9218
+ const skillModule = await import("./skill-generator-tav44xpm.js");
9196
9219
  for (const cand of skillCandidates) {
9197
9220
  if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
9198
9221
  continue;
@@ -9473,7 +9496,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
9473
9496
  } catch {}
9474
9497
  }
9475
9498
  if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
9476
- const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-qn7ew687.js");
9499
+ const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-5gwp1ar8.js");
9477
9500
  for (const { id } of contradictedEntries) {
9478
9501
  try {
9479
9502
  await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
@@ -10434,11 +10457,11 @@ var _internals12 = {
10434
10457
  return KnowledgeConfigSchema2.parse({});
10435
10458
  },
10436
10459
  applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig) => {
10437
- const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-qshyr2tc.js");
10460
+ const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-qj970412.js");
10438
10461
  return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig);
10439
10462
  },
10440
10463
  checkHivePromotions: async (entries, knowledgeConfig) => {
10441
- const { checkHivePromotions } = await import("./hive-promoter-5ngbmnrw.js");
10464
+ const { checkHivePromotions } = await import("./hive-promoter-ysk5edzw.js");
10442
10465
  return checkHivePromotions(entries, knowledgeConfig);
10443
10466
  },
10444
10467
  applyProposalTriage: async (directory, triage) => {
@@ -10447,7 +10470,7 @@ var _internals12 = {
10447
10470
  activateProposal,
10448
10471
  listSkills: listSkills2,
10449
10472
  sanitizeSlug
10450
- } = await import("./skill-generator-w6qd9mde.js");
10473
+ } = await import("./skill-generator-tav44xpm.js");
10451
10474
  const result = {
10452
10475
  approved: [],
10453
10476
  rejected: [],
@@ -17599,8 +17622,8 @@ var _internals24 = {
17599
17622
  loadCuratorDeps: async () => {
17600
17623
  const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
17601
17624
  import("./schema-aymfdrsb.js"),
17602
- import("./curator-qshyr2tc.js"),
17603
- import("./curator-llm-factory-2xgznxyw.js")
17625
+ import("./curator-qj970412.js"),
17626
+ import("./curator-llm-factory-ez48eq02.js")
17604
17627
  ]);
17605
17628
  return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
17606
17629
  }
@@ -18096,7 +18119,7 @@ import { fileURLToPath } from "url";
18096
18119
  // package.json
18097
18120
  var package_default = {
18098
18121
  name: "opencode-swarm",
18099
- version: "7.114.7",
18122
+ version: "7.114.8",
18100
18123
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
18101
18124
  main: "dist/index.js",
18102
18125
  types: "dist/index.d.ts",
@@ -20378,7 +20401,7 @@ async function handleEvidenceCommand(directory, args) {
20378
20401
  return formatTaskEvidenceMarkdown(evidenceData);
20379
20402
  }
20380
20403
  async function handleEvidenceSummaryCommand(directory) {
20381
- const { buildEvidenceSummary } = await import("./evidence-summary-service-8g594znj.js");
20404
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-fehwj116.js");
20382
20405
  const artifact = await buildEvidenceSummary(directory);
20383
20406
  if (!artifact) {
20384
20407
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -32992,7 +33015,7 @@ function buildDetailedHelp(commandName, entry) {
32992
33015
  async function handleHelpCommand(ctx) {
32993
33016
  const targetCommand = ctx.args.join(" ");
32994
33017
  if (!targetCommand) {
32995
- const { buildHelpText } = await import("./index-z01r5483.js");
33018
+ const { buildHelpText } = await import("./index-z718bgxe.js");
32996
33019
  return buildHelpText();
32997
33020
  }
32998
33021
  const tokens = targetCommand.split(/\s+/);
@@ -33001,7 +33024,7 @@ async function handleHelpCommand(ctx) {
33001
33024
  return _internals49.buildDetailedHelp(resolved.key, resolved.entry);
33002
33025
  }
33003
33026
  const similar = _internals49.findSimilarCommands(targetCommand);
33004
- const { buildHelpText: fullHelp } = await import("./index-z01r5483.js");
33027
+ const { buildHelpText: fullHelp } = await import("./index-z718bgxe.js");
33005
33028
  if (similar.length > 0) {
33006
33029
  return `Command '/swarm ${targetCommand}' not found.
33007
33030
 
@@ -33134,7 +33157,7 @@ var COMMAND_REGISTRY = {
33134
33157
  },
33135
33158
  "guardrail explain": {
33136
33159
  handler: async (ctx) => {
33137
- const { handleGuardrailExplain } = await import("./guardrail-explain-4t8fnysh.js");
33160
+ const { handleGuardrailExplain } = await import("./guardrail-explain-x2vaxp6s.js");
33138
33161
  return handleGuardrailExplain(ctx.directory, ctx.args);
33139
33162
  },
33140
33163
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -33144,7 +33167,7 @@ var COMMAND_REGISTRY = {
33144
33167
  },
33145
33168
  "guardrail-explain": {
33146
33169
  handler: async (ctx) => {
33147
- const { handleGuardrailExplain } = await import("./guardrail-explain-4t8fnysh.js");
33170
+ const { handleGuardrailExplain } = await import("./guardrail-explain-x2vaxp6s.js");
33148
33171
  return handleGuardrailExplain(ctx.directory, ctx.args);
33149
33172
  },
33150
33173
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -34544,6 +34567,7 @@ FILE: src/auth/login.ts
34544
34567
  INPUT: Validate email format, password >= 8 chars
34545
34568
  OUTPUT: Modified file
34546
34569
  CONSTRAINT: Do not modify other functions
34570
+ ACCEPTANCE: Populate from the plan task's acceptance criteria / FR / SC when the task has them (one item per line, or FR-/SC-style references), so the coder can gate its implementation on each. Omit the field entirely when the task has no structured acceptance criteria.
34547
34571
  SKILLS: file:.claude/skills/engineering-conventions/SKILL.md
34548
34572
 
34549
34573
  {{AGENT_PREFIX}}reviewer
@@ -38293,7 +38317,6 @@ function resolveWriteTargets(command, writes, cwd) {
38293
38317
  };
38294
38318
  });
38295
38319
  }
38296
-
38297
38320
  // src/hooks/guardrails/index.ts
38298
38321
  var SPEC_DRIFT_BLOCKED_TOOLS = new Set([
38299
38322
  "save_plan",
@@ -6,7 +6,7 @@ import {
6
6
  resolveSwarmKnowledgePath,
7
7
  resolveSwarmRejectedPath,
8
8
  transactKnowledge
9
- } from "./index-91qesget.js";
9
+ } from "./index-rk6qhyng.js";
10
10
  import {
11
11
  resolveKnowledgeStoreDir
12
12
  } from "./index-g40r5d08.js";
@@ -75,6 +75,25 @@ var INJECTION_PATTERNS = [
75
75
  /\bconstructor\[/,
76
76
  /\.prototype\[/
77
77
  ];
78
+ function scanContentSafety(text) {
79
+ if (typeof text !== "string" || text.length === 0)
80
+ return null;
81
+ const normalized = text.normalize("NFKC").replace(INVISIBLE_FORMAT_CHARS, " ").replace(/\s+/g, " ").toLowerCase();
82
+ for (const pattern of DANGEROUS_COMMAND_ERROR_PATTERNS) {
83
+ if (pattern.test(normalized))
84
+ return "dangerous command pattern detected";
85
+ }
86
+ for (const pattern of SECURITY_DEGRADING_PATTERNS) {
87
+ if (pattern.test(normalized)) {
88
+ return "security-degrading instruction detected";
89
+ }
90
+ }
91
+ for (const pattern of INJECTION_PATTERNS) {
92
+ if (pattern.test(text))
93
+ return "injection pattern detected";
94
+ }
95
+ return null;
96
+ }
78
97
  var VALID_CATEGORIES = new Set([
79
98
  "process",
80
99
  "architecture",
@@ -412,6 +431,11 @@ function validateActionableFields(fields) {
412
431
  errors.push(`${name} contains invalid string`);
413
432
  return;
414
433
  }
434
+ const unsafe = scanContentSafety(item);
435
+ if (unsafe) {
436
+ errors.push(`${name} contains unsafe content: ${unsafe}`);
437
+ return;
438
+ }
415
439
  }
416
440
  }
417
441
  function checkNameList(name, list) {
@@ -758,4 +782,4 @@ var _internals = {
758
782
  hasSignificantOverlap
759
783
  };
760
784
 
761
- export { DANGEROUS_COMMAND_ERROR_PATTERNS, DANGEROUS_COMMAND_WARNING_PATTERNS, DANGEROUS_COMMAND_PATTERNS, SECURITY_DEGRADING_PATTERNS, INVISIBLE_FORMAT_CHARS, INJECTION_PATTERNS, validateLesson, ACTIONABLE_STRING_MAX, ACTIONABLE_LIST_MAX, RETIRED_SKILL_HISTORY_MAX, ALLOWED_SKILL_PATH_PREFIXES, validateSkillPath, validateSkillCandidatePath, validateActionableFields, validateActionability, resolveUnactionablePath, appendUnactionable, auditEntryHealth, quarantineEntry, restoreEntry, unarchiveEntry, _internals };
785
+ export { DANGEROUS_COMMAND_ERROR_PATTERNS, DANGEROUS_COMMAND_WARNING_PATTERNS, DANGEROUS_COMMAND_PATTERNS, SECURITY_DEGRADING_PATTERNS, INVISIBLE_FORMAT_CHARS, INJECTION_PATTERNS, scanContentSafety, validateLesson, ACTIONABLE_STRING_MAX, ACTIONABLE_LIST_MAX, RETIRED_SKILL_HISTORY_MAX, ALLOWED_SKILL_PATH_PREFIXES, validateSkillPath, validateSkillCandidatePath, validateActionableFields, validateActionability, resolveUnactionablePath, appendUnactionable, auditEntryHealth, quarantineEntry, restoreEntry, unarchiveEntry, _internals };
@@ -595,7 +595,7 @@ async function applyConfidenceFloorAction(directory, touched, options) {
595
595
  const recovered = touched.filter((e) => e.confidence > CONFIDENCE_FLOOR + FLOOR_EPSILON && e.confidence_floor_demoted);
596
596
  if (atFloor.length === 0 && recovered.length === 0)
597
597
  return;
598
- const { readKnowledgeCounterRollups, effectiveRetrievalOutcomes } = await import("./knowledge-events-k2xsz5bh.js");
598
+ const { readKnowledgeCounterRollups, effectiveRetrievalOutcomes } = await import("./knowledge-events-m304swh6.js");
599
599
  const rollups = await readKnowledgeCounterRollups(directory);
600
600
  const flagIds = new Set;
601
601
  for (const e of atFloor) {
@@ -662,7 +662,7 @@ async function applyConfidenceFloorAction(directory, touched, options) {
662
662
  });
663
663
  }
664
664
  if (action === "quarantine" && toQuarantine.length > 0) {
665
- const { quarantineEntry } = await import("./knowledge-validator-2knz0d2t.js");
665
+ const { quarantineEntry } = await import("./knowledge-validator-t9sym59g.js");
666
666
  for (const { id } of toQuarantine) {
667
667
  await quarantineEntry(directory, id, "confidence_floor_negative_outcome", "auto").catch((err) => {
668
668
  log("[knowledge-store] confidence-floor quarantine failed (best-effort):", err instanceof Error ? err.message : String(err));
@@ -12,7 +12,7 @@ import {
12
12
  detectPosixWrites,
13
13
  detectWindowsWrites,
14
14
  resolveWriteTargets
15
- } from "./index-6ctejje2.js";
15
+ } from "./index-cs5765s2.js";
16
16
  import {
17
17
  checkFileAuthority,
18
18
  classifyFile,
@@ -2,18 +2,18 @@
2
2
  import {
3
3
  ALLOWED_SKILL_PATH_PREFIXES,
4
4
  validateSkillPath
5
- } from "./index-qrnhvhmg.js";
5
+ } from "./index-n18yy0z1.js";
6
6
  import {
7
7
  effectiveRetrievalOutcomes,
8
8
  readKnowledgeCounterRollups
9
- } from "./index-kjbfry6m.js";
9
+ } from "./index-8f2270hc.js";
10
10
  import {
11
11
  computeOutcomeSignal,
12
12
  readKnowledge,
13
13
  resolveHiveKnowledgePath,
14
14
  resolveSwarmKnowledgePath,
15
15
  transactKnowledge
16
- } from "./index-91qesget.js";
16
+ } from "./index-rk6qhyng.js";
17
17
  import {
18
18
  isActiveStatus
19
19
  } from "./index-bfwt3abw.js";
@@ -845,11 +845,16 @@ var init_executor = __esm(() => {
845
845
 
846
846
  // src/plan/manager.ts
847
847
  import {
848
+ closeSync as closeSync2,
848
849
  copyFileSync,
849
850
  existsSync as existsSync5,
850
- readdirSync,
851
+ fsyncSync as fsyncSync2,
852
+ mkdirSync as mkdirSync5,
853
+ openSync as openSync2,
854
+ readdirSync as readdirSync2,
851
855
  renameSync as renameSync4,
852
- unlinkSync as unlinkSync3
856
+ unlinkSync as unlinkSync3,
857
+ writeFileSync as writeFileSync6
853
858
  } from "fs";
854
859
  import * as fsPromises from "fs/promises";
855
860
  import * as path9 from "path";
@@ -2144,6 +2149,16 @@ function getLedgerPath(directory) {
2144
2149
  function getPlanJsonPath(directory) {
2145
2150
  return path8.join(directory, ".swarm", PLAN_JSON_FILENAME);
2146
2151
  }
2152
+ function writeFileFsyncedThenRename(tempPath, targetPath, data) {
2153
+ const fd = fs5.openSync(tempPath, "w");
2154
+ try {
2155
+ fs5.writeFileSync(fd, data, "utf8");
2156
+ fs5.fsyncSync(fd);
2157
+ } finally {
2158
+ fs5.closeSync(fd);
2159
+ }
2160
+ fs5.renameSync(tempPath, targetPath);
2161
+ }
2147
2162
  function computePlanHash(plan) {
2148
2163
  const normalized = {
2149
2164
  schema_version: plan.schema_version,
@@ -2277,8 +2292,7 @@ async function initLedger(directory, planId, initialPlanHash, initialPlan) {
2277
2292
  const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
2278
2293
  const line = `${JSON.stringify(event)}
2279
2294
  `;
2280
- fs5.writeFileSync(tempPath, line, "utf8");
2281
- fs5.renameSync(tempPath, ledgerPath);
2295
+ writeFileFsyncedThenRename(tempPath, ledgerPath, line);
2282
2296
  }
2283
2297
  async function appendLedgerEvent(directory, eventInput, options) {
2284
2298
  return withEvidenceLock(directory, LEDGER_LOCK_PATH, "plan-ledger", "append-ledger-event", async () => {
@@ -2305,13 +2319,11 @@ async function appendLedgerEvent(directory, eventInput, options) {
2305
2319
  const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
2306
2320
  const line = `${JSON.stringify(event)}
2307
2321
  `;
2308
- if (fs5.existsSync(ledgerPath)) {
2309
- const existingContent = fs5.readFileSync(ledgerPath, "utf8");
2310
- fs5.writeFileSync(tempPath, existingContent + line, "utf8");
2311
- } else {
2322
+ if (!fs5.existsSync(ledgerPath)) {
2312
2323
  throw new Error("Ledger not initialized. Call initLedger() first.");
2313
2324
  }
2314
- fs5.renameSync(tempPath, ledgerPath);
2325
+ const existingContent = fs5.readFileSync(ledgerPath, "utf8");
2326
+ writeFileFsyncedThenRename(tempPath, ledgerPath, existingContent + line);
2315
2327
  return event;
2316
2328
  });
2317
2329
  }
@@ -2358,14 +2370,22 @@ async function takeSnapshotEvent(directory, plan, options) {
2358
2370
  payload: snapshotPayload
2359
2371
  }, { planHashAfter: options?.planHashAfter });
2360
2372
  }
2361
- async function replayFromLedger(directory, _options) {
2373
+ async function replayFromLedger(directory, options) {
2374
+ const { plan } = await replayFromLedgerWithStatus(directory, options);
2375
+ return plan;
2376
+ }
2377
+ async function replayFromLedgerWithStatus(directory, _options) {
2362
2378
  const { events, truncated, badSuffix } = await readLedgerEventsWithIntegrity(directory);
2363
2379
  if (events.length === 0) {
2364
- return null;
2380
+ return { plan: null, truncated, badSuffix };
2365
2381
  }
2366
2382
  if (truncated && badSuffix !== null) {
2367
2383
  await quarantineLedgerSuffix(directory, badSuffix);
2368
2384
  }
2385
+ const plan = reconstructPlanFromEvents(directory, events);
2386
+ return { plan, truncated, badSuffix };
2387
+ }
2388
+ function reconstructPlanFromEvents(directory, events) {
2369
2389
  const targetPlanId = events[0].plan_id;
2370
2390
  const relevantEvents = events.filter((e) => e.plan_id === targetPlanId);
2371
2391
  {
@@ -2531,11 +2551,35 @@ async function readLedgerEventsWithIntegrity(directory) {
2531
2551
  }
2532
2552
  }
2533
2553
  async function quarantineLedgerSuffix(directory, badSuffix) {
2554
+ let salvagedCount = 0;
2555
+ for (const line of badSuffix.split(`
2556
+ `)) {
2557
+ if (line.trim() === "")
2558
+ continue;
2559
+ try {
2560
+ JSON.parse(line);
2561
+ salvagedCount++;
2562
+ } catch {}
2563
+ }
2534
2564
  try {
2535
- const quarantinePath = path8.join(directory, ".swarm", "plan-ledger.quarantine");
2565
+ const hash = crypto2.createHash("sha256").update(badSuffix, "utf8").digest("hex").slice(0, 12);
2566
+ const swarmDir = path8.join(directory, ".swarm");
2567
+ try {
2568
+ const existing = fs5.readdirSync(swarmDir).filter((name) => name.startsWith("plan-ledger.quarantine.") && name.endsWith(`.${hash}`));
2569
+ for (const name of existing) {
2570
+ const existingPath = path8.join(swarmDir, name);
2571
+ if (fs5.readFileSync(existingPath, "utf8") === badSuffix) {
2572
+ return { path: existingPath, salvagedCount };
2573
+ }
2574
+ }
2575
+ } catch {}
2576
+ const quarantinePath = path8.join(swarmDir, `plan-ledger.quarantine.${Date.now()}.${hash}`);
2536
2577
  fs5.writeFileSync(quarantinePath, badSuffix, "utf8");
2537
- console.warn(`[ledger] Corrupted suffix quarantined to ${path8.relative(directory, quarantinePath)}`);
2538
- } catch {}
2578
+ console.warn(`[ledger] Corrupted suffix quarantined to ${path8.relative(directory, quarantinePath)} (salvageable events: ${salvagedCount})`);
2579
+ return { path: quarantinePath, salvagedCount };
2580
+ } catch {
2581
+ return { path: null, salvagedCount };
2582
+ }
2539
2583
  }
2540
2584
  async function loadLastApprovedPlan(directory, expectedPlanId) {
2541
2585
  const events = await readLedgerEvents(directory);
@@ -2868,7 +2912,15 @@ async function loadPlan(directory, cache) {
2868
2912
  } else {
2869
2913
  warn("[loadPlan] plan.json is stale (hash mismatch with ledger) \u2014 rebuilding from ledger. If this recurs, run /swarm reset-session to clear stale session state.");
2870
2914
  try {
2871
- const rebuilt = await replayFromLedger(directory);
2915
+ const { plan: rebuilt, truncated } = await replayFromLedgerWithStatus(directory);
2916
+ if (truncated) {
2917
+ const runtimeStale = validated;
2918
+ runtimeStale._ledgerReplayStale = true;
2919
+ runtimeStale._ledgerReplayStaleReason = "Ledger truncated: a malformed line stopped replay before the tail, so the ledger could only be reconstructed up to the corruption. plan.json was PRESERVED (not rolled back to the prefix-only projection) and the corrupted suffix was quarantined to .swarm/plan-ledger.quarantine.*. Verify state, then run /swarm reset-session if this persists.";
2920
+ ledgerStaleWorkspaces.add(resolvedWorkspace);
2921
+ criticalWarn("[loadPlan] Ledger truncated (poison line detected) \u2014 preserving plan.json instead of rolling back to the prefix-only ledger projection. Durable post-poison events remain in plan.json. Corrupted suffix quarantined to .swarm/plan-ledger.quarantine.*. Run /swarm reset-session after verifying state if this persists.");
2922
+ return runtimeStale;
2923
+ }
2872
2924
  if (rebuilt) {
2873
2925
  await rebuildPlan(directory, rebuilt, {
2874
2926
  reason: "ledger_hash_mismatch_recovery"
@@ -3190,7 +3242,7 @@ async function savePlan(directory, plan, options) {
3190
3242
  }
3191
3243
  const MAX_ARCHIVED_SIBLINGS = 5;
3192
3244
  try {
3193
- const allFiles = readdirSync(swarmDir2);
3245
+ const allFiles = readdirSync2(swarmDir2);
3194
3246
  const archivedSiblings = allFiles.filter((f) => f.startsWith("plan-ledger.archived-") && f.endsWith(".jsonl")).sort();
3195
3247
  if (archivedSiblings.length > MAX_ARCHIVED_SIBLINGS) {
3196
3248
  const toRemove = archivedSiblings.slice(0, archivedSiblings.length - MAX_ARCHIVED_SIBLINGS);
@@ -3407,8 +3459,17 @@ async function rebuildPlan(directory, plan, options) {
3407
3459
  const swarmDir = path9.join(directory, ".swarm");
3408
3460
  const planPath = path9.join(swarmDir, "plan.json");
3409
3461
  const mdPath = path9.join(swarmDir, "plan.md");
3462
+ mkdirSync5(swarmDir, { recursive: true });
3410
3463
  const tempPlanPath = path9.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3411
- await bunWrite(tempPlanPath, JSON.stringify(targetPlan, null, 2));
3464
+ {
3465
+ const fd = openSync2(tempPlanPath, "w");
3466
+ try {
3467
+ writeFileSync6(fd, JSON.stringify(targetPlan, null, 2), "utf8");
3468
+ fsyncSync2(fd);
3469
+ } finally {
3470
+ closeSync2(fd);
3471
+ }
3472
+ }
3412
3473
  renameSync4(tempPlanPath, planPath);
3413
3474
  try {
3414
3475
  const markerPath = path9.join(swarmDir, ".plan-write-marker");
@@ -3805,8 +3866,8 @@ function migrateLegacyPlan(planContent, swarmId) {
3805
3866
 
3806
3867
  // src/evidence/manager.ts
3807
3868
  import {
3808
- mkdirSync as mkdirSync5,
3809
- readdirSync as readdirSync2,
3869
+ mkdirSync as mkdirSync6,
3870
+ readdirSync as readdirSync3,
3810
3871
  realpathSync,
3811
3872
  rmSync,
3812
3873
  statSync as statSync2
@@ -4244,7 +4305,7 @@ async function saveEvidence(directory, taskId, evidence) {
4244
4305
  if (bundleJson.length > EVIDENCE_MAX_JSON_BYTES) {
4245
4306
  throw new Error(`Evidence bundle size (${bundleJson.length} bytes) exceeds maximum (${EVIDENCE_MAX_JSON_BYTES} bytes)`);
4246
4307
  }
4247
- mkdirSync5(evidenceDir, { recursive: true });
4308
+ mkdirSync6(evidenceDir, { recursive: true });
4248
4309
  const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4249
4310
  try {
4250
4311
  await bunWrite(tempPath, bundleJson);
@@ -4351,7 +4412,7 @@ async function listEvidenceTaskIds(directory) {
4351
4412
  }
4352
4413
  let entries;
4353
4414
  try {
4354
- entries = readdirSync2(evidenceBasePath);
4415
+ entries = readdirSync3(evidenceBasePath);
4355
4416
  } catch {
4356
4417
  return [];
4357
4418
  }
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-gmjrwz34.js";
4
+ } from "./index-ry8nwsq6.js";
5
5
  import {
6
6
  handleGuardrailLog
7
7
  } from "./index-7jmrbp68.js";
@@ -81,7 +81,7 @@ import {
81
81
  handleWriteRetroCommand,
82
82
  normalizeSwarmCommandInput,
83
83
  resolveCommand
84
- } from "./index-6ctejje2.js";
84
+ } from "./index-cs5765s2.js";
85
85
  import"./index-yfedche9.js";
86
86
  import"./index-c8s9a3zh.js";
87
87
  import"./index-9bsmzfk3.js";
@@ -90,15 +90,15 @@ import {
90
90
  ORCHESTRATOR_NAME,
91
91
  stripKnownSwarmPrefix
92
92
  } from "./index-a45jq4b7.js";
93
- import"./index-f6y341yk.js";
93
+ import"./index-yrrqs4b0.js";
94
94
  import"./index-scww5b77.js";
95
95
  import"./index-ga4ta3cr.js";
96
96
  import"./index-80jkseqw.js";
97
- import"./index-vjvdvjd3.js";
98
- import"./index-qrnhvhmg.js";
99
- import"./index-7stsmndb.js";
100
- import"./index-kjbfry6m.js";
101
- import"./index-91qesget.js";
97
+ import"./index-x12hvfpf.js";
98
+ import"./index-n18yy0z1.js";
99
+ import"./index-6f6y2rbp.js";
100
+ import"./index-8f2270hc.js";
101
+ import"./index-rk6qhyng.js";
102
102
  import"./index-bfwt3abw.js";
103
103
  import"./index-g40r5d08.js";
104
104
  import"./index-vxv732ex.js";