opencode-swarm 7.119.0 → 7.120.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/cli/{config-doctor-p73yfdz0.js → config-doctor-404c8mf8.js} +3 -3
  2. package/dist/cli/config-fingerprint-v22xh5a2.js +10 -0
  3. package/dist/cli/{core-gf1t2jmn.js → core-xnwasfpc.js} +1 -1
  4. package/dist/cli/curation-policy-re2kaysb.js +25 -0
  5. package/dist/cli/{curator-5xfkejsq.js → curator-852s9p0s.js} +30 -23
  6. package/dist/cli/{curator-drift-nn71xfz6.js → curator-drift-6b2fjsgr.js} +3 -2
  7. package/dist/cli/curator-llm-factory-5gnvggd7.js +50 -0
  8. package/dist/cli/{evidence-summary-service-cy3k37je.js → evidence-summary-service-jtx5q3r6.js} +9 -7
  9. package/dist/cli/{gate-evidence-j2m4cd85.js → gate-evidence-tbpezk28.js} +5 -5
  10. package/dist/cli/guardrail-explain-wadbhh06.js +51 -0
  11. package/dist/cli/{guardrail-log-qjqgg7pz.js → guardrail-log-dhpp0kmn.js} +4 -4
  12. package/dist/cli/{hive-promoter-03m8pwg6.js → hive-promoter-7fn55bgw.js} +30 -23
  13. package/dist/cli/index-0e1v02hq.js +343 -0
  14. package/dist/cli/{index-jbdgpqpj.js → index-0k4r8111.js} +28 -10
  15. package/dist/cli/index-0mbmfmt0.js +349 -0
  16. package/dist/cli/{index-7pspktns.js → index-1smvzr78.js} +2 -2
  17. package/dist/cli/{index-5bcrp3bc.js → index-22e127jw.js} +3 -3
  18. package/dist/cli/{index-pjhfe7c3.js → index-3366gs26.js} +43 -7
  19. package/dist/cli/{index-wpgv6hht.js → index-3jvqw5hy.js} +13 -26
  20. package/dist/cli/{index-bfwt3abw.js → index-4rhcth7a.js} +1 -1
  21. package/dist/cli/{index-n6fa2hhk.js → index-51gg9947.js} +1 -1
  22. package/dist/cli/index-69ms9ny1.js +261 -0
  23. package/dist/cli/{index-42cx6g2h.js → index-76kvm9z5.js} +1 -1
  24. package/dist/cli/{index-r2ysfdet.js → index-78b192v7.js} +7 -7
  25. package/dist/cli/{index-vwvkpg0d.js → index-816rkp4y.js} +3 -92
  26. package/dist/cli/index-89s4mhmw.js +6 -0
  27. package/dist/cli/index-9xpgytj9.js +386 -0
  28. package/dist/cli/index-ae75rja9.js +26 -0
  29. package/dist/cli/{index-mr3pnc4h.js → index-bndd885n.js} +1926 -2005
  30. package/dist/cli/index-fndvr7fr.js +135 -0
  31. package/dist/cli/index-gnp9x6se.js +93 -0
  32. package/dist/cli/{index-0pfqcjp6.js → index-h07wn84h.js} +1 -1
  33. package/dist/cli/{index-p45sshb5.js → index-j5xv8zbp.js} +17 -352
  34. package/dist/cli/{index-g2wrs7jy.js → index-kyvg2cmp.js} +1 -1
  35. package/dist/cli/{index-5hze8ztj.js → index-nhgw7de8.js} +7 -5
  36. package/dist/cli/{index-abjjn4yh.js → index-qg11yf8q.js} +4 -4
  37. package/dist/cli/index-rtry5xyf.js +27 -0
  38. package/dist/cli/{index-qx5z58a7.js → index-rywkdacj.js} +93 -11
  39. package/dist/cli/{index-pdk1n8yh.js → index-tftv9xh9.js} +44 -2
  40. package/dist/cli/{index-11q1t6g0.js → index-vqcgmy4y.js} +1 -1
  41. package/dist/cli/{index-05ee8pws.js → index-w64n2y0f.js} +32 -25
  42. package/dist/cli/{index-q4kgsvdd.js → index-wtn8wf7h.js} +1 -1
  43. package/dist/cli/{index-bcg89pt8.js → index-zvae4m8g.js} +1 -1
  44. package/dist/cli/index.js +30 -23
  45. package/dist/cli/{knowledge-escalator-gt1q632m.js → knowledge-escalator-0ejqs8c9.js} +9 -8
  46. package/dist/cli/{knowledge-events-0bjdtdsb.js → knowledge-events-wx9e37ba.js} +6 -5
  47. package/dist/cli/knowledge-link-sy85fw3w.js +37 -0
  48. package/dist/cli/{knowledge-store-fm2300xf.js → knowledge-store-rnajx33a.js} +17 -6
  49. package/dist/cli/{knowledge-validator-91kx3eqv.js → knowledge-validator-1f4mkzmc.js} +11 -7
  50. package/dist/cli/{pending-delegations-9cn17983.js → pending-delegations-0z3znsec.js} +5 -5
  51. package/dist/cli/{pr-subscriptions-8a6jwek2.js → pr-subscriptions-x653151h.js} +4 -4
  52. package/dist/cli/scan-cursor-qapm0sxj.js +25 -0
  53. package/dist/cli/{schema-7c484wjz.js → schema-vwxpsk6j.js} +2 -2
  54. package/dist/cli/{scope-persistence-fm0gzhnk.js → scope-persistence-mfr36yc9.js} +2 -2
  55. package/dist/cli/{skill-generator-3f8dqjj2.js → skill-generator-tkv7s12g.js} +12 -9
  56. package/dist/config/schema.d.ts +8 -8
  57. package/dist/hooks/curator-postmortem.d.ts +6 -4
  58. package/dist/hooks/curator.d.ts +5 -1
  59. package/dist/hooks/hive-promoter.d.ts +3 -1
  60. package/dist/hooks/knowledge-store.d.ts +54 -1
  61. package/dist/hooks/knowledge-types.d.ts +107 -2
  62. package/dist/hooks/knowledge-validator.d.ts +25 -4
  63. package/dist/index.js +387 -384
  64. package/dist/knowledge/curation-policy.d.ts +125 -0
  65. package/dist/knowledge/family-manifest.d.ts +30 -2
  66. package/dist/knowledge/family-migration.d.ts +16 -4
  67. package/dist/knowledge/scan-cursor.d.ts +107 -0
  68. package/dist/knowledge/worktree-identity.d.ts +39 -0
  69. package/dist/memory/schema.d.ts +1 -1
  70. package/dist/services/knowledge-diagnostics.d.ts +29 -0
  71. package/package.json +1 -1
  72. package/dist/cli/curator-llm-factory-rwwnmdvk.js +0 -43
  73. package/dist/cli/guardrail-explain-kacpc2zm.js +0 -44
  74. /package/dist/cli/{index-vvk2g744.js → index-7g4c7s5r.js} +0 -0
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Cohort-safe curation authorization policy (issue #1848 §2).
3
+ *
4
+ * THE single shared policy every destructive knowledge lifecycle action routes
5
+ * through. Replaces the previous design where archive/purge/remove/quarantine/
6
+ * retraction/merge/retire each implemented their own ad-hoc inline ownership
7
+ * check.
8
+ *
9
+ * Decision ladder (fail-closed default):
10
+ * 1. Config-mismatch guard: cohort members must share curation-relevant
11
+ * config. A fingerprint mismatch → fail closed with an actionable
12
+ * diagnostic (the worktrees will curate with different semantics).
13
+ * 2. Owner path: the acting worktree IS the proven producer and the action
14
+ * relies on its own validated evidence → authorized.
15
+ * 3. Unknown-owner legacy protection: entry has no `producer` (legacy) →
16
+ * fail closed → record a non-destructive proposal. Never guess an owner
17
+ * heuristically from the current worktree.
18
+ * 4. Not-owner + local-only evidence: a different worktree owns the entry and
19
+ * the actor only has LOCAL session evidence → fail closed → proposal.
20
+ * Absence of local evidence is NOT negative evidence.
21
+ * 5. Cohort quorum: cohort-wide evidence (read via the link-aware event
22
+ * resolver) satisfies a configured quorum → authorized.
23
+ * 6. Override: an explicit, audited operator override → authorized + recorded.
24
+ *
25
+ * This module is never imported on the plugin-init path. It is called lazily by
26
+ * the destructive callers (Layer 4 routing).
27
+ */
28
+ import { readFile } from 'node:fs/promises';
29
+ import { readKnowledgeEvents } from '../hooks/knowledge-events.js';
30
+ import { isLinked, readLinkPointer, resolveKnowledgeStoreDir, resolveLinkDir } from '../hooks/knowledge-link.js';
31
+ import type { CurationAction, CurationEvidenceScope, CurationProposal, KnowledgeConfig } from '../hooks/knowledge-types.js';
32
+ import { type CohortConfigFingerprintInput, cohortConfigFingerprint } from './config-fingerprint.js';
33
+ import { resolveWorktreeId } from './worktree-identity.js';
34
+ /** Entry shape the policy needs to inspect for ownership decisions. */
35
+ export interface CurationTargetEntry {
36
+ id: string;
37
+ producer?: {
38
+ worktree_id: string;
39
+ cohort_id: string;
40
+ } | null;
41
+ revision?: number;
42
+ content_hash?: string;
43
+ status?: string;
44
+ }
45
+ /** Input describing the destructive intent. */
46
+ export interface CurationAuthorizationInput {
47
+ directory: string;
48
+ action: CurationAction;
49
+ entryId: string;
50
+ reason?: string;
51
+ /** CAS token from the snapshot the plan was built on. */
52
+ expectedRevision?: number;
53
+ expectedContentHash?: string;
54
+ /** Declared by the caller: which evidence scope was used to decide. */
55
+ evidenceScope: CurationEvidenceScope;
56
+ /** Resolved worktree id of the actor (injected for testability). If
57
+ * omitted, resolved lazily via `resolveWorktreeId`. */
58
+ actorWorktreeId?: string;
59
+ actorRole?: string;
60
+ /** Explicit audited operator override. */
61
+ override?: {
62
+ actor: 'manual-override';
63
+ reason: string;
64
+ };
65
+ }
66
+ /** Result: either authorized (with basis) or unauthorized (with proposal). */
67
+ export type CurationAuthorizationResult = {
68
+ authorized: true;
69
+ basis: 'owner' | 'quorum' | 'override' | 'config-skipped-unlinked';
70
+ detail: string;
71
+ expectedRevision?: number;
72
+ expectedContentHash?: string;
73
+ } | {
74
+ authorized: false;
75
+ basis: 'protected-unknown-owner' | 'not-owner-local-evidence' | 'config-mismatch' | 'quorum-insufficient' | 'entry-not-found';
76
+ detail: string;
77
+ proposal: CurationProposal;
78
+ };
79
+ /** Context carrying config + the target entry (caller pre-fetches once). */
80
+ export interface CurationContext {
81
+ config: KnowledgeConfig;
82
+ entry: CurationTargetEntry | null;
83
+ }
84
+ /** Quorum configuration for cohort-wide decisions. */
85
+ export interface CohortQuorumConfig {
86
+ /** Minimum distinct cohort events (e.g. violations) required to authorize. */
87
+ minCohortEvidence: number;
88
+ }
89
+ /**
90
+ * Authorize a destructive curation action against a single entry.
91
+ *
92
+ * The caller MUST pre-fetch the entry (so ownership is decided against the
93
+ * freshest snapshot, not a stale plan) and pass it via `context.entry`.
94
+ */
95
+ export declare function authorizeCuration(input: CurationAuthorizationInput, context: CurationContext, quorum?: CohortQuorumConfig): Promise<CurationAuthorizationResult>;
96
+ /**
97
+ * Build a config-fingerprint input from a loaded KnowledgeConfig (issue #1848
98
+ * §3 / C-8 fix). Projects the config through its field set so two worktrees
99
+ * with equivalent effective config produce the same fingerprint regardless of
100
+ * whether a value was set explicitly or resolved to a default (the caller is
101
+ * responsible for passing the *resolved* config — defaults are already baked
102
+ * in by the time KnowledgeConfig reaches this layer).
103
+ */
104
+ export declare function buildConfigFingerprintInput(config: KnowledgeConfig): CohortConfigFingerprintInput;
105
+ /**
106
+ * Read the cohort's stored config fingerprint from the cohort link dir
107
+ * (issue #1848 §3). Returns null when no cohort fingerprint is stored yet
108
+ * (first member) or when unlinked.
109
+ */
110
+ export declare function readCohortConfigFingerprint(directory: string): Promise<string | null>;
111
+ /**
112
+ * DI seam for tests. Tests override these to avoid git subprocesses and real
113
+ * filesystem access (per the `_internals` pattern in AGENTS.md invariant 7).
114
+ */
115
+ export declare const _internals: {
116
+ resolveWorktreeId: typeof resolveWorktreeId;
117
+ readKnowledgeEvents: typeof readKnowledgeEvents;
118
+ cohortConfigFingerprint: typeof cohortConfigFingerprint;
119
+ readCohortConfigFingerprint: typeof readCohortConfigFingerprint;
120
+ readFile: typeof readFile;
121
+ isLinked: typeof isLinked;
122
+ readLinkPointer: typeof readLinkPointer;
123
+ resolveLinkDir: typeof resolveLinkDir;
124
+ resolveKnowledgeStoreDir: typeof resolveKnowledgeStoreDir;
125
+ };
@@ -19,10 +19,24 @@
19
19
  * - `append-union`: union by the line's stable id field (event id / entry id),
20
20
  * appending only lines whose id is not already present on the destination.
21
21
  * Idempotent on retry/relink.
22
+ * - `append-concat`: union of an append-only audit log that has NO top-level
23
+ * `id`. Dedup is keyed off a per-member composite `keyOf` selector (e.g.
24
+ * `entry_id + after_revision + timestamp` for rewrite history) so legitimately
25
+ * distinct revisions/proposals are preserved while an idempotent relink/retry
26
+ * does not duplicate a record. Mechanically identical to `append-union` but
27
+ * labelled separately so the manifest documents which members are non-`id`
28
+ * audit logs (issue #1848 cohort-scoped rewrite/proposal history).
22
29
  * - `sum-counters`: per-counter field-wise SUM, reusing the existing
23
30
  * `mergeRollupInto` primitive (shown/applied/... counts sum; timestamps take
24
31
  * the max). Used for the counter baseline JSON.
25
32
  *
33
+ * Key selection:
34
+ * - Every member is deduped by a stable key. By default the key is the line's
35
+ * string `id` field. A member may override this with an explicit `keyOf`
36
+ * selector returning a composite string (or `null` for an unkeyable/malformed
37
+ * line). The default preserves the historical `id`-keyed behavior for every
38
+ * pre-existing member exactly.
39
+ *
26
40
  * NOT in the manifest (deliberate exceptions, documented in
27
41
  * `knowledge-link.ts` "Intentionally NOT redirected"):
28
42
  * - `synonym-map.json` — derived state. It is rebuilt deterministically from
@@ -31,8 +45,15 @@
31
45
  * - `.knowledge-shown.json`, `plan.json`, evidence, session state — per
32
46
  * worktree by design.
33
47
  */
34
- export type FamilyRole = 'store' | 'events' | 'rejected' | 'retractions' | 'counters' | 'quarantine' | 'unactionable' | 'application-legacy';
35
- export type FamilyMergeStrategy = 'dedup-id-merge' | 'append-union' | 'sum-counters';
48
+ export type FamilyRole = 'store' | 'events' | 'rejected' | 'retractions' | 'counters' | 'quarantine' | 'unactionable' | 'application-legacy' | 'rewrite-history' | 'curation-proposals';
49
+ export type FamilyMergeStrategy = 'dedup-id-merge' | 'append-union' | 'append-concat' | 'sum-counters';
50
+ /**
51
+ * Extracts the stable dedup key from a single parsed JSONL line. Returns `null`
52
+ * for a line that carries no derivable key (e.g. missing required fields) — such
53
+ * a line is unaddressable for dedup and is skipped by the append strategies and
54
+ * rejected by the pre-commit validator, exactly as an id-less line is today.
55
+ */
56
+ export type FamilyKeySelector = (line: unknown) => string | null;
36
57
  export interface KnowledgeFamilyMember {
37
58
  /** Filename within the swarm family directory (e.g. `knowledge.jsonl`). */
38
59
  readonly filename: string;
@@ -40,6 +61,13 @@ export interface KnowledgeFamilyMember {
40
61
  readonly mergeStrategy: FamilyMergeStrategy;
41
62
  /** True when unlink may copy this member back to the local worktree. */
42
63
  readonly reversible: boolean;
64
+ /**
65
+ * Optional per-member identity-key selector. When omitted, the member is
66
+ * keyed by its string `id` field (the historical default that every
67
+ * pre-existing member relies on). Members whose records have no top-level
68
+ * `id` (append-only audit logs) supply a composite selector here.
69
+ */
70
+ readonly keyOf?: FamilyKeySelector;
43
71
  }
44
72
  /**
45
73
  * The complete knowledge family that participates in cohort linking.
@@ -36,6 +36,14 @@ export interface FamilyMigrationCounts {
36
36
  skipped: number;
37
37
  }>;
38
38
  }
39
+ /**
40
+ * Resolve the dedup-key selector for a member: its explicit `keyOf` when
41
+ * present, else the default `id` selector. Centralising this guarantees the
42
+ * append merge, the validator, and every consumer key a member identically —
43
+ * and that every pre-existing (no-`keyOf`) member keeps its exact `id`-keyed
44
+ * behavior.
45
+ */
46
+ declare function keySelectorFor(member: KnowledgeFamilyMember): (obj: unknown) => string | null;
39
47
  /**
40
48
  * Provenance-preserving merge of the active store (`dedup-id-merge`).
41
49
  *
@@ -54,11 +62,14 @@ declare function mergeStoreEntries(destination: KnowledgeEntryBase[], source: Kn
54
62
  /** Field-level union of `src` into `target` (mutates target). */
55
63
  declare function mergeEntryFields(target: KnowledgeEntryBase, src: KnowledgeEntryBase): void;
56
64
  /**
57
- * Append-union: append source lines whose id is not already present on the
58
- * destination. Lines without an id field are skipped (we cannot dedup them
59
- * safely, and every family member here is id-keyed).
65
+ * Append-union / append-concat: append source lines whose key is not already
66
+ * present on the destination. The key is extracted by `keyOf` the member's id
67
+ * field by default (`append-union`), or a composite selector for non-`id`
68
+ * append-only audit logs (`append-concat`). Lines whose key is `null` (no
69
+ * derivable key / malformed) are skipped: we cannot dedup them safely, and a
70
+ * keyless line is unaddressable. Idempotent on retry/relink for both strategies.
60
71
  */
61
- declare function appendUnionById<T>(destination: T[], source: T[]): {
72
+ declare function appendUnionById<T>(destination: T[], source: T[], keyOf?: (obj: unknown) => string | null): {
62
73
  merged: T[];
63
74
  added: number;
64
75
  skipped: number;
@@ -104,6 +115,7 @@ export declare const _internals: {
104
115
  mergeEntryFields: typeof mergeEntryFields;
105
116
  serialize: typeof serialize;
106
117
  validateSerialized: typeof validateSerialized;
118
+ keySelectorFor: typeof keySelectorFor;
107
119
  MIGRATION_LOCK_STALE_MS: number;
108
120
  };
109
121
  export {};
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Fair, durable scanning cursor for cohort curation (issue #1848 §4).
3
+ *
4
+ * Replaces the fixed oldest-~500-entry window in curator-postmortem with
5
+ * durable, fair pagination/cursor semantics. Every eligible record is
6
+ * eventually visited; progress survives restart; concurrent append/update does
7
+ * not permanently skip or duplicate destructive handling; retry after crash is
8
+ * idempotent.
9
+ *
10
+ * TWO-LAYER IDEMPOTENCY (C-5 fix from the critic review):
11
+ *
12
+ * 1. ATOMIC BATCH CLAIM: `claimNextScanBatch` reads the cursor AND advances it
13
+ * in ONE locked transaction, BEFORE curation. This prevents two concurrent
14
+ * cohort worktrees (W1, W2) from taking the same batch — W1 claims batch B,
15
+ * advances the cursor; W2 sees the advanced cursor and takes the next batch.
16
+ * Crash-after-claim-before-commit → that batch's work is lost for THIS run
17
+ * (picked up next generation); crash-before-claim → cursor unchanged →
18
+ * retry is idempotent.
19
+ *
20
+ * 2. PER-ENTRY GENERATION STAMP: each entry carries `last_curated_generation`.
21
+ * Non-idempotent actions (rewrite/demote/confidence-delta) are skipped if
22
+ * `entry.last_curated_generation === currentGeneration`. Status-idempotent
23
+ * actions (archive/quarantine/remove) need no stamp (re-application is a
24
+ * no-op). This makes the cursor safe even if a batch is somehow processed
25
+ * twice (e.g. recovery from a crash-after-claim that was partially applied).
26
+ *
27
+ * Cursor state is cohort-scoped (under `resolveKnowledgeStoreDir`, so it is
28
+ * shared across linked worktrees) and lives at `curation-cursor.json`. It is
29
+ * NOT a KNOWLEDGE_FAMILY migration member — it's regenerable state, not
30
+ * id-mergeable data.
31
+ *
32
+ * This module is never imported on the plugin-init path.
33
+ */
34
+ import { existsSync } from 'node:fs';
35
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
36
+ import { resolveKnowledgeStoreDir } from '../hooks/knowledge-link.js';
37
+ import { readKnowledge, transactKnowledge } from '../hooks/knowledge-store.js';
38
+ import type { KnowledgeEntryBase } from '../hooks/knowledge-types.js';
39
+ import { resolveCohortId } from './cohort-identity.js';
40
+ /** Cursor state persisted to disk (cohort-scoped). */
41
+ export interface ScanCursorState {
42
+ cohort_id: string;
43
+ /** `${created_at}|${id}` — stable ordering + deterministic tie-breaker. */
44
+ last_visited_sort_key: string;
45
+ /** Monotonically increasing sweep generation. */
46
+ generation: number;
47
+ /** True when the last sweep reached the tail; next call starts fresh. */
48
+ completed: boolean;
49
+ /** Approximate count of entries still eligible to visit this generation. */
50
+ remaining_estimate: number;
51
+ updated_at: string;
52
+ }
53
+ /** A claimed batch + its generation (for the generation-stamp idempotency). */
54
+ export interface ScanBatch {
55
+ entries: KnowledgeEntryBase[];
56
+ generation: number;
57
+ /** True when this batch completed the current sweep. */
58
+ sweepCompleted: boolean;
59
+ remaining_estimate: number;
60
+ }
61
+ /** Lightweight status for diagnostics. */
62
+ export interface ScanStatus {
63
+ generation: number;
64
+ completed: boolean;
65
+ remaining_estimate: number;
66
+ cohort_id: string;
67
+ }
68
+ /**
69
+ * DI seam for tests.
70
+ */
71
+ export declare const _internals: {
72
+ readFile: typeof readFile;
73
+ writeFile: typeof writeFile;
74
+ existsSync: typeof existsSync;
75
+ mkdir: typeof mkdir;
76
+ readKnowledge: typeof readKnowledge;
77
+ transactKnowledge: typeof transactKnowledge;
78
+ resolveCohortId: typeof resolveCohortId;
79
+ resolveKnowledgeStoreDir: typeof resolveKnowledgeStoreDir;
80
+ };
81
+ /**
82
+ * Claim the next scan batch ATOMICALLY (issue #1848 §4 / C-5 fix).
83
+ *
84
+ * Reads the cursor, selects the next `batchSize` unvisited eligible entries,
85
+ * and ADVANCES the cursor past them in ONE step — all before curation runs.
86
+ * This means a concurrent cohort worktree calling this in parallel will see
87
+ * the already-advanced cursor and take the NEXT batch, never the same one.
88
+ *
89
+ * Eligible = active status (not archived/quarantined/unactionable) and sort-key
90
+ * beyond the cursor's last-visited position for this generation.
91
+ *
92
+ * Returns an empty batch when the sweep is complete; the caller may then start
93
+ * a fresh sweep (generation bumped) on the next invocation.
94
+ */
95
+ export declare function claimNextScanBatch(directory: string, batchSize?: number): Promise<ScanBatch>;
96
+ /**
97
+ * Read scan status for diagnostics (issue #1848 §4 "remaining eligible work").
98
+ */
99
+ export declare function getScanStatus(directory: string): Promise<ScanStatus>;
100
+ /**
101
+ * Check whether a non-idempotent action should be skipped for this entry in the
102
+ * current generation (C-5 fix, second layer of idempotency). Returns true if
103
+ * the entry was already curated in this generation → skip the action.
104
+ */
105
+ export declare function alreadyCuratedThisGeneration(entry: {
106
+ last_curated_generation?: number;
107
+ }, generation: number): boolean;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Stable per-worktree identity (issue #1848 §1).
3
+ *
4
+ * `resolveWorktreeId` returns a stable UUID that is UNIQUE PER WORKTREE (not
5
+ * per cohort). It is the primary ownership key for the curation-policy layer:
6
+ * a worktree may directly mutate a knowledge entry only when it is the proven
7
+ * producer (`entry.producer.worktree_id === actorWorktreeId`).
8
+ *
9
+ * CRITICAL: this id is stored at `<directory>/.swarm/worktree-id.json` via the
10
+ * RAW directory path — it is deliberately NOT link-resolved. Two sibling
11
+ * worktrees of the same cohort share a knowledge store but have DISTINCT
12
+ * worktree ids, which is exactly what makes cohort-safe ownership decisions
13
+ * possible.
14
+ *
15
+ * This module performs no git subprocess calls and is never imported on the
16
+ * plugin-init path. It is called lazily by the curation stamping path and the
17
+ * authorization policy. Fail-open: if the id file cannot be read or written, a
18
+ * transient ephemeral id is returned so curation never hard-blocks on an I/O
19
+ * hiccup.
20
+ */
21
+ import { randomUUID } from 'node:crypto';
22
+ import { existsSync } from 'node:fs';
23
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
24
+ /**
25
+ * Seam for dependency injection in tests. Tests override `readFile`,
26
+ * `writeFile`, and `randomUUID` to avoid touching the real filesystem / entropy.
27
+ */
28
+ export declare const _internals: {
29
+ readFile: typeof readFile;
30
+ writeFile: typeof writeFile;
31
+ existsSync: typeof existsSync;
32
+ mkdir: typeof mkdir;
33
+ randomUUID: typeof randomUUID;
34
+ };
35
+ /**
36
+ * Resolve the stable per-worktree id, creating it on first call if absent.
37
+ * Returns a transient ephemeral id on I/O failure (fail-open).
38
+ */
39
+ export declare function resolveWorktreeId(directory: string): Promise<string>;
@@ -215,8 +215,8 @@ export declare const MemoryProposalSchema: z.ZodObject<{
215
215
  rationale: z.ZodString;
216
216
  evidenceRefs: z.ZodArray<z.ZodString>;
217
217
  status: z.ZodEnum<{
218
- applied: "applied";
219
218
  pending: "pending";
219
+ applied: "applied";
220
220
  rejected: "rejected";
221
221
  approved: "approved";
222
222
  superseded: "superseded";
@@ -91,6 +91,35 @@ export interface KnowledgeDebugMeta {
91
91
  promotion_event_id?: string;
92
92
  }>;
93
93
  };
94
+ /**
95
+ * Cohort-safe curation health (issue #1848). Surfaces the fair scan
96
+ * cursor progress and cohort config-fingerprint agreement so an operator
97
+ * knows whether entries are being starved and whether cohort members
98
+ * share curation semantics.
99
+ */
100
+ curation: {
101
+ /** Fair scan generation (monotonic sweep counter). 0 = no cursor yet. */
102
+ scan_generation: number;
103
+ /** True when the last sweep completed (next sweep starts fresh). */
104
+ scan_completed: boolean;
105
+ /** Approximate count of entries still eligible to visit this generation. */
106
+ scan_remaining_estimate: number;
107
+ /** True when this worktree's config fingerprint matches the cohort's.
108
+ * `null` when unlinked or no cohort fingerprint is stored. */
109
+ config_fingerprint_match: boolean | null;
110
+ /** Count of entries with producer provenance (v3+). */
111
+ entries_with_provenance: number;
112
+ /** Count of legacy entries without provenance (protected by default). */
113
+ entries_unknown_owner: number;
114
+ /**
115
+ * Pending curation proposals persisted to `curation-proposals.jsonl`
116
+ * (PRR-008): destructive actions that were blocked (unknown-owner,
117
+ * config-mismatch, insufficient evidence) and recorded for a cohort
118
+ * member to later confirm. Never surfaced before; operators need to see
119
+ * that blocked destructive intent is queued.
120
+ */
121
+ pending_proposals: number;
122
+ };
94
123
  }
95
124
  /**
96
125
  * Compute the debug-metadata block for the knowledge system. Best-effort: never
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.119.0",
3
+ "version": "7.120.0",
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",
@@ -1,43 +0,0 @@
1
- // @bun
2
- import {
3
- createCuratorLLMDelegate
4
- } from "./index-mr3pnc4h.js";
5
- import"./index-nprawcc0.js";
6
- import"./index-g2wrs7jy.js";
7
- import"./index-c8s9a3zh.js";
8
- import"./index-11q1t6g0.js";
9
- import"./index-pdk1n8yh.js";
10
- import"./index-p45sshb5.js";
11
- import"./index-vwvkpg0d.js";
12
- import"./index-4905hd2m.js";
13
- import"./index-5bcrp3bc.js";
14
- import"./index-r2ysfdet.js";
15
- import"./index-pjhfe7c3.js";
16
- import"./index-jbdgpqpj.js";
17
- import"./index-5hze8ztj.js";
18
- import"./index-qx5z58a7.js";
19
- import"./index-bfwt3abw.js";
20
- import"./index-wpgv6hht.js";
21
- import"./index-vxv732ex.js";
22
- import"./index-8fwhhayc.js";
23
- import"./index-q1exe2b3.js";
24
- import"./index-n6fa2hhk.js";
25
- import"./index-q4kgsvdd.js";
26
- import"./index-ye3fndsw.js";
27
- import"./index-kzj3fp7c.js";
28
- import"./index-5e4e2hvv.js";
29
- import"./index-p0arc26j.js";
30
- import"./index-zgwm4ryv.js";
31
- import"./index-dqh3zhhc.js";
32
- import"./index-abjjn4yh.js";
33
- import"./index-0pfqcjp6.js";
34
- import"./index-293f68mj.js";
35
- import"./index-42cx6g2h.js";
36
- import"./index-fsrp8wp3.js";
37
- import"./index-q2rrv0cy.js";
38
- import"./index-vvk2g744.js";
39
- import"./index-qe3v54nb.js";
40
- import"./index-a76rekgs.js";
41
- export {
42
- createCuratorLLMDelegate
43
- };
@@ -1,44 +0,0 @@
1
- // @bun
2
- import {
3
- handleGuardrailExplain
4
- } from "./index-7pspktns.js";
5
- import"./index-mr3pnc4h.js";
6
- import"./index-nprawcc0.js";
7
- import"./index-g2wrs7jy.js";
8
- import"./index-c8s9a3zh.js";
9
- import"./index-11q1t6g0.js";
10
- import"./index-pdk1n8yh.js";
11
- import"./index-p45sshb5.js";
12
- import"./index-vwvkpg0d.js";
13
- import"./index-4905hd2m.js";
14
- import"./index-5bcrp3bc.js";
15
- import"./index-r2ysfdet.js";
16
- import"./index-pjhfe7c3.js";
17
- import"./index-jbdgpqpj.js";
18
- import"./index-5hze8ztj.js";
19
- import"./index-qx5z58a7.js";
20
- import"./index-bfwt3abw.js";
21
- import"./index-wpgv6hht.js";
22
- import"./index-vxv732ex.js";
23
- import"./index-8fwhhayc.js";
24
- import"./index-q1exe2b3.js";
25
- import"./index-n6fa2hhk.js";
26
- import"./index-q4kgsvdd.js";
27
- import"./index-ye3fndsw.js";
28
- import"./index-kzj3fp7c.js";
29
- import"./index-5e4e2hvv.js";
30
- import"./index-p0arc26j.js";
31
- import"./index-zgwm4ryv.js";
32
- import"./index-dqh3zhhc.js";
33
- import"./index-abjjn4yh.js";
34
- import"./index-0pfqcjp6.js";
35
- import"./index-293f68mj.js";
36
- import"./index-42cx6g2h.js";
37
- import"./index-fsrp8wp3.js";
38
- import"./index-q2rrv0cy.js";
39
- import"./index-vvk2g744.js";
40
- import"./index-qe3v54nb.js";
41
- import"./index-a76rekgs.js";
42
- export {
43
- handleGuardrailExplain
44
- };