opencode-swarm 7.181.1 → 7.181.3

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 (99) hide show
  1. package/binaries/win32-arm64/swarm-sandbox-runner.exe +0 -0
  2. package/binaries/win32-x64/swarm-sandbox-runner.exe +0 -0
  3. package/dist/background/stage-b-gates.d.ts +4 -0
  4. package/dist/cli/{coder-settlement-ee8bmg83.js → coder-settlement-txte2m87.js} +11 -11
  5. package/dist/cli/{config-doctor-mnmxzg86.js → config-doctor-101n3c1r.js} +3 -3
  6. package/dist/cli/{core-m9yymv84.js → core-s0yzzpaj.js} +3 -3
  7. package/dist/cli/{curation-policy-m3x9mkj9.js → curation-policy-r4dkajw9.js} +4 -4
  8. package/dist/cli/{curator-7nmwpcxy.js → curator-dp49fr0p.js} +38 -38
  9. package/dist/cli/curator-drift-r7w3cg3m.js +37 -0
  10. package/dist/cli/curator-llm-factory-tycc9xkw.js +74 -0
  11. package/dist/cli/{evidence-summary-service-m2b0mxmr.js → evidence-summary-service-r648x5f2.js} +17 -17
  12. package/dist/cli/{gate-evidence-4y2x85g4.js → gate-evidence-8rh69tsh.js} +6 -6
  13. package/dist/cli/guardrail-explain-dfyj1g1c.js +75 -0
  14. package/dist/cli/{guardrail-log-202rfw7b.js → guardrail-log-yr60w7h6.js} +7 -7
  15. package/dist/cli/{guardrail-reset-7nt7z18b.js → guardrail-reset-eztc62va.js} +38 -38
  16. package/dist/cli/{hive-promoter-segsykns.js → hive-promoter-g56ba4a4.js} +38 -38
  17. package/dist/cli/{index-m65tnv4j.js → index-01r2wrwa.js} +1 -1
  18. package/dist/cli/{index-hxe22tx3.js → index-1ks0rnzn.js} +5 -5
  19. package/dist/cli/{index-ztph1v9m.js → index-1w1xrk5k.js} +1 -1
  20. package/dist/cli/{index-12ewkms0.js → index-23mdeh2a.js} +1 -1
  21. package/dist/cli/{index-a7z6jygf.js → index-2pt5g7zz.js} +3 -3
  22. package/dist/cli/{index-y4zjbrqs.js → index-2t21pv2v.js} +1 -1
  23. package/dist/cli/{index-38cfx8g8.js → index-427x3j6p.js} +2 -2
  24. package/dist/cli/{index-tczqm089.js → index-4ffryqb1.js} +1 -1
  25. package/dist/cli/{index-pee6ykkg.js → index-4jvt3r77.js} +5 -5
  26. package/dist/cli/{index-908yea8v.js → index-6ep83w7x.js} +1 -1
  27. package/dist/cli/{index-r6p6x0pb.js → index-789tdsfv.js} +4 -4
  28. package/dist/cli/{index-yhx94xpr.js → index-7s6ct0t2.js} +6 -6
  29. package/dist/cli/{index-9j5jjqks.js → index-8c37kzh6.js} +7 -7
  30. package/dist/cli/{index-gc6h1fmf.js → index-a2hvmc3a.js} +1 -1
  31. package/dist/cli/{index-ea9fk6v0.js → index-ap6hs8fj.js} +1 -1
  32. package/dist/cli/{index-92m05mew.js → index-by9wfeja.js} +1 -1
  33. package/dist/cli/{index-kjstkfgh.js → index-ckrm4784.js} +7 -7
  34. package/dist/cli/{index-rxs0rrv1.js → index-dy9mxh0z.js} +40 -40
  35. package/dist/cli/{index-b2zbak7v.js → index-ekwe81my.js} +2 -2
  36. package/dist/cli/{index-3qhx9dx0.js → index-h6gxh3an.js} +4946 -1965
  37. package/dist/cli/{index-basp4sxt.js → index-hcaspktr.js} +1 -1
  38. package/dist/cli/{index-bdpgfep1.js → index-jrxrjz89.js} +1 -1
  39. package/dist/cli/{index-q1prchye.js → index-k5rqe97g.js} +27 -13
  40. package/dist/cli/{index-4h54k8h3.js → index-kexzhwbq.js} +2 -2
  41. package/dist/cli/{index-516j9vsk.js → index-kkrbybgt.js} +27 -2
  42. package/dist/cli/{index-cyyvc1cd.js → index-m4e3p8na.js} +2 -2
  43. package/dist/cli/{index-jqjdejqe.js → index-mdqp0cq2.js} +4 -4
  44. package/dist/cli/{index-6mwha20t.js → index-mkpdcbye.js} +3 -3
  45. package/dist/cli/{index-y30y1rkg.js → index-nw3z614g.js} +6 -6
  46. package/dist/cli/{index-nhxykfyy.js → index-nw8pfcw1.js} +270 -10
  47. package/dist/cli/{index-na3br902.js → index-qdz43cne.js} +38 -38
  48. package/dist/cli/{index-g9ax7jyf.js → index-rchygh27.js} +57 -4
  49. package/dist/cli/{index-6gcxgx84.js → index-s34y6gbh.js} +1 -1
  50. package/dist/cli/{index-s0zgz4p1.js → index-sf51gh68.js} +1 -1
  51. package/dist/cli/{index-gydvqmtg.js → index-sr9k8sh7.js} +4 -4
  52. package/dist/cli/{index-edb3sgxg.js → index-ttgex8ky.js} +6 -6
  53. package/dist/cli/{index-91dhg3wr.js → index-ved1325w.js} +2 -2
  54. package/dist/cli/{index-rw0wy2y7.js → index-vgjmahnd.js} +3 -3
  55. package/dist/cli/{index-bv4b8tpa.js → index-vvgpgcyn.js} +10 -0
  56. package/dist/cli/{index-vmthakz0.js → index-w1ydvzef.js} +1 -1
  57. package/dist/cli/{index-gqe4sm73.js → index-xqr076aq.js} +4 -4
  58. package/dist/cli/{index-g5wjasjn.js → index-yv0q0hp9.js} +140 -9
  59. package/dist/cli/{index-vs83acgx.js → index-yzapt508.js} +3 -3
  60. package/dist/cli/index.js +39 -39
  61. package/dist/cli/{knowledge-escalator-r9chkas8.js → knowledge-escalator-0rcft6q4.js} +12 -12
  62. package/dist/cli/{knowledge-events-5j2n6age.js → knowledge-events-7jyz3qxs.js} +10 -10
  63. package/dist/cli/{knowledge-link-s2qs178z.js → knowledge-link-655xa3db.js} +3 -3
  64. package/dist/cli/{knowledge-store-mybx9bmx.js → knowledge-store-asa26g6p.js} +4 -4
  65. package/dist/cli/{knowledge-validator-v1sj9tv4.js → knowledge-validator-j3x9ezej.js} +6 -6
  66. package/dist/cli/{mcp-7yx5230p.js → mcp-70hefjk6.js} +1 -1
  67. package/dist/cli/model-preflight-szekh1dc.js +359 -0
  68. package/dist/cli/{pending-delegations-amjfmc7t.js → pending-delegations-rb6bhv5x.js} +10 -10
  69. package/dist/cli/{pr-subscriptions-rctqs0gk.js → pr-subscriptions-v4ms5pfb.js} +10 -10
  70. package/dist/cli/{pr-workflow-gate-9he8ys4e.js → pr-workflow-gate-qmfpz6wb.js} +38 -38
  71. package/dist/cli/{runner-tzgwvvn1.js → runner-b5vt1any.js} +4 -4
  72. package/dist/cli/{scan-cursor-pxqkphrh.js → scan-cursor-7zn4t1hw.js} +5 -5
  73. package/dist/cli/{schema-n8pk3f80.js → schema-tx62mfy8.js} +2 -2
  74. package/dist/cli/{scope-persistence-p992x2yd.js → scope-persistence-f8w12sns.js} +14 -14
  75. package/dist/cli/{server-ywrd2q6z.js → server-f2vrp2wc.js} +73 -603
  76. package/dist/cli/{skill-generator-cdppktqx.js → skill-generator-jme09w7n.js} +14 -14
  77. package/dist/cli/{snapshot-coordination-init-z42ev5g9.js → snapshot-coordination-init-ad83sq6c.js} +38 -38
  78. package/dist/cli/{speckit-checkoff-6ynv46bn.js → speckit-checkoff-98gfvn3r.js} +4 -4
  79. package/dist/cli/{telemetry-zhcgfe70.js → telemetry-6k8t2mrw.js} +1 -1
  80. package/dist/cli/{workspace-snapshot-4m2ex1ey.js → workspace-snapshot-hsssm50w.js} +2 -2
  81. package/dist/cli/{worktree-collision-ownership-65971p3m.js → worktree-collision-ownership-47kdjtqb.js} +11 -11
  82. package/dist/cli/{worktree-isolation-xf7dfc46.js → worktree-isolation-etaawcnt.js} +38 -38
  83. package/dist/commands/registry.d.ts +1 -1
  84. package/dist/config/agent-model.d.ts +14 -0
  85. package/dist/config/bundled-skill-dispositions.d.ts +29 -0
  86. package/dist/db/qa-gate-profile.d.ts +6 -3
  87. package/dist/db/qa-gate-session-override.d.ts +72 -0
  88. package/dist/index.js +404 -390
  89. package/dist/memory/index.d.ts +1 -0
  90. package/dist/memory/instruction-pairing.d.ts +112 -0
  91. package/dist/services/model-preflight.d.ts +103 -21
  92. package/dist/session/restart-reconciliation.d.ts +74 -0
  93. package/dist/state.d.ts +6 -3
  94. package/dist/tools/get-qa-gate-profile.d.ts +13 -6
  95. package/package.json +1 -1
  96. package/dist/cli/curator-drift-q42fr0c9.js +0 -297
  97. package/dist/cli/curator-llm-factory-s1b2p162.js +0 -74
  98. package/dist/cli/guardrail-explain-nvmf3y7j.js +0 -75
  99. package/dist/cli/model-preflight-ckvv1arg.js +0 -170
@@ -7,6 +7,7 @@ export type { MemoryGatewayOptions, ProposeMemoryInput, RecallMemoryInput, Recor
7
7
  export { createConfiguredMemoryProvider, createConfiguredMemoryProviderForRoot, createMemoryGateway, MemoryGateway, } from './gateway';
8
8
  export { type HeldoutRecallCorpusCase, type HeldoutRecallCorpusManifest, type LoadedHeldoutRecallScenario, loadHeldoutRecallEvaluationCorpus, loadHeldoutRecallEvaluationScenarios, validateHeldoutRecallCorpusManifest, } from './heldout-evaluation-corpus';
9
9
  export { createMemoryLifecycleHooks, type MemoryLifecycleHookOptions, type MemoryLifecycleHooks, } from './injector';
10
+ export { DEFAULT_INSTRUCTION_PAIRING_TASKS, type InstructionPairingArmResult, type InstructionPairingDirectivePriority, type InstructionPairingOptions, type InstructionPairingPair, type InstructionPairingRecord, type InstructionPairingReport, type InstructionPairingTask, runInstructionSelectionPairing, } from './instruction-pairing';
10
11
  export { backupLegacyJsonl, getLegacyJsonlFileStatus, getLegacyOutcomeJsonlSignature, type JsonlBackupResult, type JsonlImportPayload, type JsonlInvalidRow, type JsonlMigrationReport, LEGACY_JSONL_MIGRATION_NAME, LEGACY_JSONL_MIGRATION_VERSION, LEGACY_JSONL_OUTCOME_META_KEY, readLegacyJsonl, readMigrationReport, resolveMemoryStorageDir, resolveSqliteDatabasePath, writeJsonlExport, writeMigrationReport, } from './jsonl-migration';
11
12
  export { LocalJsonlMemoryProvider } from './local-jsonl-provider';
12
13
  export { buildMemoryMaintenanceReport, type MemoryMaintenanceReport, type MemoryMaintenanceReportOptions, type MemoryRecallUsageByMemory, type MemoryRecallUsageByRole, type MemorySupersededChain, shouldCompactMemory, } from './maintenance';
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Paired cached-vs-uncached outcome evidence for instruction selection (#2672).
3
+ *
4
+ * Runs the REAL knowledge-injector hook (the plugin's instruction-selection and
5
+ * instruction-cache surface) over deterministic, offline fixture tasks and
6
+ * reports paired per-task outcomes: quality (which directive labels survive
7
+ * selection), latency per arm, cache reads, uncached cost, and the rendered
8
+ * prefix length at the host-renderable carrier boundary.
9
+ *
10
+ * Contract discipline (issue #2672):
11
+ * - Same task/model/budget on both arms; the uncached arm is the regeneration
12
+ * reference and the cached arm replays byte-identical context on a warm
13
+ * per-instance cache.
14
+ * - Negative results are RETAINED rows (`negative_result: true` when the
15
+ * cached arm did not improve the paired quality outcome). On the offline
16
+ * deterministic corpus both arms run the same selection algorithm over the
17
+ * same records, so `quality_outcome` is 'identical' by design — the honest
18
+ * "caching did not change instruction quality" negative result.
19
+ * - NO percentage or savings claim exists anywhere in the report: deltas are
20
+ * absolute numbers and every quantity names its denominator in `measurement`.
21
+ * - `identity.instruction_set_digest` is the evidence handle a HarnessOpt
22
+ * lineage record (#2503) can reference; this module never modifies the
23
+ * harness-optimizer — #2503 remains the broad held-out comparison owner.
24
+ */
25
+ export type InstructionPairingDirectivePriority = 'critical' | 'high' | 'medium' | 'low';
26
+ export interface InstructionPairingRecord {
27
+ /**
28
+ * Stable identifier recovered from the rendered prefix. Constrained to
29
+ * `[A-Za-z0-9-]` (PRR-012): other characters still reach the fixture
30
+ * store verbatim but can never be matched back by the sentinel pattern,
31
+ * silently undercounting `quality.expected_hit_count` for that record.
32
+ */
33
+ label: string;
34
+ lesson: string;
35
+ directivePriority?: InstructionPairingDirectivePriority;
36
+ tags?: string[];
37
+ }
38
+ export interface InstructionPairingTask {
39
+ id: string;
40
+ lastUserMessage: string;
41
+ expectedLabels: string[];
42
+ records: InstructionPairingRecord[];
43
+ }
44
+ export interface InstructionPairingOptions {
45
+ /**
46
+ * Project directory context. Fixture stores live in disposable temp roots
47
+ * (never under the caller's `.swarm/`); when `writeReport` is set, the
48
+ * durable report artifact is written under this directory's
49
+ * `.swarm/memory/`.
50
+ */
51
+ directory: string;
52
+ /** Omitted falls back to the deterministic DEFAULT_INSTRUCTION_PAIRING_TASKS. */
53
+ tasks?: InstructionPairingTask[];
54
+ modelId?: string;
55
+ injectCharBudget?: number;
56
+ /** Explicit report destination; overrides the `writeReport` default path. */
57
+ reportPath?: string;
58
+ /** Write the report to `<directory>/.swarm/memory/instruction-pairing-report.json`. */
59
+ writeReport?: boolean;
60
+ }
61
+ export interface InstructionPairingArmResult {
62
+ latency_ms: number;
63
+ /** Injections served from the warm context cache in the measured invocation (0 or 1). */
64
+ cache_reads: number;
65
+ /** How cache_reads was determined ('events-file-delta' = observed, 'protocol-assumed' = identical-context replay). */
66
+ cache_reads_basis: string;
67
+ rendered_prefix_chars: number;
68
+ /** The paired uncached arm's regeneration latency, reported on both arms (paired attribution, absolute ms). */
69
+ uncached_cost: number;
70
+ quality: {
71
+ expected_hit_count: number;
72
+ expected_total: number;
73
+ selected_labels: string[];
74
+ };
75
+ }
76
+ export interface InstructionPairingPair {
77
+ task: {
78
+ id: string;
79
+ };
80
+ arm_cached: InstructionPairingArmResult;
81
+ arm_uncached: InstructionPairingArmResult;
82
+ quality_outcome: 'identical' | 'improved' | 'degraded';
83
+ negative_result: boolean;
84
+ }
85
+ export interface InstructionPairingReport {
86
+ schema_version: 1;
87
+ generated_at: string;
88
+ identity: {
89
+ task_set_id: string;
90
+ model_id: string;
91
+ budget_chars: number;
92
+ instruction_set_digest: string;
93
+ };
94
+ pairs: InstructionPairingPair[];
95
+ measurement: {
96
+ latency_denominator: string;
97
+ cost_denominator: string;
98
+ prefix_denominator: string;
99
+ };
100
+ negative_results_retained: boolean;
101
+ cache_invalidation: {
102
+ verified: boolean;
103
+ detail: string;
104
+ };
105
+ }
106
+ /** Deterministic offline corpus used when `options.tasks` is omitted. */
107
+ export declare const DEFAULT_INSTRUCTION_PAIRING_TASKS: InstructionPairingTask[];
108
+ /**
109
+ * Runs the paired cached-vs-uncached control over the task set and returns the
110
+ * report. Deterministic and offline (lexical store, hive disabled, real hook).
111
+ */
112
+ export declare function runInstructionSelectionPairing(options: InstructionPairingOptions): Promise<InstructionPairingReport>;
@@ -1,18 +1,21 @@
1
1
  /**
2
- * Agent model-resolution preflight (issue #2271 bug 4).
2
+ * Agent model-resolution preflight (issue #2271 bug 4; issue #2680).
3
3
  *
4
- * Agent model strings (DEFAULT_MODELS plus user `agents.<role>.model`
5
- * overrides) are handed to the OpenCode host unvalidated. When one does not
6
- * resolve ("Model not found", "Forbidden"), dispatch-lanes classifies the
7
- * failure as permanent, the affected agent never runs, and the plan-critic
8
- * gate wedges into manual `approve_plan_critic` overrides because no APPROVED
9
- * verdict is ever produced.
4
+ * Agent model strings (DEFAULT_MODELS plus user `agents.<role>.model` /
5
+ * `swarms.<id>.agents.<role>.model` overrides) are handed to the OpenCode host
6
+ * unvalidated. When one does not resolve ("Model not found", "Forbidden"),
7
+ * dispatch-lanes classifies the failure as permanent, the affected agent never
8
+ * runs, and the plan-critic gate wedges into manual `approve_plan_critic`
9
+ * overrides because no APPROVED verdict is ever produced.
10
10
  *
11
- * This module validates configured model IDs against the live provider
12
- * catalog (`client.provider.list()`). Every check is FAIL-OPEN: if the catalog
13
- * is unreachable or malformed, results come back `unknown` and callers must
14
- * proceed the preflight only acts on a POSITIVE confirmation that a model
15
- * does not resolve.
11
+ * This module validates the FINAL effective model selection for every ENABLED
12
+ * legacy and prefixed multi-swarm role (issue #2680) against the live provider
13
+ * catalog (`client.provider.list()`), reusing the exact-name resolution
14
+ * helpers from src/config/agent-model.ts. Every check is FAIL-OPEN: if the
15
+ * catalog is unreachable or malformed, results come back `unknown` and callers
16
+ * must proceed — the preflight only acts on a POSITIVE confirmation that a
17
+ * model does not resolve (or that no final selection exists at all, the
18
+ * catalog-independent `missing-selection` class).
16
19
  */
17
20
  import type { OpencodeClient } from '@opencode-ai/sdk';
18
21
  import type { PluginConfig } from '../config/schema.js';
@@ -21,12 +24,17 @@ export declare const PROVIDER_LIST_TIMEOUT_MS = 2000;
21
24
  export interface ConfiguredAgentModel {
22
25
  /** Agent role the model is configured for. */
23
26
  agent: string;
24
- /** Full model id in `provider/model` form. */
27
+ /** Full model id in `provider/model` form. Empty when no final selection exists. */
25
28
  model: string;
26
29
  /** Where the value came from. */
27
30
  source: 'default' | 'override' | 'fallback';
31
+ /**
32
+ * Issue #2680: whether the entry's role is registered as a primary agent
33
+ * (OpenCode's UI owns its model selection) or a dispatchable subagent.
34
+ */
35
+ mode?: 'primary' | 'subagent';
28
36
  }
29
- export type ModelResolutionStatus = 'ok' | 'unresolved' | 'unknown';
37
+ export type ModelResolutionStatus = 'ok' | 'unresolved' | 'unknown' | 'missing-selection' | 'host-controlled';
30
38
  export interface ModelResolution {
31
39
  agent: string;
32
40
  model: string;
@@ -34,6 +42,7 @@ export interface ModelResolution {
34
42
  status: ModelResolutionStatus;
35
43
  /** Human-readable explanation for non-ok statuses. */
36
44
  detail?: string;
45
+ mode?: ConfiguredAgentModel['mode'];
37
46
  }
38
47
  export interface ModelPreflightResult {
39
48
  /** False when the catalog could not be fetched — every entry is `unknown`. */
@@ -53,29 +62,102 @@ export declare function splitModelId(model: string): {
53
62
  * Collect every model id the swarm could dispatch with: DEFAULT_MODELS
54
63
  * entries, user agent overrides, and declared fallback_models. Overriding one
55
64
  * role replaces its default (mirrors getModelForAgent precedence).
65
+ *
66
+ * Superseded by collectEnabledAgentModels (issue #2680): this legacy view
67
+ * enumerates the top-level config plus a blanket DEFAULT_MODELS pass and
68
+ * cannot see multi-swarm per-swarm selections or enabled-role gating. Retained
69
+ * unchanged for API compatibility; runModelPreflight no longer consumes it.
56
70
  */
57
71
  export declare function collectConfiguredAgentModels(config?: PluginConfig): ConfiguredAgentModel[];
72
+ /**
73
+ * Issue #2680: compute the generated agent names the plugin would register for
74
+ * this configuration — the "enabled role" domain the preflight must validate.
75
+ * Mirrors createAgents' generation rules (legacy single default swarm vs
76
+ * per-swarm prefixing; object-level top-level merge; disabled exclusion;
77
+ * feature-gated roles only while their flag is on). The live
78
+ * swarmState.generatedAgentNames registry stays authoritative when available;
79
+ * this computation is the config-only fallback (doctor, unit tests) and is
80
+ * drift-guarded by the parity test.
81
+ */
82
+ export declare function enumerateEnabledAgentRoles(config?: PluginConfig): string[];
83
+ /** Options for the enabled-role preflight (issue #2680). */
84
+ export interface EnabledAgentPreflightOptions {
85
+ /**
86
+ * The live registry of generated agent names the host actually registered
87
+ * (swarmState.generatedAgentNames). Authoritative when provided; the
88
+ * config-only enumeration is the fallback.
89
+ */
90
+ generatedAgentNames?: readonly string[];
91
+ /**
92
+ * The registered agents map (name → { model, mode }) as produced by
93
+ * createAgents/getAgentConfigs. When provided, `mode` comes from it;
94
+ * otherwise primaries are derived via resolvePrimaryAgentNames.
95
+ */
96
+ registeredAgents?: Record<string, {
97
+ model?: unknown;
98
+ mode?: unknown;
99
+ }>;
100
+ }
101
+ /**
102
+ * Issue #2680: collect the FINAL effective model selection for every enabled
103
+ * legacy and prefixed multi-swarm role, plus each role's explicitly configured
104
+ * fallback entries. Final selections are computed with the shipped exact-name
105
+ * helpers (resolveRegisteredAgentModel / resolveConfiguredAgentModel) so
106
+ * defaults, swarm prefixes, and the object-level merge match dispatch reality.
107
+ *
108
+ * Outcome classes (see resolveAgainstCatalog):
109
+ * - an explicitly configured but blank model, or a registry name that resolves
110
+ * to no target at all, yields an EMPTY model — the distinct
111
+ * `missing-selection` class (never silently treated as a successful default);
112
+ * - primary roles are tagged `mode: 'primary'` → `host-controlled` (OpenCode's
113
+ * UI owns their selection);
114
+ * - fallback entries appear only when explicitly configured and recorded —
115
+ * never synthesized (curator roles inherit explorer's chain at runtime via
116
+ * resolveFallbackModel; that inheritance is documented, not duplicated here).
117
+ */
118
+ export declare function collectEnabledAgentModels(config?: PluginConfig, opts?: EnabledAgentPreflightOptions): ConfiguredAgentModel[];
58
119
  /** Provider catalog snapshot: provider id → set of model ids. */
59
120
  export type ProviderCatalog = Map<string, Set<string>>;
60
121
  /** Test/gate seam: drop the cached catalogs (used after config changes and denials). */
61
122
  export declare function invalidateProviderCatalogCache(): void;
123
+ /**
124
+ * PR #2782 review PRR-001: neutralize control characters before interpolating
125
+ * config- or host-sourced strings (model ids, agent names, provider detail)
126
+ * into console.warn / thrown-Error text. Newlines forge multi-line
127
+ * plugin-looking output and ESC sequences drive the terminal; control runs
128
+ * collapse to a single space (same policy as the doctor's stripControlChars).
129
+ */
130
+ export declare function sanitizePreflightText(value: string): string;
62
131
  /**
63
132
  * Fetch the provider catalog. Returns null when the catalog cannot be
64
133
  * obtained or parsed — callers fail open on null.
65
134
  */
66
135
  export declare function fetchProviderCatalog(client: OpencodeClient | null): Promise<ProviderCatalog | null>;
67
136
  /**
68
- * Validate collected models against a catalog. A model whose provider is
69
- * absent from the catalog, or whose provider exists but does not list the
70
- * model, is `unresolved` but only when the catalog itself was successfully
71
- * fetched (callers must treat a null catalog as fail-open, never unresolved).
137
+ * Validate collected models against a catalog. Classification order (issue
138
+ * #2680): an EMPTY model is the catalog-independent `missing-selection` class;
139
+ * a primary role is `host-controlled` (OpenCode's UI owns its selection
140
+ * never a preflight failure and never a consumer-default warning); otherwise
141
+ * the catalog decides — a model whose provider is absent from the catalog, or
142
+ * whose provider exists but does not list the model, is `unresolved` — but
143
+ * only when the catalog itself was successfully fetched (callers must treat a
144
+ * null catalog as fail-open, never unresolved).
72
145
  */
73
146
  export declare function resolveAgainstCatalog(models: ConfiguredAgentModel[], catalog: ProviderCatalog | null): ModelPreflightResult;
74
147
  /**
75
- * End-to-end preflight: collect configured models, fetch the catalog, and
76
- * resolve. Fail-open on every catalog error.
148
+ * Issue #2680: render the operator warning for a preflight result. Reports
149
+ * ONLY actionable classes (`unresolved`, `missing-selection`) — ok/unknown
150
+ * entries and host-controlled primaries stay silent, so disabled, optional,
151
+ * and merely-uninvoked roles never produce consumer-default noise. Returns
152
+ * null when there is nothing to report.
153
+ */
154
+ export declare function formatModelPreflightWarning(result: ModelPreflightResult): string | null;
155
+ /**
156
+ * End-to-end preflight: collect the FINAL effective selection for every
157
+ * enabled role (issue #2680), fetch the catalog, and resolve. Fail-open on
158
+ * every catalog error.
77
159
  */
78
- export declare function runModelPreflight(config: PluginConfig | undefined, client: OpencodeClient | null): Promise<ModelPreflightResult>;
160
+ export declare function runModelPreflight(config: PluginConfig | undefined, client: OpencodeClient | null, opts?: EnabledAgentPreflightOptions): Promise<ModelPreflightResult>;
79
161
  /**
80
162
  * Resolves a single model id against the live catalog for gate preflights.
81
163
  * Returns 'unknown' whenever the catalog cannot be fetched — gates must only
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Owner-named restart reconciliation (issue #2668).
3
+ *
4
+ * At the rehydrate boundary, ephemeral execution authority expires by design
5
+ * (`delegationActive` and friends are reset in
6
+ * `TRANSIENT_SESSION_FIELDS`) — but the expiry must not be silent. When a
7
+ * serialized session arrives with in-flight authority, the boundary records a
8
+ * bounded, owner-named reconciliation outcome so an operator (and the
9
+ * resumed session) can tell "interrupted mid-execution, outcome UNKNOWN"
10
+ * from "was idle": absence must never read as success.
11
+ *
12
+ * Two surfaces, written together inside the rehydrate populate loop (after
13
+ * the transient reset, before the caller proceeds to the projection write):
14
+ *
15
+ * 1. The durable artifact `.swarm/session/restart-reconciliation.json` — a
16
+ * bounded list (cap {@link MAX_RESTART_RECONCILIATION_ENTRIES}, FIFO
17
+ * overflow) deduped by `(sessionId, taskId)` so a repeated restart
18
+ * refreshes the entry instead of duplicating it.
19
+ * 2. A one-shot advisory string pushed into the rehydrated session's
20
+ * `pendingAdvisoryMessages` (per-restart; the next rehydrate re-derives
21
+ * it).
22
+ *
23
+ * The record deliberately COMPOSES the shipped recovery surfaces instead of
24
+ * duplicating them: guidance points operators at `/swarm recover` (stale
25
+ * coder-settlement WALs) and `/swarm status` (uncertain provider effects),
26
+ * and the #2665 `stale`/`ambiguous`/`live_wedge` classification for
27
+ * coder settlements remains owned by those stores.
28
+ */
29
+ /** Relative to the project root's `.swarm/` directory. */
30
+ export declare const RESTART_RECONCILIATION_FILE = "session/restart-reconciliation.json";
31
+ export declare const MAX_RESTART_RECONCILIATION_ENTRIES = 50;
32
+ export type RestartReconciliationClassification = 'interrupted';
33
+ export interface RestartReconciliationEntry {
34
+ sessionId: string;
35
+ agentName: string;
36
+ /** The task the interrupted execution was driving ('(unknown)' when the snapshot carried none). */
37
+ taskId: string;
38
+ classification: RestartReconciliationClassification;
39
+ observedAt: string;
40
+ guidance: string;
41
+ }
42
+ export interface RestartReconciliationFile {
43
+ version: 1;
44
+ entries: RestartReconciliationEntry[];
45
+ }
46
+ export declare const RESTART_RECONCILIATION_GUIDANCE = "outcome UNKNOWN \u2014 do not assume success; inspect /swarm status for uncertain provider effects; /swarm recover settles stale coder-settlement WALs; a human must resolve external effects (pushed commits, published PRs) manually";
47
+ /**
48
+ * Read the reconciliation artifact fail-closed: absent, corrupt, partial, or
49
+ * oversized files read as an empty list — the boundary then re-records what
50
+ * it observes and the artifact heals on the next append.
51
+ */
52
+ export declare function readRestartReconciliation(directory: string): RestartReconciliationFile;
53
+ /**
54
+ * Append (or refresh, keyed by `(sessionId, taskId)`) a reconciliation entry
55
+ * and persist the bounded list atomically. Fail-open: a write error is
56
+ * logged and swallowed — the reconciliation record must never fail the
57
+ * rehydrate that produces it.
58
+ */
59
+ export declare function recordInterruptedExecution(directory: string, entry: {
60
+ sessionId: string;
61
+ agentName: string;
62
+ taskId: string;
63
+ observedAt?: string;
64
+ guidance?: string;
65
+ }): Promise<RestartReconciliationEntry>;
66
+ /**
67
+ * The one-shot session advisory naming the owner of the interrupted effect.
68
+ * Pushed into `pendingAdvisoryMessages` AFTER the transient-field reset so
69
+ * it survives the rehydrate that produced it.
70
+ */
71
+ export declare function buildInterruptedAdvisoryDedupeKey(entry: Pick<RestartReconciliationEntry, 'sessionId' | 'taskId'>): string;
72
+ export declare function buildInterruptedAdvisoryMessage(entry: Pick<RestartReconciliationEntry, 'sessionId' | 'agentName' | 'taskId'> & {
73
+ guidance?: string;
74
+ }): string;
package/dist/state.d.ts CHANGED
@@ -417,9 +417,12 @@ export interface AgentSessionState {
417
417
  autoProceedNudgeDone?: boolean;
418
418
  /** Session-level QA gate overrides layered on top of the spec-level profile.
419
419
  * Overrides can only enable gates (true); false values are ignored by
420
- * getEffectiveGates. Cleared on session reset. Optional for backwards
421
- * compatibility with pre-existing session state fixtures; consumers
422
- * should read via `session.qaGateSessionOverrides ?? {}`. */
420
+ * getEffectiveGates. Durable runtime policy (#2668): the authoritative copy
421
+ * lives in the project DB (qa_gate_session_override), written durable-first
422
+ * by `/swarm qa-gates override`, restored by rehydrateState, and deleted in
423
+ * lockstep with the session (end/stale-eviction/reset). Optional for
424
+ * backwards compatibility with pre-existing session state fixtures;
425
+ * consumers should read via `session.qaGateSessionOverrides ?? {}`. */
423
426
  qaGateSessionOverrides?: Partial<QaGates>;
424
427
  /** Session-scoped Full Auto flag for autonomous multi-agent oversight */
425
428
  fullAutoMode: boolean;
@@ -3,12 +3,15 @@
3
3
  *
4
4
  * Read-only: uses plan.json identity when available or an explicit exact
5
5
  * swarm/title pair before plan creation, then looks up the profile in the
6
- * per-project DB. Returns the spec-level profile gates, lock state, and profile
7
- * hash. Callers layering session overrides should combine with
8
- * `getEffectiveGates` themselves this tool intentionally returns the
9
- * persisted/locked spec-level view.
6
+ * per-project DB. Returns the spec-level profile gates, lock state, and
7
+ * profile hash, plus when the caller carries a session context (#2668)
8
+ * the session's ratchet-tighter overrides and the EFFECTIVE gates (profile
9
+ * merged with those overrides) so an agent can inspect the effective
10
+ * runtime policy after a restart. The live session's override cache is
11
+ * authoritative when present; the durable `qa_gate_session_override` row is
12
+ * the restart authority consulted when the live session is absent.
10
13
  */
11
- import type { tool } from '@opencode-ai/plugin';
14
+ import type { ToolContext, tool } from '@opencode-ai/plugin';
12
15
  import { type QaGatePlanIdentityArgs } from './qa-gate-plan-identity.js';
13
16
  export type GetQaGateProfileArgs = QaGatePlanIdentityArgs;
14
17
  interface GetQaGateProfileResult {
@@ -25,7 +28,11 @@ interface GetQaGateProfileResult {
25
28
  created_at: string;
26
29
  profile_hash: string;
27
30
  };
31
+ /** Calling session's ratchet-tighter overrides (empty when no session context or none set). */
32
+ session_overrides?: Record<string, boolean>;
33
+ /** Effective gates: profile merged with the calling session's overrides (#2668). */
34
+ effective_gates?: Record<string, boolean>;
28
35
  }
29
- export declare function executeGetQaGateProfile(args: GetQaGateProfileArgs, directory: string): Promise<GetQaGateProfileResult>;
36
+ export declare function executeGetQaGateProfile(args: GetQaGateProfileArgs, directory: string, ctx?: ToolContext): Promise<GetQaGateProfileResult>;
30
37
  export declare const get_qa_gate_profile: ReturnType<typeof tool>;
31
38
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.181.1",
3
+ "version": "7.181.3",
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",