opencode-swarm 7.144.1 → 7.144.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 (103) hide show
  1. package/.opencode/skills/swarm-pr-review/references/lane-output-recoverability.md +96 -0
  2. package/dist/agents/architect.d.ts +1 -0
  3. package/dist/agents/coder.d.ts +1 -0
  4. package/dist/agents/critic.d.ts +1 -0
  5. package/dist/agents/explorer.d.ts +1 -0
  6. package/dist/agents/reviewer.d.ts +1 -0
  7. package/dist/background/pending-delegations.d.ts +20 -0
  8. package/dist/cli/{ast-diff-ypptrcan.js → ast-diff-wbjcazfx.js} +5 -3
  9. package/dist/cli/{config-doctor-5hj26phk.js → config-doctor-pkr9w2nv.js} +3 -3
  10. package/dist/cli/{core-aqtegsag.js → core-7pymdx0b.js} +3 -3
  11. package/dist/cli/{curation-policy-pkb2eeb1.js → curation-policy-y1pqpztb.js} +5 -5
  12. package/dist/cli/curator-drift-2jbjv03a.js +1 -1
  13. package/dist/cli/{curator-llm-factory-1rg62kzr.js → curator-llm-factory-7are9wjh.js} +34 -34
  14. package/dist/cli/{curator-vcrcht4h.js → curator-wbzcjd0q.js} +34 -34
  15. package/dist/cli/{evidence-summary-service-fsh6jfhm.js → evidence-summary-service-rcgrdm52.js} +14 -14
  16. package/dist/cli/{explorer-18353rhn.js → explorer-zvk5kbcb.js} +3 -1
  17. package/dist/cli/{gate-evidence-9w2eehnj.js → gate-evidence-jmzctnhg.js} +7 -7
  18. package/dist/cli/{guardrail-explain-6mxn4bj8.js → guardrail-explain-brgw4sve.js} +35 -35
  19. package/dist/cli/{guardrail-log-81c5b018.js → guardrail-log-k1s9xf3h.js} +4 -4
  20. package/dist/cli/{hive-promoter-rxkykddk.js → hive-promoter-vcj5h0e3.js} +36 -36
  21. package/dist/cli/{index-n11zkp9g.js → index-06cn7haa.js} +2 -2
  22. package/dist/cli/{index-tq5s07ta.js → index-0fgwdtvp.js} +1 -1
  23. package/dist/cli/{index-j3byg0a9.js → index-1brb14xc.js} +6 -2
  24. package/dist/cli/{index-e9w1tfmm.js → index-247wf23m.js} +6974 -1859
  25. package/dist/cli/{index-mz1zwz96.js → index-2en8s72h.js} +2 -2
  26. package/dist/cli/{index-gdd4jt6b.js → index-584grrwn.js} +1 -1
  27. package/dist/cli/{index-2ht8c7ew.js → index-6s2q144w.js} +2 -7
  28. package/dist/cli/{index-xmzmeknp.js → index-70ha9e09.js} +2 -1
  29. package/dist/cli/{index-j2eb0x44.js → index-7554tecy.js} +2 -2
  30. package/dist/cli/{index-5n69863d.js → index-7ryh498h.js} +1 -173
  31. package/dist/cli/{index-k92cagmt.js → index-8214dd7j.js} +2 -2
  32. package/dist/cli/{index-rdb3nr6d.js → index-8hkjk37c.js} +1 -1
  33. package/dist/cli/{index-315tnsve.js → index-b227mpnk.js} +4 -4
  34. package/dist/cli/index-ddpkwdcb.js +176 -0
  35. package/dist/cli/{index-w7x7qxz2.js → index-dpfphnmy.js} +1 -1
  36. package/dist/cli/{index-8gs34d2q.js → index-ep14xc5z.js} +4 -4
  37. package/dist/cli/{index-g0vr2a4m.js → index-eya357bk.js} +9 -9
  38. package/dist/cli/{index-hc25qawy.js → index-ghzvxj3g.js} +37 -37
  39. package/dist/cli/{index-bgdr1ww3.js → index-j6mfyy0a.js} +3 -3
  40. package/dist/cli/{index-4fda8kkw.js → index-krxcxkdq.js} +13 -3
  41. package/dist/cli/{index-f79hf8dr.js → index-p7hbphaq.js} +4 -4
  42. package/dist/cli/{index-4s5htbgq.js → index-p863y7xx.js} +1 -1
  43. package/dist/cli/{index-dq26vbfp.js → index-pag8nsb1.js} +10 -10
  44. package/dist/cli/{index-rr2fjfsd.js → index-pr8vhj6j.js} +12 -12
  45. package/dist/cli/{index-8jrn4rwp.js → index-qj22wyw2.js} +3 -3
  46. package/dist/cli/{index-1faydhv8.js → index-satc0vs6.js} +203 -100
  47. package/dist/cli/{index-2pmaxbvj.js → index-wck434f8.js} +2 -2
  48. package/dist/cli/{index-jqsfsqbx.js → index-whezv7vc.js} +4 -0
  49. package/dist/cli/{index-4905hd2m.js → index-wx8zdh4a.js} +6 -1
  50. package/dist/cli/{index-sqp6349j.js → index-x68tw7v3.js} +5 -5
  51. package/dist/cli/{index-c3zag2cw.js → index-xwpmxpb7.js} +9 -9
  52. package/dist/cli/{index-erfnw81g.js → index-zswmbm15.js} +1 -1
  53. package/dist/cli/{index-4q9vfav6.js → index-zwrqe065.js} +1 -1
  54. package/dist/cli/index.js +37 -51
  55. package/dist/cli/{knowledge-escalator-hg4jzbrx.js → knowledge-escalator-x6jrcrwj.js} +13 -13
  56. package/dist/cli/{knowledge-events-wgy7gbct.js → knowledge-events-aw22r72m.js} +11 -11
  57. package/dist/cli/{knowledge-link-fn5j3r4q.js → knowledge-link-bntppte4.js} +4 -4
  58. package/dist/cli/{knowledge-store-nd0f0e86.js → knowledge-store-19xg1rw1.js} +6 -6
  59. package/dist/cli/{knowledge-validator-0ar015bf.js → knowledge-validator-8wh82z0a.js} +8 -8
  60. package/dist/cli/{pending-delegations-thy8g30k.js → pending-delegations-1y303j3k.js} +3 -3
  61. package/dist/cli/{pr-subscriptions-jyav582d.js → pr-subscriptions-wvd041rp.js} +3 -3
  62. package/dist/cli/{runner-pde6b5sx.js → runner-jn55k8xb.js} +5 -5
  63. package/dist/cli/{runtime-te94z1zb.js → runtime-x10qb3z1.js} +3 -2
  64. package/dist/cli/{scan-cursor-pqp3bsqv.js → scan-cursor-429r5db9.js} +7 -7
  65. package/dist/cli/{schema-4ew49zrv.js → schema-jdwjcby0.js} +2 -2
  66. package/dist/cli/{scope-persistence-geth2hb1.js → scope-persistence-s0s1bz15.js} +7 -7
  67. package/dist/cli/{skill-generator-8azm6p2y.js → skill-generator-ffbj0ye5.js} +16 -16
  68. package/dist/cli/tree-sitter-431q1bka.js +26 -0
  69. package/dist/cli/{worktree-collision-ownership-j6d33p49.js → worktree-collision-ownership-z5ftwjz3.js} +6 -6
  70. package/dist/cli/{worktree-isolation-cqxgdk2p.js → worktree-isolation-y2p93438.js} +34 -34
  71. package/dist/commands/registry.d.ts +1 -1
  72. package/dist/config/constants.d.ts +1 -1
  73. package/dist/config/schema.d.ts +8 -0
  74. package/dist/hooks/pr-workflow-gate.d.ts +132 -3
  75. package/dist/hooks/review-receipt-scope.d.ts +42 -15
  76. package/dist/hooks/reviewer-scope-file-fingerprint.d.ts +58 -6
  77. package/dist/hooks/reviewer-scope-lifecycle.d.ts +9 -1
  78. package/dist/hooks/reviewer-scope-mergeback.d.ts +23 -0
  79. package/dist/index.d.ts +3 -0
  80. package/dist/index.js +858 -816
  81. package/dist/memory/config.d.ts +6 -0
  82. package/dist/memory/consolidation.d.ts +2 -0
  83. package/dist/memory/gateway.d.ts +12 -1
  84. package/dist/memory/index.d.ts +6 -4
  85. package/dist/memory/jsonl-migration.d.ts +28 -3
  86. package/dist/memory/local-jsonl-provider.d.ts +16 -1
  87. package/dist/memory/maintenance.d.ts +5 -0
  88. package/dist/memory/memory-family-migration.d.ts +12 -0
  89. package/dist/memory/outcome-events.d.ts +24 -0
  90. package/dist/memory/provider.d.ts +7 -1
  91. package/dist/memory/reflection-injection.d.ts +1 -0
  92. package/dist/memory/reflection-service.d.ts +68 -0
  93. package/dist/memory/reflection.d.ts +41 -0
  94. package/dist/memory/schema.d.ts +13 -1
  95. package/dist/memory/sqlite-provider.d.ts +18 -1
  96. package/dist/memory/types.d.ts +19 -1
  97. package/dist/state.d.ts +107 -2
  98. package/dist/tools/index.d.ts +1 -0
  99. package/dist/tools/manifest.d.ts +1 -0
  100. package/dist/tools/swarm-memory-outcome.d.ts +11 -0
  101. package/dist/tools/tool-metadata.d.ts +4 -0
  102. package/package.json +1 -1
  103. package/dist/cli/{index-k31en0ja.js → index-wd24zn2e.js} +3 -3
@@ -20,60 +20,60 @@ import {
20
20
  sanitizeWorktreeTaskId,
21
21
  standardWorktreeByCallID,
22
22
  standardWorktreeSerializationSessions
23
- } from "./index-e9w1tfmm.js";
24
- import"./index-j2eb0x44.js";
25
- import"./index-mz1zwz96.js";
26
- import"./index-1faydhv8.js";
23
+ } from "./index-247wf23m.js";
24
+ import"./index-7554tecy.js";
25
+ import"./index-2en8s72h.js";
26
+ import"./index-satc0vs6.js";
27
27
  import"./index-134d35c1.js";
28
28
  import"./index-b6e016tv.js";
29
- import"./index-4q9vfav6.js";
30
- import"./index-k92cagmt.js";
29
+ import"./index-zwrqe065.js";
30
+ import"./index-8214dd7j.js";
31
31
  import"./index-y8552snf.js";
32
- import"./index-2pmaxbvj.js";
33
- import"./index-xmzmeknp.js";
32
+ import"./index-wck434f8.js";
33
+ import"./index-70ha9e09.js";
34
34
  import"./index-c8s9a3zh.js";
35
35
  import"./index-9ss2m4rs.js";
36
- import"./index-sqp6349j.js";
37
36
  import"./index-74hpm6sh.js";
38
- import"./index-dq26vbfp.js";
39
- import"./index-g0vr2a4m.js";
40
- import"./index-f79hf8dr.js";
41
- import"./index-bgdr1ww3.js";
42
- import"./index-556ja8an.js";
43
- import"./index-gdd4jt6b.js";
37
+ import"./index-584grrwn.js";
38
+ import"./index-pag8nsb1.js";
39
+ import"./index-eya357bk.js";
40
+ import"./index-b227mpnk.js";
41
+ import"./index-0fgwdtvp.js";
42
+ import"./index-rtry5xyf.js";
44
43
  import"./index-zsrtt42s.js";
44
+ import"./index-krxcxkdq.js";
45
45
  import"./index-k3cxyk66.js";
46
- import"./index-4fda8kkw.js";
47
- import"./index-2ht8c7ew.js";
46
+ import"./index-x68tw7v3.js";
47
+ import"./index-6s2q144w.js";
48
+ import"./index-p7hbphaq.js";
49
+ import"./index-j6mfyy0a.js";
50
+ import"./index-556ja8an.js";
51
+ import"./index-7sy3aa9m.js";
52
+ import"./index-wx8zdh4a.js";
48
53
  import"./index-8fwhhayc.js";
49
54
  import"./index-q1exe2b3.js";
50
- import"./index-7sy3aa9m.js";
51
- import"./index-4905hd2m.js";
52
- import"./index-rr2fjfsd.js";
55
+ import"./index-xwpmxpb7.js";
56
+ import"./index-pr8vhj6j.js";
53
57
  import {
54
58
  resolveWorktreeIsolationConfig
55
- } from "./index-8jrn4rwp.js";
59
+ } from "./index-qj22wyw2.js";
56
60
  import"./index-gprxjmm7.js";
57
61
  import"./index-n832052r.js";
58
- import"./index-rdb3nr6d.js";
59
- import"./index-j3byg0a9.js";
60
62
  import"./index-rn0eq85g.js";
61
- import"./index-4rhhvd1a.js";
62
- import"./index-jqsfsqbx.js";
63
63
  import"./index-z6xqpmqg.js";
64
+ import"./index-8hkjk37c.js";
65
+ import"./index-1brb14xc.js";
66
+ import"./index-4rhhvd1a.js";
67
+ import"./index-whezv7vc.js";
64
68
  import"./index-zjygnfay.js";
65
- import"./index-315tnsve.js";
66
- import"./index-tq5s07ta.js";
67
- import"./index-rtry5xyf.js";
68
- import"./index-c3zag2cw.js";
69
69
  import"./index-ae75rja9.js";
70
- import"./index-w7x7qxz2.js";
71
- import"./index-erfnw81g.js";
72
- import"./index-bk5tah7q.js";
70
+ import"./index-dpfphnmy.js";
71
+ import"./index-zswmbm15.js";
73
72
  import"./index-ed5ykebp.js";
74
- import"./index-k31en0ja.js";
75
- import"./index-7g4c7s5r.js";
73
+ import"./index-bk5tah7q.js";
74
+ import"./index-wd24zn2e.js";
76
75
  import"./index-s2rcz51a.js";
76
+ import"./index-7g4c7s5r.js";
77
77
  import"./index-bpmtbmy9.js";
78
78
  import"./index-4nt52yr9.js";
79
79
  import"./index-zgwm4ryv.js";
@@ -619,7 +619,7 @@ export declare const COMMAND_REGISTRY: {
619
619
  readonly handler: (ctx: CommandContext) => Promise<string>;
620
620
  readonly description: "Clear a stuck PR_REVIEW/PR_FEEDBACK mechanical gate and stop the auto-resume loop [mode] [reason]";
621
621
  readonly args: "[PR_REVIEW|PR_FEEDBACK] [reason...]";
622
- readonly details: "Human-only escape hatch for an unrecoverable PR_REVIEW or PR_FEEDBACK mechanical gate. When the architect cannot reach complete_pr_workflow — for example a compound `git fetch && git checkout` was rejected as read-only shell syntax, the PR head cannot be fetched, or the working tree is on the wrong branch — running this clears the durable gate state for the current session and stops the auto-resume loop without depending on the trapped model. The agent itself cannot run this command; it must call the abort_pr_workflow tool (or ask you to run this command). Both paths funnel into the same fail-closed abortPrWorkflow hook, which refuses while the workflow is armed for publication or while PR workflow lanes are still in flight. When checkout preparation preserved a stash, the result instructs the caller to run prepare_pr_workflow_checkout operation=restore after the clear. An audit event is appended to .swarm/events.jsonl.";
622
+ readonly details: "Human-only escape hatch for an unrecoverable PR_REVIEW or PR_FEEDBACK mechanical gate. When the architect cannot reach complete_pr_workflow — for example a compound `git fetch && git checkout` was rejected as read-only shell syntax, the PR head cannot be fetched, or the working tree is on the wrong branch — running this clears the durable gate state for the current session and stops the auto-resume loop without depending on the trapped model. The agent itself cannot run this command; it must call the abort_pr_workflow tool (or ask you to run this command). Both paths funnel into the same fail-closed abortPrWorkflow hook, which refuses while the workflow is armed for publication or while PR workflow lanes are still in flight. This human-only force path has exactly one exception to that second refusal (issue #2251): when the ONLY lanes still blocking are ones past the 30-minute staleness horizon that the liveness probe reports as still running — a lane nothing will ever settle on a schedule — it clears the gate anyway, names exactly which lanes it overrode, and finalizes their delegation records so the session can start a new PR workflow. Those sessions are NOT stopped and their output is NOT collected. A lane with a fresh updatedAt still blocks even under force, and any delegation record that still keeps the session blocked is named in the warning. When checkout preparation preserved a stash, the result instructs the caller to run prepare_pr_workflow_checkout operation=restore after the clear. An audit event is appended to .swarm/events.jsonl.";
623
623
  readonly category: "utility";
624
624
  readonly toolPolicy: "restricted";
625
625
  };
@@ -5,7 +5,7 @@ export type { AgentName, PipelineAgentName, QAAgentName } from './agent-names';
5
5
  export { ALL_AGENT_NAMES, ALL_SUBAGENT_NAMES, ORCHESTRATOR_NAME, PIPELINE_AGENTS, QA_AGENTS, } from './agent-names';
6
6
  export declare const OPENCODE_NATIVE_AGENTS: Set<"plan" | "summary" | "compaction" | "title" | "build" | "general" | "explore">;
7
7
  export declare const CLAUDE_CODE_NATIVE_COMMANDS: ReadonlySet<string>;
8
- export declare const MEMORY_TOOL_NAMES: readonly ["swarm_memory_recall", "swarm_memory_propose"];
8
+ export declare const MEMORY_TOOL_NAMES: readonly ["swarm_memory_recall", "swarm_memory_propose", "swarm_memory_outcome"];
9
9
  export declare const MEMORY_AGENT_TOOL_MAP: Partial<Record<AgentName, ToolName[]>>;
10
10
  export declare const EXTERNAL_SKILL_TOOL_NAMES: readonly ["external_skill_discover", "external_skill_list", "external_skill_inspect", "external_skill_promote", "external_skill_reject", "external_skill_delete", "external_skill_revoke"];
11
11
  export declare const EXTERNAL_SKILL_AGENT_TOOL_MAP: Partial<Record<AgentName, ToolName[]>>;
@@ -816,6 +816,10 @@ export declare const MemoryConfigSchema: z.ZodObject<{
816
816
  tokenBudget: z.ZodDefault<z.ZodNumber>;
817
817
  }, z.core.$strip>>;
818
818
  }, z.core.$strip>>;
819
+ reflection: z.ZodDefault<z.ZodObject<{
820
+ enabled: z.ZodDefault<z.ZodBoolean>;
821
+ halfLifeDays: z.ZodDefault<z.ZodNumber>;
822
+ }, z.core.$strip>>;
819
823
  learning: z.ZodDefault<z.ZodObject<{
820
824
  learningRate: z.ZodDefault<z.ZodNumber>;
821
825
  propagationFactor: z.ZodDefault<z.ZodNumber>;
@@ -2199,6 +2203,10 @@ export declare const PluginConfigSchema: z.ZodObject<{
2199
2203
  tokenBudget: z.ZodDefault<z.ZodNumber>;
2200
2204
  }, z.core.$strip>>;
2201
2205
  }, z.core.$strip>>;
2206
+ reflection: z.ZodDefault<z.ZodObject<{
2207
+ enabled: z.ZodDefault<z.ZodBoolean>;
2208
+ halfLifeDays: z.ZodDefault<z.ZodNumber>;
2209
+ }, z.core.$strip>>;
2202
2210
  learning: z.ZodDefault<z.ZodObject<{
2203
2211
  learningRate: z.ZodDefault<z.ZodNumber>;
2204
2212
  propagationFactor: z.ZodDefault<z.ZodNumber>;
@@ -2,7 +2,7 @@ import * as fsp from 'node:fs/promises';
2
2
  import { z } from 'zod';
3
3
  import { type CandidateSeverity, type RowFormatFamily } from '../background/candidate-contract.js';
4
4
  import { type LaneOutputArtifact } from '../background/lane-output-store.js';
5
- import { type BackgroundDelegationRecord, type BackgroundDelegationResult, findByBatchId } from '../background/pending-delegations.js';
5
+ import { type BackgroundDelegationRecord, type BackgroundDelegationResult, findByBatchId, sweepStaleDelegations } from '../background/pending-delegations.js';
6
6
  import { type PrReviewInlineTriggerRow } from '../background/pr-review-trigger-contract.js';
7
7
  import { type RevisionDigestResult, resolveCommitCountSince, resolveCommitCountSinceAsync, resolveCurrentGitHead, resolveCurrentGitHeadAsync, resolveCurrentUpstreamPushTarget, resolveCurrentUpstreamPushTargetAsync, resolveCurrentUpstreamRemoteRef, resolveExactRemoteBranchHead, resolveExactRemoteBranchHeadAsync, resolveIsExactSingleChildCommit, resolveIsExactSingleChildCommitAsync, resolveIsWorkingTreeClean, resolveIsWorkingTreeCleanAsync, resolvePrReviewDiffStats, resolvePrReviewDiffStatsAsync, resolvePrWorkflowRevisionDigest, resolvePrWorkflowRevisionDigestDetailed, resolveRemoteRefsContainingHead, resolveRemoteRefsContainingHeadAsync } from '../background/workspace-snapshot.js';
8
8
  import { classifyPrWorkflowGitState, type PrWorkflowGitState } from '../git/pr-workflow-state.js';
@@ -408,13 +408,99 @@ export declare function withInactivePrWorkflowCheckoutRestoreLock<T>(directory:
408
408
  export declare function clearPrWorkflowGateState(directory: string, sessionID: string, expectedRevision?: number, options?: {
409
409
  allowSalvagedRead?: boolean;
410
410
  }): Promise<void>;
411
+ /**
412
+ * The single session operation the liveness probe needs.
413
+ *
414
+ * Deliberately a narrow LOCAL structural type rather than a type-only import of
415
+ * `SessionOps` from `src/tools/dispatch-lanes.ts`: that module's
416
+ * `isLaneReadyForCollection` is the fail-CLOSED counterpart of this probe, and
417
+ * keeping the two textually independent removes any invitation to "share" the
418
+ * predicate. A type-only import would be erased either way; this is about
419
+ * preventing the wrong reuse, not about module graph cost.
420
+ */
421
+ interface PrWorkflowLaneLivenessSessionOps {
422
+ status?: (args: {
423
+ query?: {
424
+ directory?: string;
425
+ };
426
+ }) => Promise<{
427
+ data?: Record<string, {
428
+ type?: string;
429
+ }> | null;
430
+ error?: unknown;
431
+ }>;
432
+ }
433
+ /** Why a probe produced no evidence of life. Absent means the probe ran. */
434
+ export type PrWorkflowLaneProbeDegradedReason = 'probe-unavailable' | 'probe-error' | 'probe-timeout' | 'probe-no-data';
435
+ /**
436
+ * Probe which of `records` have a session the host affirmatively reports as
437
+ * still running (issue #2251).
438
+ *
439
+ * **FAIL-OPEN, by design.** For its ERROR and NO-DATA cases this is the exact
440
+ * inverse of `isLaneReadyForCollection` (`src/tools/dispatch-lanes.ts`), which
441
+ * returns `false` on a truthy `error`, on missing `data`, and on a throw —
442
+ * because a lane it cannot verify must not be collected. The two agree, rather
443
+ * than invert, on ONE case: a host that exposes no `status` function at all.
444
+ * There the collector returns `true` (`dispatch-lanes.ts`, the `typeof
445
+ * session.status !== 'function'` guard) and this probe reports
446
+ * `probe-unavailable`, so both fall through to proceeding — an unprobeable host
447
+ * must not wedge either side.
448
+ *
449
+ * Here the default outcome must be "settle": an unavailable, erroring,
450
+ * timing-out or empty probe returns the empty set, so age alone decides and the
451
+ * reachability floor this whole subsystem exists to preserve is untouched. Only
452
+ * a probe that RAN and affirmatively named a live session may contradict
453
+ * staleness.
454
+ *
455
+ * The returned set is built only from a fully-read response — a mid-iteration
456
+ * throw yields the empty set rather than a partial spare list, so a malformed
457
+ * response can never spare an arbitrary subset of lanes.
458
+ *
459
+ * Keys are `subagentSessionId`s. A record without one is unprobeable and is
460
+ * simply never added (it settles), which is the fail-open direction.
461
+ */
462
+ declare function probeAlivePrWorkflowLaneSessions(directory: string, records: BackgroundDelegationRecord[]): Promise<{
463
+ alive: Set<string>;
464
+ degradedReason?: PrWorkflowLaneProbeDegradedReason;
465
+ }>;
411
466
  export interface PrWorkflowStaleLaneSettlement {
412
- /** Lanes still genuinely in flight (fresh `updatedAt`). These still block. */
467
+ /**
468
+ * Every lane that still blocks: lanes with a fresh `updatedAt` PLUS lanes
469
+ * past the horizon whose session the liveness probe reported still running.
470
+ */
413
471
  openLaneIds: string[];
414
472
  openLanes: number;
473
+ /**
474
+ * Open lanes that block on FRESHNESS alone (age below the horizon).
475
+ *
476
+ * Separated from `openLanes` because the human-only `force` abort override
477
+ * (issue #2251 S3) must distinguish "blocked only by probe retention", which
478
+ * an operator may override, from "blocked by a lane that is genuinely young",
479
+ * which stays fail-closed. Deriving it by subtraction or by comparing lane
480
+ * LABELS would be wrong: `prWorkflowLaneLabel` falls back to `'unknown'`, so
481
+ * labels are not unique.
482
+ */
483
+ freshOpenLanes: number;
415
484
  /** Lanes stale past the horizon, treated as settled with disclosure. */
416
485
  presumedStaleLaneIds: string[];
417
- /** Operator-facing disclosure; `undefined` when nothing was presumed stale. */
486
+ /**
487
+ * Lanes past the horizon that the probe spared. Present only when non-empty.
488
+ */
489
+ probedAliveLaneIds?: string[];
490
+ /**
491
+ * The SAME records as {@link probedAliveLaneIds}, in the same order, keyed by
492
+ * `correlationId` instead of by display label.
493
+ *
494
+ * Both are needed and neither substitutes for the other: `prWorkflowLaneLabel`
495
+ * falls back to `'unknown'` and is not unique, so it cannot address a durable
496
+ * record — while `correlationId` is the ledger's primary key and is what the
497
+ * human-only `force` override must name to finalize exactly these records and
498
+ * nothing else. Present only when non-empty.
499
+ */
500
+ probeRetainedCorrelationIds?: string[];
501
+ /** Set when the probe could not produce evidence; absent when it ran. */
502
+ probeDegradedReason?: PrWorkflowLaneProbeDegradedReason;
503
+ /** Operator-facing disclosure; `undefined` when nothing was settled or retained. */
418
504
  disclosure?: string;
419
505
  }
420
506
  /**
@@ -439,6 +525,13 @@ export interface PrWorkflowStaleLaneSettlement {
439
525
  *
440
526
  * A lane with a RECENT `updatedAt` still blocks: a re-verification that CAN run
441
527
  * and reports "still progressing" contradicts settlement and fails closed.
528
+ *
529
+ * Issue #2251 adds the second, stronger contradiction: a lane PAST the horizon
530
+ * whose session the host affirmatively reports as `busy`/`retry` is genuinely
531
+ * running (nothing heartbeats `updatedAt`, so age alone cannot see this) and is
532
+ * retained instead of discarded. The probe is fail-open — see
533
+ * {@link probeAlivePrWorkflowLaneSessions} — so an unavailable, erroring or
534
+ * empty probe leaves the age-only behaviour exactly as it was.
442
535
  */
443
536
  export declare function settlePresumedStalePrWorkflowLanes(directory: string, sessionID: string): Promise<PrWorkflowStaleLaneSettlement>;
444
537
  /**
@@ -480,6 +573,16 @@ export declare function abortPrWorkflow(directory: string, sessionID: string, op
480
573
  openLanes: number;
481
574
  presumedStaleLanes?: string[];
482
575
  presumedStaleDisclosure?: string;
576
+ /** Lanes past the horizon the liveness probe reported as still running. */
577
+ probeRetainedLanes?: string[];
578
+ /**
579
+ * Present ONLY when a human `force` abort cleared the gate while those
580
+ * probe-retained lanes were the sole remaining reason it was blocked
581
+ * (issue #2251 S3).
582
+ */
583
+ probeRetentionOverrideDisclosure?: string;
584
+ /** Why the liveness probe produced no evidence, when it could not run. */
585
+ probeDegradedReason?: PrWorkflowLaneProbeDegradedReason;
483
586
  stateSalvaged?: boolean;
484
587
  stateSalvageDisclosure?: string;
485
588
  casEscapeDisclosure?: string;
@@ -652,6 +755,32 @@ export declare const _test_exports: {
652
755
  removeCheckoutLock: typeof removeCheckoutLockFile;
653
756
  checkoutMutationActionTimeoutMs: number;
654
757
  classifyPrWorkflowGitStateAsync: typeof classifyPrWorkflowGitState;
758
+ /**
759
+ * The durability sweep, reached through a seam so a test can prove that a
760
+ * failing sweep cannot un-settle the decision `settlePresumedStalePrWorkflow-
761
+ * Lanes` already made in memory. It cannot throw today (it catches internally
762
+ * and returns 0) — the seam is what makes that guarantee testable rather than
763
+ * merely asserted.
764
+ */
765
+ sweepStaleDelegationsAsync: typeof sweepStaleDelegations;
766
+ /** The fail-open liveness probe (issue #2251). */
767
+ probeLaneLivenessAsync: typeof probeAlivePrWorkflowLaneSessions;
768
+ /**
769
+ * Session handle for the liveness probe.
770
+ *
771
+ * A seam rather than a direct `swarmState.opencodeClient` read at the call
772
+ * site because 20+ test files mutate that field; without this, this suite and
773
+ * the stale-lane suite would be order-dependent in bun's shared process.
774
+ * Mirrors `_internals.getSessionOps` in `src/tools/dispatch-lanes.ts`.
775
+ */
776
+ getSessionOps: () => PrWorkflowLaneLivenessSessionOps | null;
777
+ /**
778
+ * Probe deadline, read through the seam at CALL time. `freezeClock` patches
779
+ * `Date.now()`, not `setTimeout`, so a test that must reach the timeout branch
780
+ * has to shorten the real deadline. Same precedent as
781
+ * `checkoutMutationActionTimeoutMs` above.
782
+ */
783
+ laneLivenessProbeTimeoutMs: number;
655
784
  resolveCurrentGitHead: typeof resolveCurrentGitHead;
656
785
  resolveCurrentGitHeadAsync: typeof resolveCurrentGitHeadAsync;
657
786
  resolveCurrentUpstreamPushTarget: typeof resolveCurrentUpstreamPushTarget;
@@ -1,25 +1,38 @@
1
1
  /**
2
- * Canonical scope construction for Stage-B reviewer receipts.
2
+ * Canonical scope construction for Stage-B reviewer receipts (v2, issue #2100).
3
3
  *
4
4
  * The architect-authored reviewer prompt is not authoritative scope: it can
5
5
  * omit files that the coder actually changed. Guardrails records every
6
- * write-tool target in `modifiedFilesThisCoderTask`, so receipts fingerprint a
7
- * bounded manifest of repository HEAD, those paths, and their current byte
8
- * content instead. A base change therefore invalidates the receipt even when
9
- * the working-file bytes happen to remain identical.
6
+ * write-tool target on the coder generation, so receipts fingerprint an exact
7
+ * manifest of repository HEAD, the canonical workspace identity, those paths,
8
+ * and their complete current bytes (streamed SHA-256 no byte caps). A base
9
+ * change therefore invalidates the receipt even when the working-file bytes
10
+ * happen to remain identical.
10
11
  *
11
- * Any missing session state, unsafe path, symlink/reparse point, concurrent
12
- * mutation, or size overflow fails toward no receipt. A durable receipt must
13
- * never claim a scope the harness could not reconstruct exactly.
12
+ * The inline payload budget (`maxBytes`) selects a per-file DELIVERY MODE only
13
+ * it never decides which files enter the manifest and never changes the
14
+ * manifest digest. Unsafe paths, symlink/reparse points, non-regular files,
15
+ * and concurrent mutation fail toward typed results; transient classes
16
+ * (HEAD timeout/race, capture races, capture deadline) are retryable.
14
17
  */
15
18
  import * as child_process from 'node:child_process';
16
19
  import * as fs from 'node:fs';
17
- export declare const REVIEWER_TASK_SCOPE_DESCRIPTION = "reviewer-task-files-v1";
20
+ export declare const REVIEWER_TASK_SCOPE_DESCRIPTION = "reviewer-task-files-v2";
21
+ export declare const REVIEWER_TASK_SCOPE_HEADER = "opencode-swarm-reviewer-task-scope-v2";
22
+ export interface ReviewerTaskScopeDeliveryEntry {
23
+ path: string;
24
+ mode: 'inline' | 'manual';
25
+ }
18
26
  export interface ReviewerTaskScope {
19
27
  content: string;
20
- description: typeof REVIEWER_TASK_SCOPE_DESCRIPTION;
28
+ /** Manifest version discriminator — `reviewer-task-files-v2` for live builds; legacy v1 values only reach us from persisted data. */
29
+ description: string;
21
30
  files: string[];
22
31
  headSha: string;
32
+ /** Canonical workspace identity the manifest bytes were captured from. */
33
+ workspaceIdentity: string;
34
+ /** Per-file delivery mode — advisory prompt metadata, never part of the digest. */
35
+ delivery: ReviewerTaskScopeDeliveryEntry[];
23
36
  taskId?: string;
24
37
  coderCallID?: string;
25
38
  generation?: number;
@@ -41,6 +54,17 @@ export interface ResolveReviewerTaskScopeOptions {
41
54
  /** Injectable clock used only for deterministic expiry tests. */
42
55
  now?: number;
43
56
  }
57
+ export type ReviewerScopeBuildFailureCode = 'no_files' | 'too_many_files' | 'invalid_budget' | 'invalid_path' | 'workspace_unresolvable' | 'head_timeout' | 'head_changed' | 'outside_workspace' | 'symlink_or_reparse' | 'non_regular' | 'unreadable' | 'file_changed_during_capture' | 'capture_deadline';
58
+ export type ReviewerScopeBuildResult = {
59
+ ok: true;
60
+ scope: ReviewerTaskScope;
61
+ } | {
62
+ ok: false;
63
+ code: ReviewerScopeBuildFailureCode;
64
+ retryable: boolean;
65
+ file?: string;
66
+ detail?: string;
67
+ };
44
68
  /**
45
69
  * Resolve the exact known plan task carried by reviewer/coder Task arguments.
46
70
  *
@@ -58,14 +82,17 @@ export declare function resolveReviewerScopeTaskId(directory: string, args: unkn
58
82
  export declare const _internals: {
59
83
  spawn: typeof child_process.spawn;
60
84
  realpath: typeof fs.promises.realpath;
61
- lstatBigInt: (path: fs.PathLike) => Promise<fs.BigIntStats>;
62
- open: typeof fs.promises.open;
63
- fileHandleStatBigInt: (handle: fs.promises.FileHandle) => Promise<fs.BigIntStats>;
64
85
  };
65
86
  /**
66
- * Build the exact scope fingerprint input for a guardrails-observed file list.
87
+ * Build the exact reviewer-task manifest for a guardrails-observed file list.
88
+ *
89
+ * Every regular file contributes its COMPLETE byte count and SHA-256 (streamed
90
+ * capture); the payload budget only selects per-file delivery mode. Transient
91
+ * failures return typed retryable results; the caller owns bounded retry.
67
92
  */
68
- export declare function buildReviewerTaskScope(directory: string, modifiedFiles: readonly string[], maxBytes?: number, provenance?: ReviewerTaskScopeProvenance): Promise<ReviewerTaskScope | null>;
93
+ export declare function buildReviewerTaskScope(directory: string, modifiedFiles: readonly string[], maxBytes?: number, provenance?: ReviewerTaskScopeProvenance, options?: {
94
+ deadlineAt?: number;
95
+ }): Promise<ReviewerScopeBuildResult>;
69
96
  /**
70
97
  * Resolve scope only from the architect session's guardrails-owned task state.
71
98
  * Receipt collection consumes the completed coder handoff exactly once; direct
@@ -1,6 +1,33 @@
1
1
  import * as fs from 'node:fs';
2
- export declare const MAX_REVIEWER_SCOPE_FINGERPRINT_BYTES = 1048576;
3
- export declare const MAX_REVIEWER_SCOPE_FINGERPRINT_AGGREGATE_BYTES = 4194304;
2
+ /**
3
+ * Exact reviewer-scope capture (v2, issue #2100).
4
+ *
5
+ * Every regular file is fingerprinted over its COMPLETE bytes with one fixed
6
+ * chunk buffer — there is no per-file or aggregate byte cap. File identity is
7
+ * never denied by size: resource pressure surfaces as the typed retryable
8
+ * `capture_deadline` (or a per-file race code), and only a successful capture
9
+ * can ever produce a digest. A partial, sampled, or capped digest cannot be
10
+ * represented by this module by construction.
11
+ */
12
+ export type ReviewerScopeCaptureFailureCode = 'invalid_request' | 'outside_workspace' | 'symlink_or_reparse' | 'non_regular' | 'unreadable' | 'workspace_mismatch' | 'file_changed_during_capture' | 'capture_deadline';
13
+ /** `binding_mismatch` and invalid generation identity are lifecycle-level typed errors (reviewer-scope-lifecycle), not per-file capture codes. */
14
+ export declare const RETRYABLE_CAPTURE_FAILURE_CODES: ReadonlySet<ReviewerScopeCaptureFailureCode>;
15
+ export type ReviewerScopeCaptureResult = {
16
+ kind: 'captured_file';
17
+ file: string;
18
+ size: number;
19
+ hash: string;
20
+ } | {
21
+ kind: 'captured_deleted';
22
+ file: string;
23
+ } | {
24
+ kind: 'capture_failed';
25
+ file: string;
26
+ code: ReviewerScopeCaptureFailureCode;
27
+ retryable: boolean;
28
+ detail?: string;
29
+ };
30
+ /** Stored success record persisted on generations and ownership tombstones. */
4
31
  export type ReviewerScopeFileFingerprint = {
5
32
  file: string;
6
33
  kind: 'file';
@@ -10,12 +37,37 @@ export type ReviewerScopeFileFingerprint = {
10
37
  file: string;
11
38
  kind: 'deleted';
12
39
  };
40
+ /** Fixed streaming chunk — the only buffer a capture ever allocates. */
41
+ export declare const REVIEWER_SCOPE_CAPTURE_CHUNK_BYTES = 262144;
42
+ /** Bounded attempts for typed retryable capture failures at every call site. */
43
+ export declare const REVIEWER_SCOPE_CAPTURE_ATTEMPTS = 3;
44
+ /** Total deadline (ms) for one capture batch (guardrails batch, freshness check, manifest build). */
45
+ export declare const REVIEWER_SCOPE_CAPTURE_BATCH_DEADLINE_MS = 10000;
13
46
  export declare const _internals: {
14
- readFileSync: typeof fs.readFileSync;
47
+ open: typeof fs.openSync;
48
+ fstat: (fd: number, options: {
49
+ bigint: true;
50
+ }) => fs.BigIntStats;
51
+ read: typeof fs.readSync;
52
+ close: typeof fs.closeSync;
53
+ lstat: typeof fs.lstatSync;
54
+ stat: (path: fs.PathLike, options: {
55
+ bigint: true;
56
+ }) => fs.BigIntStats;
57
+ realpathSync: typeof fs.realpathSync;
58
+ now: () => number;
15
59
  };
16
60
  /**
17
- * Capture the exact post-write file state with a bounded read. Oversized,
18
- * non-regular, path-racing, and unreadable targets fail closed to null.
61
+ * Capture the exact current file state with a complete, bounded-memory SHA-256.
62
+ *
63
+ * Failure surfaces are typed: traversal/root escape, symlinks/reparse points,
64
+ * non-regular files, and unreadable targets fail closed as non-retryable codes;
65
+ * open/stat races and capture deadlines are retryable. Deletions under a
66
+ * contained (possibly removed) directory are `captured_deleted`.
19
67
  */
20
- export declare function captureReviewerScopeFileFingerprint(directory: string, file: string, maxBytes?: number): ReviewerScopeFileFingerprint | null;
68
+ export declare function captureReviewerScopeFileFingerprint(directory: string, file: string, options?: {
69
+ deadlineAt?: number;
70
+ }): ReviewerScopeCaptureResult;
71
+ /** Convert a successful capture into the stored fingerprint record shape. */
72
+ export declare function reviewerScopeCaptureToFingerprint(result: ReviewerScopeCaptureResult): ReviewerScopeFileFingerprint | null;
21
73
  export declare function reviewerScopeFileFingerprintsEqual(left: ReviewerScopeFileFingerprint, right: ReviewerScopeFileFingerprint): boolean;
@@ -1,4 +1,12 @@
1
- export type ReviewerScopeLifecycleTransition = 'coder_started' | 'coder_ready' | 'reviewer_claimed';
1
+ import * as child_process from 'node:child_process';
2
+ export type ReviewerScopeLifecycleTransition = 'coder_started' | 'coder_ready' | 'coder_no_change' | 'reviewer_claimed';
3
+ /** Test-only dependency-injection seam for the no-change status probe. */
4
+ export declare const _internals: {
5
+ spawn: typeof child_process.spawn;
6
+ backoffMs?: number;
7
+ /** Test-only deadline override for the bounded retry loop (ms). */
8
+ retryDeadlineMs?: number;
9
+ };
2
10
  /** Run only after the complete blocking before-chain approved this Task call. */
3
11
  export declare function beginApprovedReviewerScopeLifecycle(input: {
4
12
  directory: string;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Merge-back verification for lane-captured reviewer generations (issue #2100
3
+ * contract D).
4
+ *
5
+ * A lane generation is only reviewable from the primary checkout after every
6
+ * manifest file (including deletions) matches the lane manifest in the primary
7
+ * root. Verification re-captures each file from the PRIMARY directory with the
8
+ * shared exact-byte capture and compares against the generation's stored
9
+ * fingerprints. A conflict, deferred merge, or changed byte produces a typed
10
+ * retained state (`mergeback_mismatch`) — never a generic reviewer-stale
11
+ * relabel and never a discard.
12
+ */
13
+ /**
14
+ * Best-effort synchronous verification. Never throws: an unexpected error is
15
+ * recorded as a typed mismatch so the generation stays actionable.
16
+ */
17
+ export declare function verifyReviewerScopeGenerationMergeBack(input: {
18
+ parentSessionID: string;
19
+ taskId: string;
20
+ coderCallID: string;
21
+ primaryDirectory: string;
22
+ now?: number;
23
+ }): void;
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { Plugin } from '@opencode-ai/plugin';
2
2
  import { loadPluginConfigWithMetaAsync } from './config';
3
3
  import { resolveAutoReviewConfig } from './config/schema';
4
4
  import { createRepoGraphBuilderHook } from './hooks';
5
+ import type { MemoryConfig as RuntimeMemoryConfig } from './memory/config.js';
5
6
  import { loadSnapshot } from './session/snapshot-reader.js';
6
7
  import { ensureSwarmGitExcluded } from './utils/gitignore-warning';
7
8
  /**
@@ -24,6 +25,7 @@ type PostResolutionTask = () => void | Promise<void>;
24
25
  * expensive filesystem work while `server()` remains unresolved (issue #704).
25
26
  */
26
27
  declare function schedulePostResolutionTasks(tasks: readonly PostResolutionTask[]): void;
28
+ type RegenerateMemoryReflectionForInit = (directory: string, config: Partial<RuntimeMemoryConfig>) => Promise<unknown>;
27
29
  export declare function overrideIndexInternalsForTest(overrides: {
28
30
  createRepoGraphBuilderHook?: typeof createRepoGraphBuilderHook;
29
31
  schedulePostResolutionTasks?: typeof schedulePostResolutionTasks;
@@ -31,6 +33,7 @@ export declare function overrideIndexInternalsForTest(overrides: {
31
33
  loadSnapshot?: typeof loadSnapshot;
32
34
  ensureSwarmGitExcluded?: typeof ensureSwarmGitExcluded;
33
35
  resolveAutoReviewConfig?: typeof resolveAutoReviewConfig;
36
+ regenerateMemoryReflection?: RegenerateMemoryReflectionForInit;
34
37
  }): () => void;
35
38
  export declare function schedulePostResolutionTasksForTest(tasks: readonly PostResolutionTask[]): void;
36
39
  /**