opencode-swarm 7.136.1 → 7.136.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 (112) hide show
  1. package/.opencode/skills/brainstorm/SKILL.md +1 -1
  2. package/.opencode/skills/clarify/SKILL.md +3 -3
  3. package/.opencode/skills/clarify-spec/SKILL.md +2 -2
  4. package/.opencode/skills/consult/SKILL.md +1 -1
  5. package/.opencode/skills/council/SKILL.md +1 -1
  6. package/.opencode/skills/critic-gate/SKILL.md +8 -3
  7. package/.opencode/skills/discover/SKILL.md +1 -1
  8. package/.opencode/skills/execute/SKILL.md +1 -1
  9. package/.opencode/skills/gate-attribution/SKILL.md +1 -1
  10. package/.opencode/skills/issue-ingest/SKILL.md +3 -3
  11. package/.opencode/skills/phase-wrap/SKILL.md +1 -1
  12. package/.opencode/skills/plan/SKILL.md +3 -3
  13. package/.opencode/skills/pre-phase-briefing/SKILL.md +1 -1
  14. package/.opencode/skills/resume/SKILL.md +1 -1
  15. package/.opencode/skills/specify/SKILL.md +1 -1
  16. package/.opencode/skills/swarm-pr-feedback/SKILL.md +41 -1
  17. package/.opencode/skills/swarm-pr-review/SKILL.md +72 -24
  18. package/README.md +18 -0
  19. package/dist/background/workspace-snapshot.d.ts +54 -0
  20. package/dist/cli/{config-doctor-6fphg5xm.js → config-doctor-g71mz50j.js} +2 -2
  21. package/dist/cli/{core-4z1s2ak1.js → core-jpjk2qvt.js} +1 -1
  22. package/dist/cli/{curation-policy-m24jbyag.js → curation-policy-zhyttjpa.js} +6 -6
  23. package/dist/cli/{curator-zrt5sqj2.js → curator-36dxw38r.js} +27 -24
  24. package/dist/cli/{curator-llm-factory-w5arq7tr.js → curator-llm-factory-k2a7cv41.js} +27 -24
  25. package/dist/cli/{evidence-summary-service-w006jnpg.js → evidence-summary-service-nse06dqr.js} +8 -6
  26. package/dist/cli/{gate-evidence-aenyz6vt.js → gate-evidence-kdygjr56.js} +4 -4
  27. package/dist/cli/{guardrail-explain-462jwhbh.js → guardrail-explain-1k337z5d.js} +28 -25
  28. package/dist/cli/{guardrail-log-ax21jd9e.js → guardrail-log-xbc9bvdt.js} +3 -3
  29. package/dist/cli/hashing-mjwn6j4y.js +34 -0
  30. package/dist/cli/{hive-promoter-2vacnbws.js → hive-promoter-daxrn62p.js} +27 -24
  31. package/dist/cli/{index-r9v98d0y.js → index-0vq9gnqd.js} +46 -5
  32. package/dist/cli/{index-jwrydqjp.js → index-176zwqcq.js} +2 -2
  33. package/dist/cli/{index-cggqh2dz.js → index-45y0y3xh.js} +57 -1000
  34. package/dist/cli/{index-gf7hqbmz.js → index-68tjkaqk.js} +5 -1
  35. package/dist/cli/{index-dyy3hvk3.js → index-79pgzj9a.js} +4 -4
  36. package/dist/cli/{index-45t7w06b.js → index-7t3vjw5e.js} +1 -1
  37. package/dist/cli/{index-ey29aap6.js → index-b3jfcptk.js} +1 -1
  38. package/dist/cli/{index-prnjt2jr.js → index-brtg922w.js} +4576 -2316
  39. package/dist/cli/{index-7a2hm51h.js → index-cze4bq1x.js} +41 -0
  40. package/dist/cli/{index-mrtms113.js → index-d2sf9an1.js} +2 -2
  41. package/dist/cli/{index-wxyxf0bd.js → index-d61h3f3h.js} +2 -2
  42. package/dist/cli/{index-rhgctfn3.js → index-g2kpqpvh.js} +29 -26
  43. package/dist/cli/{index-g7g4hqh3.js → index-g5rtcb1n.js} +1 -1
  44. package/dist/cli/{index-1sq47n6t.js → index-gj5jerzz.js} +12 -6
  45. package/dist/cli/{index-c9ddxv4k.js → index-hh34tyv7.js} +1 -1
  46. package/dist/cli/{index-h3bweb88.js → index-kp5h245k.js} +5 -5
  47. package/dist/cli/{index-tcn457d5.js → index-kym0cctr.js} +1 -1
  48. package/dist/cli/{index-kzc2ygea.js → index-mgbpbs1f.js} +3 -3
  49. package/dist/cli/{index-tfa40hwb.js → index-n89fdxwg.js} +19 -2
  50. package/dist/cli/index-ne28wyyc.js +783 -0
  51. package/dist/cli/index-nfm9f10v.js +1100 -0
  52. package/dist/cli/{index-zkddc5ye.js → index-pff46kfv.js} +2 -2
  53. package/dist/cli/{index-f4cmtd89.js → index-rtgqy0yg.js} +2 -2
  54. package/dist/cli/{index-09b9zncg.js → index-tncy55bp.js} +1 -1
  55. package/dist/cli/{index-vg3yx648.js → index-w6j1n5az.js} +1 -1
  56. package/dist/cli/{index-hzkvbycn.js → index-wbdxmf1a.js} +6 -6
  57. package/dist/cli/{index-y6a7gjtj.js → index-wsdnttf4.js} +228 -81
  58. package/dist/cli/index-y8552snf.js +264 -0
  59. package/dist/cli/{index-9gxp450h.js → index-zcrvn579.js} +2 -2
  60. package/dist/cli/{index-p1pqqwgp.js → index-zwh5dewz.js} +1 -1
  61. package/dist/cli/{index-dzyjb33e.js → index-zzhyws9g.js} +1 -1
  62. package/dist/cli/index.js +27 -24
  63. package/dist/cli/{knowledge-escalator-1zt8qy1c.js → knowledge-escalator-h7fspgph.js} +7 -7
  64. package/dist/cli/{knowledge-events-ej3s9tsm.js → knowledge-events-vkf7an5n.js} +5 -5
  65. package/dist/cli/{knowledge-link-mm1w967j.js → knowledge-link-zr40rnwr.js} +4 -4
  66. package/dist/cli/{knowledge-store-ey4cbkp4.js → knowledge-store-s4976v9c.js} +5 -5
  67. package/dist/cli/{knowledge-validator-zwmq7s2c.js → knowledge-validator-64ppqy4y.js} +8 -8
  68. package/dist/cli/{pending-delegations-qajsxct0.js → pending-delegations-0h5b18p7.js} +3 -3
  69. package/dist/cli/{pr-subscriptions-qhr41epq.js → pr-subscriptions-jn0h047q.js} +3 -3
  70. package/dist/cli/runner-deeswadt.js +21 -0
  71. package/dist/cli/{scan-cursor-809hf2n1.js → scan-cursor-xbkae12h.js} +6 -6
  72. package/dist/cli/{schema-mhd7xqwr.js → schema-7jm70cab.js} +5 -1
  73. package/dist/cli/{scope-persistence-h2fpgxww.js → scope-persistence-5xc9ntdh.js} +4 -4
  74. package/dist/cli/{skill-generator-yyc8zd9j.js → skill-generator-8gtq1ajr.js} +9 -9
  75. package/dist/cli/{telemetry-859khp82.js → telemetry-6678gya0.js} +1 -1
  76. package/dist/cli/{workspace-snapshot-jmyamqnv.js → workspace-snapshot-h5rzw37b.js} +5 -1
  77. package/dist/cli/{worktree-collision-ownership-13btcj9g.js → worktree-collision-ownership-wt7cc850.js} +3 -3
  78. package/dist/commands/registry.d.ts +72 -0
  79. package/dist/commands/skill-opt.d.ts +41 -0
  80. package/dist/config/schema.d.ts +57 -0
  81. package/dist/hooks/delegation-gate.d.ts +12 -1
  82. package/dist/hooks/gate-denial-tracker.d.ts +175 -0
  83. package/dist/hooks/guardrails/execution-episode.d.ts +41 -0
  84. package/dist/hooks/guardrails/execution-stall.d.ts +285 -0
  85. package/dist/hooks/guardrails/file-authority.d.ts +11 -2
  86. package/dist/hooks/guardrails/internals-guard.d.ts +117 -0
  87. package/dist/hooks/guardrails/messages-transform.d.ts +85 -0
  88. package/dist/hooks/pr-workflow-gate.d.ts +261 -4
  89. package/dist/hooks/pr-workflow-response-gate.d.ts +27 -9
  90. package/dist/hooks/trajectory-logger.d.ts +76 -0
  91. package/dist/hooks/write-target-resolver.d.ts +19 -0
  92. package/dist/index.js +511 -462
  93. package/dist/memory/schema.d.ts +3 -3
  94. package/dist/prm/index.d.ts +2 -0
  95. package/dist/services/skill-evaluator.d.ts +17 -0
  96. package/dist/services/skill-optimizer/activation.d.ts +58 -0
  97. package/dist/services/skill-optimizer/candidates.d.ts +88 -0
  98. package/dist/services/skill-optimizer/controller.d.ts +146 -0
  99. package/dist/services/skill-optimizer/deterministic-seed.d.ts +29 -0
  100. package/dist/services/skill-optimizer/lifecycle.d.ts +70 -0
  101. package/dist/services/skill-optimizer/promoted-external-staleness.d.ts +98 -0
  102. package/dist/services/skill-optimizer/retirement.d.ts +54 -0
  103. package/dist/services/skill-optimizer/skill-eval-tasks.d.ts +47 -0
  104. package/dist/services/skill-optimizer/smoke.d.ts +46 -0
  105. package/dist/services/skill-optimizer/store.d.ts +118 -0
  106. package/dist/state.d.ts +32 -0
  107. package/dist/telemetry.d.ts +66 -1
  108. package/dist/tools/write-pr-review-trigger-eval.d.ts +2 -1
  109. package/dist/types/events.d.ts +14 -1
  110. package/dist/utils/stable-stringify.d.ts +46 -0
  111. package/evaluation-fixtures/skill-eval/scoring/score-skill-eval.cjs +97 -0
  112. package/package.json +2 -1
@@ -33,21 +33,21 @@ import {
33
33
  sanitizeSlug,
34
34
  selectCandidateEntries,
35
35
  writeEvalStub
36
- } from "./index-1sq47n6t.js";
37
- import"./index-dyy3hvk3.js";
38
- import"./index-jwrydqjp.js";
36
+ } from "./index-gj5jerzz.js";
37
+ import"./index-79pgzj9a.js";
38
+ import"./index-176zwqcq.js";
39
39
  import"./index-rtry5xyf.js";
40
- import"./index-hzkvbycn.js";
41
- import"./index-kzc2ygea.js";
40
+ import"./index-wbdxmf1a.js";
41
+ import"./index-mgbpbs1f.js";
42
42
  import"./index-ae75rja9.js";
43
- import"./index-dzyjb33e.js";
44
- import"./index-ey29aap6.js";
43
+ import"./index-zzhyws9g.js";
44
+ import"./index-b3jfcptk.js";
45
45
  import"./index-bk5tah7q.js";
46
46
  import"./index-4rhhvd1a.js";
47
47
  import"./index-fsrp8wp3.js";
48
- import"./index-09b9zncg.js";
48
+ import"./index-tncy55bp.js";
49
49
  import"./index-7g4c7s5r.js";
50
- import"./index-7a2hm51h.js";
50
+ import"./index-cze4bq1x.js";
51
51
  import"./index-y111zefa.js";
52
52
  import"./index-zgwm4ryv.js";
53
53
  import"./index-a76rekgs.js";
@@ -12,7 +12,7 @@ import {
12
12
  startHeartbeatTracking,
13
13
  stopHeartbeatTracking,
14
14
  telemetry
15
- } from "./index-7a2hm51h.js";
15
+ } from "./index-cze4bq1x.js";
16
16
  import"./index-a76rekgs.js";
17
17
  export {
18
18
  telemetry,
@@ -31,11 +31,13 @@ import {
31
31
  resolvePrReviewDiffStatsAsync,
32
32
  resolvePrWorkflowRevisionDigest,
33
33
  resolvePrWorkflowRevisionDigestAsync,
34
+ resolvePrWorkflowRevisionDigestDetailed,
35
+ resolvePrWorkflowRevisionDigestDetailedAsync,
34
36
  resolveRemoteRefsContainingHead,
35
37
  resolveRemoteRefsContainingHeadAsync,
36
38
  switchPrFeedbackTrackingCandidateAsync,
37
39
  workspaceSnapshotMatches
38
- } from "./index-y6a7gjtj.js";
40
+ } from "./index-wsdnttf4.js";
39
41
  import"./index-y111zefa.js";
40
42
  import"./index-a76rekgs.js";
41
43
  export {
@@ -43,6 +45,8 @@ export {
43
45
  switchPrFeedbackTrackingCandidateAsync,
44
46
  resolveRemoteRefsContainingHeadAsync,
45
47
  resolveRemoteRefsContainingHead,
48
+ resolvePrWorkflowRevisionDigestDetailedAsync,
49
+ resolvePrWorkflowRevisionDigestDetailed,
46
50
  resolvePrWorkflowRevisionDigestAsync,
47
51
  resolvePrWorkflowRevisionDigest,
48
52
  resolvePrReviewDiffStatsAsync,
@@ -8,13 +8,13 @@ import {
8
8
  import {
9
9
  scanDelegationFallbacksForRecovery,
10
10
  scanDelegationsForRecovery
11
- } from "./index-45t7w06b.js";
11
+ } from "./index-7t3vjw5e.js";
12
12
  import"./index-4qzeef9h.js";
13
13
  import"./index-fsrp8wp3.js";
14
- import"./index-09b9zncg.js";
14
+ import"./index-tncy55bp.js";
15
15
  import"./index-7g4c7s5r.js";
16
16
  import"./index-bpmtbmy9.js";
17
- import"./index-7a2hm51h.js";
17
+ import"./index-cze4bq1x.js";
18
18
  import"./index-z6xqpmqg.js";
19
19
  import"./index-zjygnfay.js";
20
20
  import {
@@ -258,6 +258,78 @@ export declare const COMMAND_REGISTRY: {
258
258
  readonly category: "diagnostics";
259
259
  readonly toolPolicy: "agent";
260
260
  };
261
+ readonly 'skill-opt': {
262
+ readonly handler: (ctx: CommandContext) => Promise<string>;
263
+ readonly description: "Governed single-skill optimizer (issue #1822). Proposes, validates, and activates one allowlisted SKILL.md candidate at a time with durable lifecycle, serial control, and manual approval.";
264
+ readonly args: "plan|run|status|diff|approve|reject|rollback|history <slug> [candidateId] [--json] [--confirm] [--expected-content-hash <hash>] [--models <csv>] [--dry-run]";
265
+ readonly details: "Disabled/proposal-only by default. `run` requires skill_opt.enabled=true AND --confirm (consumes a held-out test set). approve/activate/reject/rollback are human-only and require --expected-content-hash to refuse a stale base. Stores append-only lifecycle under .swarm/evolution/skills/<slug>/<candidateId>/.";
266
+ readonly category: "utility";
267
+ readonly toolPolicy: "agent";
268
+ };
269
+ readonly 'skill-opt plan': {
270
+ readonly handler: (ctx: CommandContext) => Promise<string>;
271
+ readonly description: "Propose an optimization round (dry-run; no mutation, no validation)";
272
+ readonly subcommandOf: "skill-opt";
273
+ readonly args: "<slug> [--json] [--models <csv>]";
274
+ readonly category: "utility";
275
+ readonly toolPolicy: "agent";
276
+ };
277
+ readonly 'skill-opt run': {
278
+ readonly handler: (ctx: CommandContext) => Promise<string>;
279
+ readonly description: "Execute the optimization loop (draft→smoke→validate; held-out set is single-use so at most one validation per run). Requires skill_opt.enabled=true and --confirm.";
280
+ readonly subcommandOf: "skill-opt";
281
+ readonly args: "<slug> --confirm [--json] [--models <csv>]";
282
+ readonly category: "utility";
283
+ readonly toolPolicy: "human-only";
284
+ };
285
+ readonly 'skill-opt status': {
286
+ readonly handler: (ctx: CommandContext) => Promise<string>;
287
+ readonly description: "Show the current candidate lifecycle state";
288
+ readonly subcommandOf: "skill-opt";
289
+ readonly args: "<slug> <candidateId> [--json]";
290
+ readonly category: "utility";
291
+ readonly toolPolicy: "agent";
292
+ };
293
+ readonly 'skill-opt diff': {
294
+ readonly handler: (ctx: CommandContext) => Promise<string>;
295
+ readonly description: "Show baseline-vs-candidate diff summary for a candidate";
296
+ readonly subcommandOf: "skill-opt";
297
+ readonly args: "<slug> <candidateId> [--json]";
298
+ readonly category: "utility";
299
+ readonly toolPolicy: "agent";
300
+ };
301
+ readonly 'skill-opt approve': {
302
+ readonly handler: (ctx: CommandContext) => Promise<string>;
303
+ readonly description: "Activate a pending candidate (human-only; requires --expected-content-hash)";
304
+ readonly subcommandOf: "skill-opt";
305
+ readonly args: "<slug> <candidateId> --expected-content-hash <hash> [--json]";
306
+ readonly category: "utility";
307
+ readonly toolPolicy: "human-only";
308
+ };
309
+ readonly 'skill-opt reject': {
310
+ readonly handler: (ctx: CommandContext) => Promise<string>;
311
+ readonly description: "Record a rejection for a candidate (no active-skill mutation)";
312
+ readonly subcommandOf: "skill-opt";
313
+ readonly args: "<slug> <candidateId> [--json]";
314
+ readonly category: "utility";
315
+ readonly toolPolicy: "human-only";
316
+ };
317
+ readonly 'skill-opt rollback': {
318
+ readonly handler: (ctx: CommandContext) => Promise<string>;
319
+ readonly description: "Restore the pre-activation snapshot (appends a rolled_back event)";
320
+ readonly subcommandOf: "skill-opt";
321
+ readonly args: "<slug> <candidateId> [--json]";
322
+ readonly category: "utility";
323
+ readonly toolPolicy: "human-only";
324
+ };
325
+ readonly 'skill-opt history': {
326
+ readonly handler: (ctx: CommandContext) => Promise<string>;
327
+ readonly description: "Show the append-only lifecycle event log for a candidate";
328
+ readonly subcommandOf: "skill-opt";
329
+ readonly args: "<slug> <candidateId> [--json]";
330
+ readonly category: "utility";
331
+ readonly toolPolicy: "agent";
332
+ };
261
333
  readonly review: {
262
334
  readonly handler: typeof handleReviewCommand;
263
335
  readonly description: "Run the independent review model against a selected Git diff";
@@ -0,0 +1,41 @@
1
+ /**
2
+ * `/swarm skill-opt` command group (issue #1822).
3
+ *
4
+ * Subcommands: plan | run | status | diff | approve | reject | rollback | history
5
+ *
6
+ * - JSON output on `--json` (convention: gate-stats.ts).
7
+ * - Disabled/proposal-only default: `run` requires `config.skill_opt.enabled === true`
8
+ * AND an explicit `--confirm`. `plan`/`status`/`diff`/`history` are always
9
+ * available (read-only / proposal-only).
10
+ * - `approve`/`activate`/`reject`/`rollback` are human-gated
11
+ * (`toolPolicy: 'human-only'` on the registry entries) and require an
12
+ * explicit `--expected-content-hash` (D6) — slash commands do NOT go through
13
+ * scope-guard (which is coder-only), so the hash check is the staleness guard.
14
+ */
15
+ import { type SkillOptConfig } from '../config/schema.js';
16
+ import type { EvaluationModelDispatcher } from '../evaluation/model-dispatcher.js';
17
+ /** Resolve the skill_opt config from raw plugin config, fail-open to defaults. */
18
+ export declare function resolveSkillOptConfig(input: unknown): SkillOptConfig;
19
+ export interface SkillOptRuntime {
20
+ dispatcher?: EvaluationModelDispatcher;
21
+ parentSessionId?: string;
22
+ config?: SkillOptConfig;
23
+ }
24
+ /** `/swarm skill-opt plan <slug>` — propose a round (dry-run). */
25
+ export declare function handleSkillOptPlan(directory: string, args: string[], runtime?: SkillOptRuntime): Promise<string>;
26
+ /** `/swarm skill-opt run <slug>` — execute a round (requires enabled + --confirm). */
27
+ export declare function handleSkillOptRun(directory: string, args: string[], runtime?: SkillOptRuntime): Promise<string>;
28
+ /** `/swarm skill-opt status <slug> [candidateId]` — current candidate state. */
29
+ export declare function handleSkillOptStatus(directory: string, args: string[]): Promise<string>;
30
+ /** `/swarm skill-opt diff <slug> <candidateId>` — baseline vs candidate diff. */
31
+ export declare function handleSkillOptDiff(directory: string, args: string[]): Promise<string>;
32
+ /** `/swarm skill-opt approve <slug> <candidateId> --expected-content-hash <hash>` — activate. */
33
+ export declare function handleSkillOptApprove(directory: string, args: string[]): Promise<string>;
34
+ /** `/swarm skill-opt reject <slug> <candidateId>` — record rejection (no mutation). */
35
+ export declare function handleSkillOptReject(directory: string, args: string[]): Promise<string>;
36
+ /** `/swarm skill-opt rollback <slug> <candidateId>` — restore snapshot. */
37
+ export declare function handleSkillOptRollback(directory: string, args: string[]): Promise<string>;
38
+ /** `/swarm skill-opt history <slug> <candidateId>` — replay log. */
39
+ export declare function handleSkillOptHistory(directory: string, args: string[]): Promise<string>;
40
+ /** Compute the current content hash of a skill (for the approve hash arg). */
41
+ export declare function computeSkillContentHash(directory: string, skillSlug: string): string;
@@ -527,6 +527,11 @@ export declare const GuardrailsConfigSchema: z.ZodObject<{
527
527
  block_destructive_commands: z.ZodDefault<z.ZodBoolean>;
528
528
  interpreter_allowed_agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
529
529
  shell_audit_log: z.ZodDefault<z.ZodBoolean>;
530
+ gate_denial_warn_threshold: z.ZodDefault<z.ZodNumber>;
531
+ gate_denial_stop_threshold: z.ZodDefault<z.ZodNumber>;
532
+ execution_stall_warn_calls: z.ZodDefault<z.ZodNumber>;
533
+ execution_stall_stop_calls: z.ZodDefault<z.ZodNumber>;
534
+ execution_stall_episode_minutes: z.ZodDefault<z.ZodNumber>;
530
535
  }, z.core.$strip>;
531
536
  export type GuardrailsConfig = z.infer<typeof GuardrailsConfigSchema>;
532
537
  export declare const WatchdogConfigSchema: z.ZodObject<{
@@ -978,6 +983,36 @@ export declare const SkillsConfigSchema: z.ZodObject<{
978
983
  enabled: z.ZodDefault<z.ZodBoolean>;
979
984
  }, z.core.$strip>;
980
985
  export type SkillsConfig = z.infer<typeof SkillsConfigSchema>;
986
+ /**
987
+ * Governed skill optimizer configuration (issue #1822 — SkillOpt 3/7).
988
+ *
989
+ * Disabled by default. When disabled, `/swarm skill-opt run` refuses to
990
+ * execute a round (it still allows `plan`/`status`/`diff`/`history` in
991
+ * proposal-only mode). Enabling does NOT mutate skills autonomously: every
992
+ * activation requires an explicit human `/swarm skill-opt approve` with a
993
+ * current content hash. This config is consulted only inside command handlers
994
+ * (never on the plugin init path — AGENTS.md invariant #1).
995
+ */
996
+ export declare const SkillOptConfigSchema: z.ZodObject<{
997
+ enabled: z.ZodDefault<z.ZodBoolean>;
998
+ max_rounds: z.ZodDefault<z.ZodNumber>;
999
+ max_candidates_per_round: z.ZodDefault<z.ZodNumber>;
1000
+ max_validations_per_round: z.ZodDefault<z.ZodNumber>;
1001
+ max_round_time_ms: z.ZodDefault<z.ZodNumber>;
1002
+ max_tokens_per_round: z.ZodDefault<z.ZodNumber>;
1003
+ max_rejections: z.ZodDefault<z.ZodNumber>;
1004
+ max_inconclusive_rounds: z.ZodDefault<z.ZodNumber>;
1005
+ max_transient_retries: z.ZodDefault<z.ZodNumber>;
1006
+ convergence_non_improvements: z.ZodDefault<z.ZodNumber>;
1007
+ max_changed_lines: z.ZodDefault<z.ZodNumber>;
1008
+ max_changed_bytes: z.ZodDefault<z.ZodNumber>;
1009
+ max_changed_sections: z.ZodDefault<z.ZodNumber>;
1010
+ deadband: z.ZodDefault<z.ZodNumber>;
1011
+ retirement_min_age_days: z.ZodDefault<z.ZodNumber>;
1012
+ }, z.core.$strict>;
1013
+ export type SkillOptConfig = z.infer<typeof SkillOptConfigSchema>;
1014
+ /** Default governed-skill-optimizer config (fully disabled/safe). */
1015
+ export declare const DEFAULT_SKILL_OPT_CONFIG: SkillOptConfig;
981
1016
  export declare const SpecWriterConfigSchema: z.ZodObject<{
982
1017
  enabled: z.ZodDefault<z.ZodBoolean>;
983
1018
  model: z.ZodDefault<z.ZodNullable<z.ZodString>>;
@@ -1854,6 +1889,11 @@ export declare const PluginConfigSchema: z.ZodObject<{
1854
1889
  block_destructive_commands: z.ZodDefault<z.ZodBoolean>;
1855
1890
  interpreter_allowed_agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
1856
1891
  shell_audit_log: z.ZodDefault<z.ZodBoolean>;
1892
+ gate_denial_warn_threshold: z.ZodDefault<z.ZodNumber>;
1893
+ gate_denial_stop_threshold: z.ZodDefault<z.ZodNumber>;
1894
+ execution_stall_warn_calls: z.ZodDefault<z.ZodNumber>;
1895
+ execution_stall_stop_calls: z.ZodDefault<z.ZodNumber>;
1896
+ execution_stall_episode_minutes: z.ZodDefault<z.ZodNumber>;
1857
1897
  }, z.core.$strip>>;
1858
1898
  watchdog: z.ZodOptional<z.ZodObject<{
1859
1899
  scope_guard: z.ZodDefault<z.ZodBoolean>;
@@ -2691,6 +2731,23 @@ export declare const PluginConfigSchema: z.ZodObject<{
2691
2731
  skills: z.ZodOptional<z.ZodObject<{
2692
2732
  enabled: z.ZodDefault<z.ZodBoolean>;
2693
2733
  }, z.core.$strip>>;
2734
+ skill_opt: z.ZodOptional<z.ZodObject<{
2735
+ enabled: z.ZodDefault<z.ZodBoolean>;
2736
+ max_rounds: z.ZodDefault<z.ZodNumber>;
2737
+ max_candidates_per_round: z.ZodDefault<z.ZodNumber>;
2738
+ max_validations_per_round: z.ZodDefault<z.ZodNumber>;
2739
+ max_round_time_ms: z.ZodDefault<z.ZodNumber>;
2740
+ max_tokens_per_round: z.ZodDefault<z.ZodNumber>;
2741
+ max_rejections: z.ZodDefault<z.ZodNumber>;
2742
+ max_inconclusive_rounds: z.ZodDefault<z.ZodNumber>;
2743
+ max_transient_retries: z.ZodDefault<z.ZodNumber>;
2744
+ convergence_non_improvements: z.ZodDefault<z.ZodNumber>;
2745
+ max_changed_lines: z.ZodDefault<z.ZodNumber>;
2746
+ max_changed_bytes: z.ZodDefault<z.ZodNumber>;
2747
+ max_changed_sections: z.ZodDefault<z.ZodNumber>;
2748
+ deadband: z.ZodDefault<z.ZodNumber>;
2749
+ retirement_min_age_days: z.ZodDefault<z.ZodNumber>;
2750
+ }, z.core.$strict>>;
2694
2751
  }, z.core.$strip>;
2695
2752
  export type PluginConfig = z.infer<typeof PluginConfigSchema>;
2696
2753
  export type { AgentName, PipelineAgentName, QAAgentName, } from './constants';
@@ -221,6 +221,14 @@ export interface CoverageMissDiagnostic {
221
221
  divergenceOffset: number;
222
222
  corruptionHint?: string;
223
223
  }
224
+ /**
225
+ * Issue #2063 (A2): per-body cap, in characters, on the raw requirement body
226
+ * embedded verbatim in the ACCEPTANCE_FIELD_COVERAGE_MISMATCH error. Keeps the
227
+ * thrown message bounded even for an unusually long FR/SC body; when a body
228
+ * exceeds this cap the message states the cap and points at `.swarm/spec.md`
229
+ * for the remainder rather than growing the error without limit.
230
+ */
231
+ export declare const ACCEPTANCE_EXPECTED_BODY_CAP = 2000;
224
232
  export declare function describeCoverageMiss(params: {
225
233
  rawExpectedBody: string;
226
234
  rawAcceptanceText: string;
@@ -240,7 +248,9 @@ export declare function describeCoverageMiss(params: {
240
248
  *
241
249
  * @returns `{ covered: true }` when every id is present-and-covered or skipped;
242
250
  * `{ covered: false, missingId }` naming the FIRST id whose body is not a
243
- * substring of the ACCEPTANCE text.
251
+ * substring of the ACCEPTANCE text, plus `expectedBody` — the RAW, UNTRIMMED
252
+ * requirement body for `missingId` (issue #2063 A2) — so the throw site can
253
+ * embed paste-ready remediation text instead of just pointing at a location.
244
254
  */
245
255
  export declare function checkAcceptanceCoversFrRefs(params: {
246
256
  acceptanceText: string;
@@ -250,6 +260,7 @@ export declare function checkAcceptanceCoversFrRefs(params: {
250
260
  covered: boolean;
251
261
  missingId?: string;
252
262
  diagnostic?: CoverageMissDiagnostic;
263
+ expectedBody?: string;
253
264
  };
254
265
  interface MessageInfo {
255
266
  role: string;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * GATE DENIAL TRACKER (issue #2063, workstream B1)
3
+ *
4
+ * The architect session had no containment for a *denial-retry* loop: every
5
+ * fail-closed `tool.execute.before` hook throws, the host reports the throw as
6
+ * a tool rejection, and the model happily re-issues the identical dispatch —
7
+ * forever. Nothing counted the repeats, so nothing ever escalated.
8
+ *
9
+ * This module owns that counter. `noteGateDenial` is called from the single
10
+ * catch site wrapping the fail-closed chain in `src/index.ts`. It:
11
+ * 1. classifies the denial by the leading code token of the error message,
12
+ * 2. increments a per-(sessionID, toolName, discriminator, code) streak,
13
+ * 3. APPENDS (never rewrites) escalating guidance to the error message so the
14
+ * model reads it in the tool-rejection text, and
15
+ * 4. at the hard rung, pushes an advisory + emits telemetry.
16
+ *
17
+ * The DISCRIMINATOR (reviewer round-4 REQUIRED 2) is the canonicalized
18
+ * `subagent_type` of a `Task` call, and the empty string for every other tool.
19
+ * Without it the reset was too wide: `resetGateDenialStreaks` drops a whole
20
+ * (session, tool) prefix on any successful completion of that tool, so ONE
21
+ * successful `Task` → `explorer` erased a 4-deep `ACCEPTANCE_FIELD_REQUIRED`
22
+ * streak on `Task` → `coder`. Under the interleaving the loop actually
23
+ * exhibits — deny coder, delegate an explorer to investigate, deny coder again —
24
+ * the STOP rung was unreachable. Sub-scoping both the count and the reset by
25
+ * dispatch target makes a success clear only what plausibly succeeded.
26
+ *
27
+ * Invariants this module must not break:
28
+ * - The caller ALWAYS rethrows. Decoration is append-only, so the leading
29
+ * code token of the original message stays byte-identical and every
30
+ * existing consumer that substring-matches a gate code keeps working.
31
+ * - Abort/cancel errors are excluded entirely (a user hitting escape three
32
+ * times is not a loop) — they neither count nor reset an existing streak.
33
+ * - Nothing here may throw. A tracker failure must never convert a
34
+ * fail-closed denial into a different error.
35
+ *
36
+ * NOT to be confused with `swarmState.gateDenialCounts` (src/state.ts:768),
37
+ * which counts knowledge-application gate denials keyed by *critical-directive
38
+ * identity*. Different trigger, different key, different lifecycle.
39
+ *
40
+ * Eviction is modelled on `BoundedPendingScopeMap`
41
+ * (src/hooks/delegation-gate.ts:144-179) — TTL sweep plus a hard size cap — but
42
+ * deliberately re-implemented here rather than imported, because
43
+ * `delegation-gate.ts` is itself a member of the chain this module wraps and an
44
+ * import would create a cycle.
45
+ */
46
+ /** Default streak length at which the "do not retry" guidance is appended. */
47
+ export declare const DEFAULT_GATE_DENIAL_WARN_THRESHOLD = 3;
48
+ /** Default streak length at which the hard STOP directive is appended. */
49
+ export declare const DEFAULT_GATE_DENIAL_STOP_THRESHOLD = 5;
50
+ /** Classification used when the message carries no recognisable code token. */
51
+ export declare const UNCLASSIFIED_GATE_DENIAL_CODE = "UNCLASSIFIED";
52
+ /**
53
+ * Sub-scope of a denial streak inside one (session, tool) pair.
54
+ *
55
+ * For a `Task` call this is the canonicalized dispatch target, so `mega_coder`
56
+ * and `coder` share one streak (matching `canonicalDispatchRole` in
57
+ * `guardrails/execution-stall.ts`). Every other tool — and a `Task` whose
58
+ * `subagent_type` is absent or not a string — yields `''`, which preserves the
59
+ * pre-discriminator behavior for them exactly.
60
+ *
61
+ * Deliberately reads `subagent_type` ONLY. `parseDelegationArgs`
62
+ * (`hooks/skill-propagation-gate.ts:400`) additionally falls back to the first
63
+ * non-empty line of the delegation PROMPT, which would turn arbitrary
64
+ * model-authored prose into a map key — an unbounded-cardinality hazard
65
+ * (invariant 8) and a way for the model to shatter its own streak into
66
+ * singletons by varying one line of text.
67
+ *
68
+ * Never throws.
69
+ */
70
+ export declare function gateDenialDiscriminator(tool: string, args: unknown): string;
71
+ /**
72
+ * Derive the denial classification from an error message: the leading token up
73
+ * to the first `:`, trimmed.
74
+ *
75
+ * `'ACCEPTANCE_FIELD_COVERAGE_MISMATCH: task 1.1 ...'` -> `'ACCEPTANCE_FIELD_COVERAGE_MISMATCH'`
76
+ * `'FULL_AUTO_DENY [path_out_of_root]: ...'` -> `'FULL_AUTO_DENY [path_out_of_root]'`
77
+ * `'Blocked by skill propagation gate'` -> `'UNCLASSIFIED'` (no colon)
78
+ *
79
+ * The whole point of the classification is that repeats of the SAME cause share
80
+ * a value, so anything that cannot be a stable code (empty, absent, or longer
81
+ * than {@link MAX_CODE_LENGTH}) collapses to UNCLASSIFIED rather than producing
82
+ * a per-occurrence key.
83
+ */
84
+ export declare function deriveGateDenialCode(message: string): string;
85
+ /**
86
+ * True when the thrown value is a user/host abort rather than a policy denial.
87
+ * Aborts must not count toward a denial streak AND must not reset one: a user
88
+ * cancelling mid-loop does not mean the loop was resolved.
89
+ */
90
+ export declare function isAbortLikeError(err: unknown): boolean;
91
+ /** The append-only warn rung. Exported so tests assert the exact wording. */
92
+ export declare function gateDenialWarnText(count: number, code: string): string;
93
+ /**
94
+ * The append-only hard rung, modelled on `nonTransientHardStopMessage`
95
+ * (src/hooks/guardrails/nontransient-circuit.ts:336-354).
96
+ */
97
+ export declare function gateDenialStopText(count: number, code: string, tool: string): string;
98
+ export interface GateDenialOptions {
99
+ /**
100
+ * `guardrails.enabled`. The thresholds live in the `guardrails` config block
101
+ * and the loader force-sets `enabled: false` when a user turns guardrails
102
+ * off, so that flag has to mean "no guardrails behavior" here too — otherwise
103
+ * the config surface lies. When false the denial is neither counted nor
104
+ * decorated, and no advisory or telemetry is produced. Defaults to true.
105
+ */
106
+ enabled?: boolean;
107
+ /** `guardrails.gate_denial_warn_threshold` */
108
+ warnThreshold?: number;
109
+ /** `guardrails.gate_denial_stop_threshold` */
110
+ stopThreshold?: number;
111
+ }
112
+ export interface GateDenialOutcome {
113
+ /** Classification used for the streak key. */
114
+ code: string;
115
+ /** Streak length AFTER this denial. `0` when the denial was not counted. */
116
+ count: number;
117
+ /** Whether the warn rung fired on this denial. */
118
+ warned: boolean;
119
+ /** Whether the hard rung fired on this denial. */
120
+ stopped: boolean;
121
+ /** Whether the error message was mutated. */
122
+ decorated: boolean;
123
+ }
124
+ /**
125
+ * Count one fail-closed denial and, past the configured rungs, APPEND guidance
126
+ * to `err.message` in place.
127
+ *
128
+ * The caller is responsible for rethrowing the SAME object — mutating in place
129
+ * preserves `name`, `stack`, and any custom fields a gate attached, which
130
+ * constructing a replacement Error would destroy.
131
+ *
132
+ * `args` are the resolved `tool.execute.before` args of the DENIED call. They
133
+ * derive the discriminator, so a `Task` → `coder` streak and a `Task` →
134
+ * `explorer` streak are counted (and reset) separately. Omitting them is safe
135
+ * and reproduces the pre-discriminator single-bucket behavior.
136
+ *
137
+ * Never throws.
138
+ */
139
+ export declare function noteGateDenial(sessionID: string, tool: string, err: unknown, options?: GateDenialOptions, args?: unknown): GateDenialOutcome;
140
+ /**
141
+ * Clear every denial streak for one (sessionID, toolName, discriminator) triple.
142
+ *
143
+ * Called when the fail-closed chain completes successfully for that tool: the
144
+ * dispatch that was being denied now passes, so the streak is over.
145
+ *
146
+ * Two levels of scoping, both load-bearing:
147
+ * - by TOOL, so a successful `read` does not erase an in-progress `Task`
148
+ * denial loop; and
149
+ * - by DISCRIMINATOR, so a successful `Task` → `explorer` does not erase an
150
+ * in-progress `Task` → `coder` denial loop. `args` are the resolved
151
+ * `tool.execute.before` args of the call that just SUCCEEDED, which is the
152
+ * only thing that can be said to have been resolved. Omitting them clears
153
+ * the `''` bucket only.
154
+ */
155
+ export declare function resetGateDenialStreaks(sessionID: string, tool: string, args?: unknown): void;
156
+ /**
157
+ * Drop all tracked streaks.
158
+ *
159
+ * A test/reset helper only — there is no `/swarm close` (or any other
160
+ * production) caller. Streak lifetime in production is governed by
161
+ * {@link GATE_DENIAL_TTL_MS}, the {@link MAX_TRACKED_DENIAL_STREAKS} LRU cap,
162
+ * and `resetGateDenialStreaks`.
163
+ */
164
+ export declare function clearGateDenialStreaks(): void;
165
+ export declare const _test_exports: {
166
+ readonly MAX_TRACKED_DENIAL_STREAKS: 500;
167
+ readonly GATE_DENIAL_TTL_MS: number;
168
+ readonly MAX_CODE_LENGTH: 64;
169
+ readonly MAX_DISCRIMINATOR_LENGTH: 64;
170
+ readonly streakCount: () => number;
171
+ /** Read a streak length without mutating it. */
172
+ readonly peekStreak: (sessionID: string, tool: string, code: string, discriminator?: string) => number;
173
+ /** Force a streak's TTL into the past so eviction can be tested. */
174
+ readonly expireStreak: (sessionID: string, tool: string, code: string, discriminator?: string) => void;
175
+ };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Execution-episode seam (issue #2063 B3/B5).
3
+ *
4
+ * An "execution episode" is the window in which a session has actually
5
+ * attempted execution work — a `Task` dispatch to a mutating/verifying role, or
6
+ * an `update_task_status(..., in_progress)` that succeeded. Containment levers
7
+ * that would produce false positives during ordinary conversation, planning, or
8
+ * read-only review are gated on the episode being ARMED.
9
+ *
10
+ * This module is deliberately narrow: it owns nothing but the read/write of the
11
+ * `executionEpisodeArmed` session field, so that
12
+ *
13
+ * - the CONSUMER side (B3's medium-band runaway counting in
14
+ * `messages-transform.ts`) has a single, testable predicate, and
15
+ * - the PRODUCER side (B5's arming/lapse policy in `execution-stall.ts`)
16
+ * has a single, testable mutator to call.
17
+ *
18
+ * Keeping the field access behind these two functions is what prevents the
19
+ * arming policy from being duplicated at each call site as it grows.
20
+ *
21
+ * Defaults are fail-open toward "not armed": an unknown session, a session with
22
+ * no state, and a session whose field was never initialised all read `false`,
23
+ * so a lever gated on this seam stays silent rather than firing on a session it
24
+ * knows nothing about.
25
+ */
26
+ /**
27
+ * Whether an execution episode is currently armed for `sessionID`.
28
+ *
29
+ * Returns `false` for unknown sessions.
30
+ */
31
+ export declare function isExecutionEpisodeArmed(sessionID: string): boolean;
32
+ /**
33
+ * Arm or disarm the execution episode for `sessionID`.
34
+ *
35
+ * No-ops for an unknown session: arming state is meaningless without a session
36
+ * to hang it on, and creating one here would let a containment lever
37
+ * materialise session state as a side effect.
38
+ *
39
+ * @returns `true` when the field was written, `false` when the session is unknown.
40
+ */
41
+ export declare function setExecutionEpisodeArmed(sessionID: string, armed: boolean): boolean;