opencode-swarm 6.64.0 → 6.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,4 +8,21 @@ export interface AdversarialTestingConfig {
8
8
  enabled: boolean;
9
9
  scope: 'all' | 'security-only';
10
10
  }
11
- export declare function createArchitectAgent(model: string, customPrompt?: string, customAppendPrompt?: string, adversarialTesting?: AdversarialTestingConfig): AgentDefinition;
11
+ /**
12
+ * Subset of PluginConfig.council needed to gate the Work Complete Council
13
+ * workflow block in the architect prompt. Only `enabled` is consumed here —
14
+ * runtime behavior (maxRounds, timeout, veto priority) is enforced elsewhere
15
+ * via the council tools and config. Keeping this shape narrow avoids pulling
16
+ * the full PluginConfig type into the agent-prompt layer.
17
+ */
18
+ export interface CouncilWorkflowConfig {
19
+ enabled?: boolean;
20
+ }
21
+ /**
22
+ * Build the Work Complete Council four-phase workflow block. Returns the full
23
+ * block text when council.enabled === true, otherwise the empty string. The
24
+ * empty-string return path guarantees byte-for-byte non-regression when the
25
+ * council feature is off or the config key is absent.
26
+ */
27
+ export declare function buildCouncilWorkflow(council?: CouncilWorkflowConfig): string;
28
+ export declare function createArchitectAgent(model: string, customPrompt?: string, customAppendPrompt?: string, adversarialTesting?: AdversarialTestingConfig, council?: CouncilWorkflowConfig): AgentDefinition;
package/dist/cli/index.js CHANGED
@@ -14825,7 +14825,14 @@ async function savePlan(directory, plan, options) {
14825
14825
  const planId = `${validated.swarm}-${validated.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
14826
14826
  const planHashForInit = computePlanHash(validated);
14827
14827
  if (!await ledgerExists(directory)) {
14828
- await initLedger(directory, planId, planHashForInit, validated);
14828
+ try {
14829
+ await initLedger(directory, planId, planHashForInit, validated);
14830
+ } catch (initErr) {
14831
+ const msg = initErr instanceof Error ? initErr.message : String(initErr);
14832
+ if (!/already initialized/i.test(msg)) {
14833
+ throw initErr;
14834
+ }
14835
+ }
14829
14836
  } else {
14830
14837
  const existingEvents = await readLedgerEvents(directory);
14831
14838
  if (existingEvents.length > 0 && existingEvents[0].plan_id !== planId) {
@@ -18470,6 +18477,8 @@ var TOOL_NAMES = [
18470
18477
  "evidence_check",
18471
18478
  "check_gate_status",
18472
18479
  "completion_verify",
18480
+ "convene_council",
18481
+ "declare_council_criteria",
18473
18482
  "sbom_generate",
18474
18483
  "checkpoint",
18475
18484
  "pkg_audit",
@@ -18526,6 +18535,8 @@ var AGENT_TOOL_MAP = {
18526
18535
  "checkpoint",
18527
18536
  "check_gate_status",
18528
18537
  "completion_verify",
18538
+ "convene_council",
18539
+ "declare_council_criteria",
18529
18540
  "complexity_hotspots",
18530
18541
  "detect_domains",
18531
18542
  "evidence_check",
@@ -19182,6 +19193,14 @@ var AuthorityConfigSchema = exports_external.object({
19182
19193
  enabled: exports_external.boolean().default(true),
19183
19194
  rules: exports_external.record(exports_external.string(), AgentAuthorityRuleSchema).default({})
19184
19195
  });
19196
+ var CouncilConfigSchema = exports_external.object({
19197
+ enabled: exports_external.boolean().default(false),
19198
+ maxRounds: exports_external.number().int().min(1).max(10).default(3),
19199
+ parallelTimeoutMs: exports_external.number().int().min(5000).max(120000).default(30000),
19200
+ vetoPriority: exports_external.boolean().default(true),
19201
+ requireAllMembers: exports_external.boolean().default(false).describe("When true, convene_council rejects if fewer than 5 member verdicts are provided."),
19202
+ escalateOnMaxRounds: exports_external.string().optional().describe("Optional webhook URL or handler name invoked when maxRounds is reached without APPROVE. Declared for forward compatibility; no behavior is implemented yet.")
19203
+ }).strict();
19185
19204
  var PluginConfigSchema = exports_external.object({
19186
19205
  agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
19187
19206
  swarms: exports_external.record(exports_external.string(), SwarmConfigSchema).optional(),
@@ -19229,6 +19248,7 @@ var PluginConfigSchema = exports_external.object({
19229
19248
  }).optional(),
19230
19249
  incremental_verify: IncrementalVerifyConfigSchema.optional(),
19231
19250
  compaction_service: CompactionConfigSchema.optional(),
19251
+ council: CouncilConfigSchema.optional(),
19232
19252
  turbo_mode: exports_external.boolean().default(false).optional(),
19233
19253
  full_auto: exports_external.object({
19234
19254
  enabled: exports_external.boolean().default(false),
@@ -511,6 +511,15 @@ export declare const AuthorityConfigSchema: z.ZodObject<{
511
511
  }, z.core.$strip>>>;
512
512
  }, z.core.$strip>;
513
513
  export type AuthorityConfig = z.infer<typeof AuthorityConfigSchema>;
514
+ export declare const CouncilConfigSchema: z.ZodObject<{
515
+ enabled: z.ZodDefault<z.ZodBoolean>;
516
+ maxRounds: z.ZodDefault<z.ZodNumber>;
517
+ parallelTimeoutMs: z.ZodDefault<z.ZodNumber>;
518
+ vetoPriority: z.ZodDefault<z.ZodBoolean>;
519
+ requireAllMembers: z.ZodDefault<z.ZodBoolean>;
520
+ escalateOnMaxRounds: z.ZodOptional<z.ZodString>;
521
+ }, z.core.$strict>;
522
+ export type CouncilConfig = z.infer<typeof CouncilConfigSchema>;
514
523
  export declare const PluginConfigSchema: z.ZodObject<{
515
524
  agents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
516
525
  model: z.ZodOptional<z.ZodString>;
@@ -854,6 +863,14 @@ export declare const PluginConfigSchema: z.ZodObject<{
854
863
  emergencyThreshold: z.ZodDefault<z.ZodNumber>;
855
864
  preserveLastNTurns: z.ZodDefault<z.ZodNumber>;
856
865
  }, z.core.$strip>>;
866
+ council: z.ZodOptional<z.ZodObject<{
867
+ enabled: z.ZodDefault<z.ZodBoolean>;
868
+ maxRounds: z.ZodDefault<z.ZodNumber>;
869
+ parallelTimeoutMs: z.ZodDefault<z.ZodNumber>;
870
+ vetoPriority: z.ZodDefault<z.ZodBoolean>;
871
+ requireAllMembers: z.ZodDefault<z.ZodBoolean>;
872
+ escalateOnMaxRounds: z.ZodOptional<z.ZodString>;
873
+ }, z.core.$strict>>;
857
874
  turbo_mode: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
858
875
  full_auto: z.ZodDefault<z.ZodOptional<z.ZodObject<{
859
876
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Work Complete Council — advisory routing helper.
3
+ *
4
+ * Purpose:
5
+ * Routes a CouncilSynthesis (unifiedFeedbackMd + verdict metadata) into the
6
+ * architect's non-blocking advisory queue (`session.pendingAdvisoryMessages`).
7
+ * The guardrails `messagesTransform` hook drains that queue into an
8
+ * [ADVISORIES] block prepended to the architect's first SYSTEM message on
9
+ * the next turn.
10
+ *
11
+ * Runtime call site:
12
+ * `src/tools/convene-council.ts` invokes this helper after writing evidence,
13
+ * guarded by `ctx?.sessionID` and `getAgentSession`. Missing session, missing
14
+ * sessionID, or any thrown error silently skip — the advisory is never
15
+ * critical-path. The helper is also re-exported for direct use by any future
16
+ * caller that wants to push synthesis output into an advisory queue.
17
+ *
18
+ * Scope and known limitation:
19
+ * The advisory queue is READ by the architect session on its next turn (self
20
+ * echo). It is NOT a programmatic architect→coder delivery channel — the
21
+ * architect still has to render `unifiedFeedbackMd` into the coder's
22
+ * delegation payload manually, per the prompt's four-phase workflow. A
23
+ * dedicated architect→coder advisory primitive is future work.
24
+ *
25
+ * Dedup semantics:
26
+ * Dedup key is `council:${taskId}:${roundNumber}`. If the queue already
27
+ * contains a string whose content includes that key, the push is a no-op.
28
+ * Different rounds or tasks push distinct entries.
29
+ *
30
+ * Blocking signal (metadata only):
31
+ * - REJECT → header declares `blocking=true`. Council vetoed the candidate.
32
+ * - CONCERNS→ `blocking=false`. Architect should weigh fixes but is not vetoed.
33
+ * - APPROVE → `blocking=false`. Helper skips push entirely when there are no
34
+ * advisoryFindings (nothing useful to surface).
35
+ */
36
+ import type { AgentSessionState } from '../state';
37
+ import type { CouncilSynthesis } from './types';
38
+ /**
39
+ * Push a CouncilSynthesis into the given session's advisory queue so the
40
+ * architect will see it as an [ADVISORIES] block on the next messagesTransform.
41
+ *
42
+ * Idempotent per (taskId, roundNumber): repeated calls with the same key
43
+ * leave the queue unchanged. Safe to call on APPROVE — it is a no-op when
44
+ * there are no advisoryFindings.
45
+ */
46
+ export declare function pushCouncilAdvisory(session: Pick<AgentSessionState, 'pendingAdvisoryMessages'>, synthesis: CouncilSynthesis): void;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Work Complete Council — evidence writer.
3
+ *
4
+ * Stamps the council synthesis result into `.swarm/evidence/{taskId}.json`
5
+ * under `gates.council`, matching the shape other gate writers use and the
6
+ * shape that `check_gate_status` and `update_task_status` consume (they read
7
+ * `evidence.gates[gateName]`). Council-specific fields (verdict, vetoedBy,
8
+ * roundNumber, allCriteriaMet) are stored alongside the standard GateInfo
9
+ * fields (sessionId, timestamp, agent); existing consumers only check
10
+ * `gates.council != null`, so the extras are compatible.
11
+ *
12
+ * Existing fields in the evidence file — top-level keys AND other `gates[*]`
13
+ * entries — are preserved across the write. The raw taskId is used as the
14
+ * filename; defense-in-depth regex validation rejects malformed IDs before
15
+ * any filesystem op.
16
+ */
17
+ import type { CouncilSynthesis } from './types';
18
+ export declare function writeCouncilEvidence(workingDir: string, synthesis: CouncilSynthesis): void;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Work Complete Council — pure synthesis service.
3
+ *
4
+ * Given the verdicts of council members (critic, reviewer, sme, test_engineer),
5
+ * compute the overall verdict, classify findings, detect conflicts, and build a
6
+ * single unified feedback document for the coder.
7
+ *
8
+ * No I/O — fully unit-testable with mock inputs. All file reads/writes happen in
9
+ * sibling modules (criteria-store, council-evidence-writer).
10
+ */
11
+ import type { CouncilConfig, CouncilCriteria, CouncilMemberVerdict, CouncilSynthesis } from './types';
12
+ export declare function synthesizeCouncilVerdicts(taskId: string, swarmId: string, verdicts: CouncilMemberVerdict[], criteria: CouncilCriteria | null, roundNumber: number, config?: Partial<CouncilConfig>): CouncilSynthesis;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Work Complete Council — pre-declaration criteria writer/reader.
3
+ *
4
+ * Stores acceptance criteria under .swarm/council/{safeId}.json so they can be
5
+ * read back during council evaluation.
6
+ */
7
+ import type { CouncilCriteria, CouncilCriteriaItem } from './types';
8
+ export declare function writeCriteria(workingDir: string, taskId: string, criteria: CouncilCriteriaItem[]): void;
9
+ export declare function readCriteria(workingDir: string, taskId: string): CouncilCriteria | null;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Work Complete Council — data contracts.
3
+ *
4
+ * Flat, stable schema — no nested generics. Designed for reliable LLM output.
5
+ * No business logic, no I/O. Only types, interfaces, and defaults.
6
+ */
7
+ export type CouncilVerdict = 'APPROVE' | 'CONCERNS' | 'REJECT';
8
+ export type CouncilFindingSeverity = 'HIGH' | 'MEDIUM' | 'LOW';
9
+ export type CouncilFindingCategory = 'logic' | 'edge_case' | 'error_handling' | 'spec_compliance' | 'security' | 'maintainability' | 'naming' | 'domain' | 'test_gap' | 'test_quality' | 'mutation_gap' | 'adversarial_gap' | 'slop_pattern' | 'hallucinated_api' | 'lazy_abstraction' | 'cargo_cult' | 'spec_drift' | 'other';
10
+ export type CouncilAgent = 'critic' | 'reviewer' | 'sme' | 'test_engineer' | 'explorer';
11
+ export interface CouncilFinding {
12
+ severity: CouncilFindingSeverity;
13
+ category: CouncilFindingCategory;
14
+ /** e.g. "src/tools/convene-council.ts:42" */
15
+ location: string;
16
+ /** Human-readable explanation */
17
+ detail: string;
18
+ /** Concrete quote or line reference */
19
+ evidence: string;
20
+ }
21
+ export interface CouncilMemberVerdict {
22
+ agent: CouncilAgent;
23
+ verdict: CouncilVerdict;
24
+ /** Confidence 0.0–1.0 */
25
+ confidence: number;
26
+ findings: CouncilFinding[];
27
+ /** Criteria IDs from pre-declaration (e.g. ["C1","C3"]) */
28
+ criteriaAssessed: string[];
29
+ /** Criteria IDs that failed */
30
+ criteriaUnmet: string[];
31
+ durationMs: number;
32
+ }
33
+ export interface CouncilSynthesis {
34
+ taskId: string;
35
+ swarmId: string;
36
+ /** ISO 8601 */
37
+ timestamp: string;
38
+ overallVerdict: CouncilVerdict;
39
+ vetoedBy: CouncilAgent[] | null;
40
+ memberVerdicts: CouncilMemberVerdict[];
41
+ unresolvedConflicts: string[];
42
+ /** Severity HIGH + MEDIUM from veto members */
43
+ requiredFixes: CouncilFinding[];
44
+ /** Severity LOW or from non-veto members */
45
+ advisoryFindings: CouncilFinding[];
46
+ /** Single markdown document sent to coder */
47
+ unifiedFeedbackMd: string;
48
+ /** 1-indexed */
49
+ roundNumber: number;
50
+ allCriteriaMet: boolean;
51
+ }
52
+ export interface CouncilCriteriaItem {
53
+ id: string;
54
+ description: string;
55
+ mandatory: boolean;
56
+ }
57
+ export interface CouncilCriteria {
58
+ taskId: string;
59
+ criteria: CouncilCriteriaItem[];
60
+ /** ISO 8601 */
61
+ declaredAt: string;
62
+ }
63
+ /** Config shape — matched in schema.ts via CouncilConfigSchema. */
64
+ export interface CouncilConfig {
65
+ enabled: boolean;
66
+ /** Default 3 */
67
+ maxRounds: number;
68
+ /** Default 30_000 */
69
+ parallelTimeoutMs: number;
70
+ /** Default true — any REJECT blocks */
71
+ vetoPriority: boolean;
72
+ /** Default false — when true, convene_council rejects unless all 5 member verdicts are provided */
73
+ requireAllMembers: boolean;
74
+ /** Optional webhook URL or handler name invoked when maxRounds is reached without APPROVE. Declared for forward compatibility; no behavior is implemented yet. */
75
+ escalateOnMaxRounds?: string;
76
+ }
77
+ export declare const COUNCIL_DEFAULTS: CouncilConfig;
@@ -13,6 +13,7 @@ export { consolidateSystemMessages } from './messages-transform';
13
13
  export { extractModelInfo, NATIVE_MODEL_LIMITS, PROVIDER_CAPS, resolveModelLimit, } from './model-limits';
14
14
  export { type CuratorDelegateFactory, createPhaseMonitorHook, } from './phase-monitor';
15
15
  export { createPipelineTrackerHook } from './pipeline-tracker';
16
+ export { createRepoGraphBuilderHook, type RepoGraphBuilderHook, } from './repo-graph-builder';
16
17
  export { buildApprovedReceipt, buildReceiptContextForDrift, buildRejectedReceipt, persistReviewReceipt, readAllReceipts, readReceiptsByScopeHash, } from './review-receipt';
17
18
  export { createSystemEnhancerHook } from './system-enhancer';
18
19
  export { createToolSummarizerHook, resetSummaryIdCounter, } from './tool-summarizer';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Repo Graph Builder Hook
3
+ *
4
+ * Startup hook that builds or refreshes the repo dependency graph when a session starts.
5
+ * Write-trigger hook that incrementally updates the graph when write tools are called.
6
+ * Wrapped in try/catch — failures are logged but never block plugin initialization.
7
+ */
8
+ export interface RepoGraphBuilderHook {
9
+ init(): Promise<void>;
10
+ toolAfter(input: {
11
+ tool: string;
12
+ sessionID: string;
13
+ args?: unknown;
14
+ }, output: {
15
+ output?: unknown;
16
+ args?: unknown;
17
+ }): Promise<void>;
18
+ }
19
+ export interface RepoGraphDeps {
20
+ buildWorkspaceGraph: (workspace: string, options?: any) => any;
21
+ saveGraph: (workspace: string, graph: any) => Promise<void>;
22
+ updateGraphForFiles: (workspace: string, files: string[], options?: any) => Promise<any>;
23
+ }
24
+ export declare function createRepoGraphBuilderHook(workspaceRoot: string, deps?: Partial<RepoGraphDeps>): RepoGraphBuilderHook;