opencode-swarm 7.17.0 → 7.17.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/cli/index.js CHANGED
@@ -34,7 +34,7 @@ var package_default;
34
34
  var init_package = __esm(() => {
35
35
  package_default = {
36
36
  name: "opencode-swarm",
37
- version: "7.17.0",
37
+ version: "7.17.1",
38
38
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
39
39
  main: "dist/index.js",
40
40
  types: "dist/index.d.ts",
@@ -15935,7 +15935,6 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args) {
15935
15935
  return "Spec staleness file was corrupted. It has been removed.";
15936
15936
  }
15937
15937
  const { planTitle, phase } = stalenessData;
15938
- await fsPromises3.unlink(specStalenessPath);
15939
15938
  let currentHash = null;
15940
15939
  let planUpdateSkipped = false;
15941
15940
  try {
@@ -15949,6 +15948,9 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args) {
15949
15948
  console.error("[acknowledge-spec-drift] Failed to update plan specHash:", planError instanceof Error ? planError.message : String(planError));
15950
15949
  planUpdateSkipped = true;
15951
15950
  }
15951
+ if (!planUpdateSkipped) {
15952
+ await fsPromises3.unlink(specStalenessPath);
15953
+ }
15952
15954
  const eventsPath = validateSwarmPath(directory, "events.jsonl");
15953
15955
  const acknowledgmentEvent = {
15954
15956
  type: "spec_drift_acknowledged",
@@ -35412,6 +35414,8 @@ async function quarantineEntry(directory, entryId, reason, reportedBy) {
35412
35414
  const remaining = entries.filter((e) => e.id !== entryId);
35413
35415
  const quarantined = {
35414
35416
  ...entry,
35417
+ status: "quarantined",
35418
+ original_status: entry.status,
35415
35419
  quarantine_reason: sanitizedReason,
35416
35420
  quarantined_at: new Date().toISOString(),
35417
35421
  reported_by: reportedBy
@@ -35470,7 +35474,24 @@ async function restoreEntry(directory, entryId) {
35470
35474
  return;
35471
35475
  }
35472
35476
  const remaining = quarantinedEntries.filter((e) => e.id !== entryId);
35473
- const { quarantine_reason, quarantined_at, reported_by, ...original } = entryToRestore;
35477
+ const {
35478
+ quarantine_reason,
35479
+ quarantined_at,
35480
+ reported_by,
35481
+ original_status,
35482
+ status: _quarantineStatus,
35483
+ ...rest
35484
+ } = entryToRestore;
35485
+ const original = { ...rest, status: original_status ?? "candidate" };
35486
+ const validation = validateLesson(original.lesson, [], {
35487
+ category: original.category,
35488
+ scope: original.scope,
35489
+ confidence: original.confidence
35490
+ });
35491
+ if (!validation.valid) {
35492
+ warn(`[knowledge-validator] restoreEntry: entry ${entryId} failed re-validation: ${validation.reason}`);
35493
+ return;
35494
+ }
35474
35495
  const jsonlContent = remaining.length > 0 ? `${remaining.map((e) => JSON.stringify(e)).join(`
35475
35496
  `)}
35476
35497
  ` : "";
@@ -46325,7 +46346,7 @@ async function selectEntryPoints2(dir) {
46325
46346
  `)) {
46326
46347
  const m = line.match(/=\s*['"]([^'":]+)/);
46327
46348
  if (m) {
46328
- const modPath = m[1].replace(/\./g, "/") + ".py";
46349
+ const modPath = `${m[1].replace(/\./g, "/")}.py`;
46329
46350
  points.add(modPath);
46330
46351
  }
46331
46352
  }
@@ -47508,7 +47529,7 @@ function clearDispatchCache() {
47508
47529
  manifestRootCache.clear();
47509
47530
  insertCounter = 0;
47510
47531
  }
47511
- var _internals22, cache, insertCounter = 0, MANIFEST_FILES, MANIFEST_SET, manifestRootCache;
47532
+ var _internals22, cache, insertCounter = 0, MANIFEST_FILES, _MANIFEST_SET, manifestRootCache;
47512
47533
  var init_dispatch = __esm(() => {
47513
47534
  init_backends();
47514
47535
  init_detector();
@@ -47540,7 +47561,7 @@ var init_dispatch = __esm(() => {
47540
47561
  "Gemfile",
47541
47562
  "composer.json"
47542
47563
  ];
47543
- MANIFEST_SET = new Set(MANIFEST_FILES);
47564
+ _MANIFEST_SET = new Set(MANIFEST_FILES);
47544
47565
  manifestRootCache = new Map;
47545
47566
  });
47546
47567
 
@@ -9,7 +9,8 @@ import type { KnowledgeConfig, MessageWithParts } from './knowledge-types.js';
9
9
  /**
10
10
  * Creates a knowledge injection hook that injects relevant knowledge into the
11
11
  * architect's message context at phase start. Supports caching for re-injection
12
- * after compaction.
12
+ * after compaction. Cache is per-instance (bound to the returned hook closure),
13
+ * ensuring no cross-test pollution in Bun's shared test-runner process.
13
14
  *
14
15
  * @param directory - The project directory containing .swarm/
15
16
  * @param config - Knowledge system configuration
@@ -81,7 +81,7 @@ export interface KnowledgeEntryBase extends ActionableDirectiveFields {
81
81
  tags: string[];
82
82
  scope: string;
83
83
  confidence: number;
84
- status: 'candidate' | 'established' | 'promoted' | 'archived';
84
+ status: 'candidate' | 'established' | 'promoted' | 'archived' | 'quarantined';
85
85
  confirmed_by: PhaseConfirmationRecord[] | ProjectConfirmationRecord[];
86
86
  retrieval_outcomes: RetrievalOutcome;
87
87
  schema_version: number;
@@ -31,6 +31,7 @@ export declare function validateSkillPath(p: unknown): boolean;
31
31
  export declare function validateActionableFields(fields: ActionableDirectiveFields | undefined): ActionableValidationResult;
32
32
  export type { ActionableDirectiveFields, DirectivePriority };
33
33
  export interface QuarantinedEntry extends KnowledgeEntryBase {
34
+ original_status: string;
34
35
  quarantine_reason: string;
35
36
  quarantined_at: string;
36
37
  reported_by: 'architect' | 'user' | 'auto';
package/dist/index.js CHANGED
@@ -33,7 +33,7 @@ var package_default;
33
33
  var init_package = __esm(() => {
34
34
  package_default = {
35
35
  name: "opencode-swarm",
36
- version: "7.17.0",
36
+ version: "7.17.1",
37
37
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
38
38
  main: "dist/index.js",
39
39
  types: "dist/index.d.ts",
@@ -18445,7 +18445,6 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args) {
18445
18445
  return "Spec staleness file was corrupted. It has been removed.";
18446
18446
  }
18447
18447
  const { planTitle, phase } = stalenessData;
18448
- await fsPromises4.unlink(specStalenessPath);
18449
18448
  let currentHash = null;
18450
18449
  let planUpdateSkipped = false;
18451
18450
  try {
@@ -18459,6 +18458,9 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args) {
18459
18458
  console.error("[acknowledge-spec-drift] Failed to update plan specHash:", planError instanceof Error ? planError.message : String(planError));
18460
18459
  planUpdateSkipped = true;
18461
18460
  }
18461
+ if (!planUpdateSkipped) {
18462
+ await fsPromises4.unlink(specStalenessPath);
18463
+ }
18462
18464
  const eventsPath = validateSwarmPath(directory, "events.jsonl");
18463
18465
  const acknowledgmentEvent = {
18464
18466
  type: "spec_drift_acknowledged",
@@ -42470,6 +42472,8 @@ async function quarantineEntry(directory, entryId, reason, reportedBy) {
42470
42472
  const remaining = entries.filter((e) => e.id !== entryId);
42471
42473
  const quarantined = {
42472
42474
  ...entry,
42475
+ status: "quarantined",
42476
+ original_status: entry.status,
42473
42477
  quarantine_reason: sanitizedReason,
42474
42478
  quarantined_at: new Date().toISOString(),
42475
42479
  reported_by: reportedBy
@@ -42528,7 +42532,24 @@ async function restoreEntry(directory, entryId) {
42528
42532
  return;
42529
42533
  }
42530
42534
  const remaining = quarantinedEntries.filter((e) => e.id !== entryId);
42531
- const { quarantine_reason, quarantined_at, reported_by, ...original } = entryToRestore;
42535
+ const {
42536
+ quarantine_reason,
42537
+ quarantined_at,
42538
+ reported_by,
42539
+ original_status,
42540
+ status: _quarantineStatus,
42541
+ ...rest
42542
+ } = entryToRestore;
42543
+ const original = { ...rest, status: original_status ?? "candidate" };
42544
+ const validation = validateLesson(original.lesson, [], {
42545
+ category: original.category,
42546
+ scope: original.scope,
42547
+ confidence: original.confidence
42548
+ });
42549
+ if (!validation.valid) {
42550
+ warn(`[knowledge-validator] restoreEntry: entry ${entryId} failed re-validation: ${validation.reason}`);
42551
+ return;
42552
+ }
42532
42553
  const jsonlContent = remaining.length > 0 ? `${remaining.map((e) => JSON.stringify(e)).join(`
42533
42554
  `)}
42534
42555
  ` : "";
@@ -44359,7 +44380,9 @@ async function readMergedKnowledge(directory, config3, context) {
44359
44380
  const maxInject = config3.max_inject_count ?? 5;
44360
44381
  const topN = ranked.slice(0, maxInject);
44361
44382
  if (topN.length > 0 && context?.currentPhase) {
44362
- recordLessonsShown(directory, topN.map((e) => e.id), context.currentPhase).catch(() => {});
44383
+ recordLessonsShown(directory, topN.map((e) => e.id), context.currentPhase).catch((err2) => {
44384
+ warn("[knowledge-reader] recordLessonsShown unexpected rejection:", err2);
44385
+ });
44363
44386
  }
44364
44387
  return topN;
44365
44388
  }
@@ -54848,7 +54871,7 @@ async function selectEntryPoints2(dir) {
54848
54871
  `)) {
54849
54872
  const m = line.match(/=\s*['"]([^'":]+)/);
54850
54873
  if (m) {
54851
- const modPath = m[1].replace(/\./g, "/") + ".py";
54874
+ const modPath = `${m[1].replace(/\./g, "/")}.py`;
54852
54875
  points.add(modPath);
54853
54876
  }
54854
54877
  }
@@ -56031,7 +56054,7 @@ function clearDispatchCache() {
56031
56054
  manifestRootCache.clear();
56032
56055
  insertCounter = 0;
56033
56056
  }
56034
- var _internals28, cache, insertCounter = 0, MANIFEST_FILES, MANIFEST_SET, manifestRootCache;
56057
+ var _internals28, cache, insertCounter = 0, MANIFEST_FILES, _MANIFEST_SET, manifestRootCache;
56035
56058
  var init_dispatch = __esm(() => {
56036
56059
  init_backends();
56037
56060
  init_detector();
@@ -56063,7 +56086,7 @@ var init_dispatch = __esm(() => {
56063
56086
  "Gemfile",
56064
56087
  "composer.json"
56065
56088
  ];
56066
- MANIFEST_SET = new Set(MANIFEST_FILES);
56089
+ _MANIFEST_SET = new Set(MANIFEST_FILES);
56067
56090
  manifestRootCache = new Map;
56068
56091
  });
56069
56092
 
@@ -71420,7 +71443,7 @@ function decisionFromVerdict(verdict, escalationNeeded) {
71420
71443
  if (verdict === "APPROVED" || verdict === "ANSWER")
71421
71444
  return "allow";
71422
71445
  if (verdict === "BLOCKED")
71423
- return "pause";
71446
+ return "deny";
71424
71447
  if (verdict === "PENDING")
71425
71448
  return "pending";
71426
71449
  return "deny";
@@ -80646,7 +80669,7 @@ function createFullAutoPermissionHook(options) {
80646
80669
  phase: effectivePhase,
80647
80670
  taskID: taskId ?? undefined,
80648
80671
  planID: runState.planID,
80649
- architectOutput: undefined,
80672
+ architectOutput: output.args ? JSON.stringify(output.args) : undefined,
80650
80673
  actionContext: {
80651
80674
  tool: toolName,
80652
80675
  ...decision.context ?? {}
@@ -81352,6 +81375,7 @@ init_extractors();
81352
81375
  init_knowledge_reader();
81353
81376
  init_knowledge_store();
81354
81377
  init_utils2();
81378
+ var INJECTION_SENTINEL = "‌[[KNOWLEDGE-INJECTED]]";
81355
81379
  function buildKnowledgeBlock(entries, charBudget, cfg, currentProject) {
81356
81380
  if (entries.length === 0)
81357
81381
  return null;
@@ -81444,7 +81468,7 @@ function isOrchestratorAgent(agentName) {
81444
81468
  function injectKnowledgeMessage(output, text) {
81445
81469
  if (!output.messages)
81446
81470
  return;
81447
- const alreadyInjected = output.messages.some((m) => m.parts?.some((p) => p.text?.includes("\uD83D\uDCDA Lessons:") || p.text?.includes("<drift_report>") || p.text?.includes("<curator_briefing>")));
81471
+ const alreadyInjected = output.messages.some((m) => m.parts?.some((p) => p.text?.includes(INJECTION_SENTINEL)));
81448
81472
  if (alreadyInjected)
81449
81473
  return;
81450
81474
  let insertIdx = output.messages.length - 1;
@@ -81456,14 +81480,11 @@ function injectKnowledgeMessage(output, text) {
81456
81480
  }
81457
81481
  const knowledgeMessage = {
81458
81482
  info: { role: "system" },
81459
- parts: [{ type: "text", text }]
81483
+ parts: [{ type: "text", text: `${INJECTION_SENTINEL}${text}` }]
81460
81484
  };
81461
81485
  output.messages.splice(insertIdx, 0, knowledgeMessage);
81462
81486
  }
81463
81487
  function createKnowledgeInjectorHook(directory, config3) {
81464
- let lastSeenCacheKey = null;
81465
- let cachedInjectionText = null;
81466
- let cachedShownIds = [];
81467
81488
  function buildContextCacheKey(phase, ctx) {
81468
81489
  const parts2 = [
81469
81490
  String(phase),
@@ -81475,6 +81496,9 @@ function createKnowledgeInjectorHook(directory, config3) {
81475
81496
  ].join("|");
81476
81497
  return createHash7("sha1").update(parts2).digest("hex").slice(0, 16);
81477
81498
  }
81499
+ let lastSeenCacheKey = null;
81500
+ let cachedInjectionText = null;
81501
+ let cachedShownIds = [];
81478
81502
  return safeHook(async (_input, output) => {
81479
81503
  if (!output.messages || output.messages.length === 0)
81480
81504
  return;
@@ -87862,7 +87886,9 @@ var imports = createSwarmTool({
87862
87886
  });
87863
87887
  // src/tools/knowledge-ack.ts
87864
87888
  init_zod();
87889
+ import { randomUUID as randomUUID7 } from "node:crypto";
87865
87890
  init_state();
87891
+ init_logger();
87866
87892
  init_create_tool();
87867
87893
  var knowledge_ack = createSwarmTool({
87868
87894
  description: "Record an acknowledgment outcome (applied/ignored/violated) for a previously-injected knowledge directive. Updates retrieval-outcome counters and appends a record to .swarm/knowledge-application.jsonl.",
@@ -87890,7 +87916,11 @@ var knowledge_ack = createSwarmTool({
87890
87916
  result: a.result,
87891
87917
  reason: a.reason
87892
87918
  };
87893
- const sessionId = ctx?.sessionID ?? "unknown";
87919
+ let sessionId = ctx?.sessionID;
87920
+ if (!sessionId) {
87921
+ warn("[knowledge-ack] No sessionID in tool context — dedup disabled for this acknowledgment");
87922
+ sessionId = randomUUID7();
87923
+ }
87894
87924
  const dedupKey = buildAckDedupKey(sessionId, a.id, a.result);
87895
87925
  if (swarmState.knowledgeAckDedup.has(dedupKey)) {
87896
87926
  return JSON.stringify({
@@ -87919,7 +87949,7 @@ init_knowledge_store();
87919
87949
  init_knowledge_validator();
87920
87950
  init_manager();
87921
87951
  init_create_tool();
87922
- import { randomUUID as randomUUID7 } from "node:crypto";
87952
+ import { randomUUID as randomUUID8 } from "node:crypto";
87923
87953
  var VALID_CATEGORIES2 = [
87924
87954
  "process",
87925
87955
  "architecture",
@@ -87993,7 +88023,7 @@ var knowledge_add = createSwarmTool({
87993
88023
  project_name = plan?.title ?? "";
87994
88024
  } catch {}
87995
88025
  const entry = {
87996
- id: randomUUID7(),
88026
+ id: randomUUID8(),
87997
88027
  tier: "swarm",
87998
88028
  lesson,
87999
88029
  category,
@@ -99561,7 +99591,7 @@ ${fileList}
99561
99591
  async _withStateLock(fn) {
99562
99592
  const timeoutMs = 1e4;
99563
99593
  let timeoutId;
99564
- const withTimeout2 = new Promise((resolve47, reject) => {
99594
+ const withTimeout2 = new Promise((_resolve, reject) => {
99565
99595
  timeoutId = setTimeout(() => {
99566
99596
  reject(new Error(`_withStateLock timed out after ${timeoutMs}ms — state update will not block subsequent operations`));
99567
99597
  }, timeoutMs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.17.0",
3
+ "version": "7.17.1",
4
4
  "description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",