opencode-swarm 7.119.0 → 7.121.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.
- package/dist/cli/{config-doctor-p73yfdz0.js → config-doctor-404c8mf8.js} +3 -3
- package/dist/cli/config-fingerprint-v22xh5a2.js +10 -0
- package/dist/cli/{core-gf1t2jmn.js → core-xnwasfpc.js} +1 -1
- package/dist/cli/curation-policy-re2kaysb.js +25 -0
- package/dist/cli/{curator-5xfkejsq.js → curator-8m0pf9cz.js} +30 -23
- package/dist/cli/{curator-drift-nn71xfz6.js → curator-drift-6b2fjsgr.js} +3 -2
- package/dist/cli/curator-llm-factory-jta9tggm.js +50 -0
- package/dist/cli/{evidence-summary-service-cy3k37je.js → evidence-summary-service-jtx5q3r6.js} +9 -7
- package/dist/cli/{gate-evidence-j2m4cd85.js → gate-evidence-tbpezk28.js} +5 -5
- package/dist/cli/guardrail-explain-nsfm5ksb.js +51 -0
- package/dist/cli/{guardrail-log-qjqgg7pz.js → guardrail-log-dhpp0kmn.js} +4 -4
- package/dist/cli/{hive-promoter-03m8pwg6.js → hive-promoter-8303gnfk.js} +30 -23
- package/dist/cli/index-0e1v02hq.js +343 -0
- package/dist/cli/{index-jbdgpqpj.js → index-0k4r8111.js} +28 -10
- package/dist/cli/index-0mbmfmt0.js +349 -0
- package/dist/cli/{index-5bcrp3bc.js → index-22e127jw.js} +3 -3
- package/dist/cli/{index-pjhfe7c3.js → index-3366gs26.js} +43 -7
- package/dist/cli/{index-7pspktns.js → index-3je67dcz.js} +2 -2
- package/dist/cli/{index-wpgv6hht.js → index-3jvqw5hy.js} +13 -26
- package/dist/cli/{index-05ee8pws.js → index-3ygv9535.js} +32 -25
- package/dist/cli/{index-bfwt3abw.js → index-4rhcth7a.js} +1 -1
- package/dist/cli/{index-n6fa2hhk.js → index-51gg9947.js} +1 -1
- package/dist/cli/index-69ms9ny1.js +261 -0
- package/dist/cli/{index-42cx6g2h.js → index-76kvm9z5.js} +1 -1
- package/dist/cli/{index-r2ysfdet.js → index-78b192v7.js} +7 -7
- package/dist/cli/{index-vwvkpg0d.js → index-816rkp4y.js} +3 -92
- package/dist/cli/index-89s4mhmw.js +6 -0
- package/dist/cli/index-9xpgytj9.js +386 -0
- package/dist/cli/index-ae75rja9.js +26 -0
- package/dist/cli/index-fndvr7fr.js +135 -0
- package/dist/cli/{index-mr3pnc4h.js → index-gmc34yzt.js} +1926 -2005
- package/dist/cli/index-gnp9x6se.js +93 -0
- package/dist/cli/{index-0pfqcjp6.js → index-h07wn84h.js} +1 -1
- package/dist/cli/{index-p45sshb5.js → index-j5xv8zbp.js} +17 -352
- package/dist/cli/{index-g2wrs7jy.js → index-kyvg2cmp.js} +1 -1
- package/dist/cli/{index-5hze8ztj.js → index-nhgw7de8.js} +7 -5
- package/dist/cli/{index-abjjn4yh.js → index-qg11yf8q.js} +4 -4
- package/dist/cli/index-rtry5xyf.js +27 -0
- package/dist/cli/{index-qx5z58a7.js → index-rywkdacj.js} +93 -11
- package/dist/cli/{index-pdk1n8yh.js → index-tftv9xh9.js} +44 -2
- package/dist/cli/{index-11q1t6g0.js → index-vqcgmy4y.js} +1 -1
- package/dist/cli/{index-q4kgsvdd.js → index-wtn8wf7h.js} +1 -1
- package/dist/cli/{index-bcg89pt8.js → index-zvae4m8g.js} +1 -1
- package/dist/cli/index.js +30 -23
- package/dist/cli/{knowledge-escalator-gt1q632m.js → knowledge-escalator-0ejqs8c9.js} +9 -8
- package/dist/cli/{knowledge-events-0bjdtdsb.js → knowledge-events-wx9e37ba.js} +6 -5
- package/dist/cli/knowledge-link-sy85fw3w.js +37 -0
- package/dist/cli/{knowledge-store-fm2300xf.js → knowledge-store-rnajx33a.js} +17 -6
- package/dist/cli/{knowledge-validator-91kx3eqv.js → knowledge-validator-1f4mkzmc.js} +11 -7
- package/dist/cli/{pending-delegations-9cn17983.js → pending-delegations-0z3znsec.js} +5 -5
- package/dist/cli/{pr-subscriptions-8a6jwek2.js → pr-subscriptions-x653151h.js} +4 -4
- package/dist/cli/scan-cursor-qapm0sxj.js +25 -0
- package/dist/cli/{schema-7c484wjz.js → schema-vwxpsk6j.js} +2 -2
- package/dist/cli/{scope-persistence-fm0gzhnk.js → scope-persistence-mfr36yc9.js} +2 -2
- package/dist/cli/{skill-generator-3f8dqjj2.js → skill-generator-tkv7s12g.js} +12 -9
- package/dist/config/schema.d.ts +8 -8
- package/dist/config/skill-mirrors.d.ts +1 -1
- package/dist/hooks/curator-postmortem.d.ts +6 -4
- package/dist/hooks/curator.d.ts +5 -1
- package/dist/hooks/hive-promoter.d.ts +3 -1
- package/dist/hooks/knowledge-store.d.ts +54 -1
- package/dist/hooks/knowledge-types.d.ts +107 -2
- package/dist/hooks/knowledge-validator.d.ts +25 -4
- package/dist/index.js +387 -384
- package/dist/knowledge/curation-policy.d.ts +125 -0
- package/dist/knowledge/family-manifest.d.ts +30 -2
- package/dist/knowledge/family-migration.d.ts +16 -4
- package/dist/knowledge/scan-cursor.d.ts +107 -0
- package/dist/knowledge/worktree-identity.d.ts +39 -0
- package/dist/memory/schema.d.ts +1 -1
- package/dist/services/knowledge-diagnostics.d.ts +29 -0
- package/package.json +1 -1
- package/dist/cli/curator-llm-factory-rwwnmdvk.js +0 -43
- package/dist/cli/guardrail-explain-kacpc2zm.js +0 -44
- /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
|
|
58
|
-
* destination.
|
|
59
|
-
*
|
|
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>;
|
package/dist/memory/schema.d.ts
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "7.121.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
|
-
};
|
|
File without changes
|