opencode-swarm 7.145.2 → 7.146.1

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 (73) hide show
  1. package/.opencode/skills/swarm-pr-review/SKILL.md +67 -33
  2. package/dist/cli/{config-doctor-j1r33f4q.js → config-doctor-sd7x5bts.js} +2 -2
  3. package/dist/cli/{core-c2zrd384.js → core-17febkvw.js} +1 -1
  4. package/dist/cli/{curation-policy-xv0j8zyt.js → curation-policy-x19e8bqc.js} +4 -4
  5. package/dist/cli/{curator-llm-factory-sat3mn5n.js → curator-llm-factory-zxbgkjm5.js} +23 -23
  6. package/dist/cli/{curator-gk2kmrk1.js → curator-vghf320v.js} +23 -23
  7. package/dist/cli/{evidence-summary-service-0mhx0t0v.js → evidence-summary-service-xma4aagr.js} +9 -9
  8. package/dist/cli/{gate-evidence-a5adh55x.js → gate-evidence-2bznvee8.js} +4 -4
  9. package/dist/cli/{guardrail-explain-r50fery8.js → guardrail-explain-ppzg0xz9.js} +24 -24
  10. package/dist/cli/{guardrail-log-p69eys6d.js → guardrail-log-xexwz93q.js} +3 -3
  11. package/dist/cli/{hive-promoter-ay8a7sct.js → hive-promoter-j3vw2hj4.js} +23 -23
  12. package/dist/cli/{index-czde45km.js → index-19f0zycs.js} +2 -2
  13. package/dist/cli/{index-2vefr1f8.js → index-1c9mfnj0.js} +1 -1
  14. package/dist/cli/{index-917z76qr.js → index-4qvsh3m5.js} +8 -5
  15. package/dist/cli/{index-cb2qtetk.js → index-7x00nx8m.js} +1765 -1095
  16. package/dist/cli/{index-np4ytvvv.js → index-9qbr27bw.js} +2 -2
  17. package/dist/cli/{index-7y5akvc9.js → index-bd4g9fvb.js} +1 -1
  18. package/dist/cli/{index-jpppcckq.js → index-cw96tpy6.js} +4 -4
  19. package/dist/cli/{index-4p3qfb05.js → index-e1hjd15c.js} +1 -1
  20. package/dist/cli/{index-9080b3de.js → index-e3caa7ng.js} +25 -25
  21. package/dist/cli/{index-ncfjxz9n.js → index-fksfxhmx.js} +5 -5
  22. package/dist/cli/{index-673axbjx.js → index-g8qbjqqz.js} +1 -1
  23. package/dist/cli/{index-gpnv14kq.js → index-h8qed3pr.js} +41 -6
  24. package/dist/cli/{index-cxn95n7q.js → index-k46kg4t6.js} +5 -5
  25. package/dist/cli/{index-612rr70a.js → index-n7trn9wz.js} +5 -1
  26. package/dist/cli/{index-j20fzn0w.js → index-qbpxrw95.js} +1 -1
  27. package/dist/cli/{index-n55a28zr.js → index-t7sfrv3v.js} +3 -3
  28. package/dist/cli/{index-9gt8g2qj.js → index-twwb61bh.js} +3 -3
  29. package/dist/cli/{index-jgb7mq2y.js → index-txh3h83v.js} +4 -4
  30. package/dist/cli/{index-rk80jhef.js → index-tzyvmkd1.js} +1 -1
  31. package/dist/cli/{index-b9kxxd6k.js → index-va8a2zrq.js} +2 -2
  32. package/dist/cli/{index-6wf75rcm.js → index-vacrzesw.js} +6 -6
  33. package/dist/cli/{index-x8pm918t.js → index-w0cj7zyd.js} +1 -1
  34. package/dist/cli/{index-0a0asz2s.js → index-w5r84rd1.js} +1 -1
  35. package/dist/cli/{index-1g87esfp.js → index-xz0ccakc.js} +1 -1
  36. package/dist/cli/{index-kggqpefz.js → index-ycgm9pzg.js} +1 -1
  37. package/dist/cli/{index-nsr5b69r.js → index-zkaz3fhw.js} +1 -1
  38. package/dist/cli/{index-vav705md.js → index-zwn2e2pe.js} +3 -3
  39. package/dist/cli/index.js +23 -23
  40. package/dist/cli/{knowledge-escalator-22apj3dj.js → knowledge-escalator-wj85ppdt.js} +9 -9
  41. package/dist/cli/{knowledge-events-bx8xba9z.js → knowledge-events-4y9kk2xe.js} +7 -7
  42. package/dist/cli/{knowledge-link-82ew2yc4.js → knowledge-link-8pr02pza.js} +3 -3
  43. package/dist/cli/{knowledge-store-nc71kfsg.js → knowledge-store-kc8dwc14.js} +4 -4
  44. package/dist/cli/{knowledge-validator-g8ne80v6.js → knowledge-validator-584ga9fb.js} +6 -6
  45. package/dist/cli/{runner-pqsf809k.js → runner-e7z9cf5y.js} +4 -4
  46. package/dist/cli/{scan-cursor-6ksgv514.js → scan-cursor-rhjym1pn.js} +5 -5
  47. package/dist/cli/{schema-mpcm7nje.js → schema-yb3pvhcs.js} +5 -1
  48. package/dist/cli/{scope-persistence-mj1w3hqw.js → scope-persistence-bxwjk9rc.js} +5 -5
  49. package/dist/cli/{skill-generator-gsa6qwwt.js → skill-generator-3w8w7htr.js} +11 -11
  50. package/dist/cli/{worktree-isolation-ajzyp2gm.js → worktree-isolation-av1rshr3.js} +23 -23
  51. package/dist/commands/memory.d.ts +8 -0
  52. package/dist/commands/registry.d.ts +9 -1
  53. package/dist/config/schema.d.ts +36 -0
  54. package/dist/hooks/guardrails/index.d.ts +35 -0
  55. package/dist/hooks/pr-workflow-gate.d.ts +48 -2
  56. package/dist/hooks/pr-workflow-response-gate.d.ts +24 -0
  57. package/dist/index.js +431 -383
  58. package/dist/memory/config.d.ts +20 -0
  59. package/dist/memory/errors.d.ts +14 -0
  60. package/dist/memory/gateway.d.ts +32 -0
  61. package/dist/memory/index.d.ts +3 -2
  62. package/dist/memory/pii.d.ts +66 -0
  63. package/dist/memory/prompt-block.d.ts +1 -1
  64. package/dist/memory/provider.d.ts +15 -0
  65. package/dist/memory/redaction.d.ts +31 -8
  66. package/dist/memory/schema.d.ts +18 -0
  67. package/dist/memory/sentinel.d.ts +12 -0
  68. package/dist/memory/sqlite-provider.d.ts +93 -0
  69. package/dist/memory/types.d.ts +9 -0
  70. package/dist/tools/dispatch-lanes.d.ts +13 -1
  71. package/dist/workflow/coder-settlement.d.ts +13 -0
  72. package/dist/workflow/stage-a-repair.d.ts +41 -0
  73. package/package.json +12 -2
@@ -31,6 +31,26 @@ export interface MemoryConfig {
31
31
  };
32
32
  redaction: {
33
33
  rejectDurableSecrets: boolean;
34
+ /**
35
+ * #1466: run a PII detector over durable memory text at the write
36
+ * boundary. Default false — the default install performs no PII
37
+ * detection (pre-#1466 behavior). When true, a summary (types/counts/
38
+ * score, never matched text) is attached to proposals.
39
+ */
40
+ detectPii: boolean;
41
+ /**
42
+ * #1466: which PiiDetector implementation to use. 'regex' (default) is
43
+ * dependency-free; 'ner' requires the optional
44
+ * `@xenova/transformers` peer dependency (typed error when absent).
45
+ */
46
+ piiDetector: 'regex' | 'ner';
47
+ /**
48
+ * #1466: reject durable memory proposals whose PII score EXCEEDS
49
+ * `piiThreshold` (default 0.7). Rejection is logged to `memory_events`
50
+ * as `pii_rejected` with types/score only — never matched text.
51
+ */
52
+ rejectDurablePii: boolean;
53
+ piiThreshold: number;
34
54
  };
35
55
  maintenance: {
36
56
  /** @deprecated superseded by `maintenance.importance` (issue #1464); retained for back-compat. */
@@ -5,3 +5,17 @@ export declare class MemoryValidationError extends Error {
5
5
  export declare class MemoryDisabledError extends Error {
6
6
  constructor(message?: string);
7
7
  }
8
+ /**
9
+ * #1466: the configured PiiDetector could not run. Thrown fail-closed — an
10
+ * operator who opted into `memory.redaction.piiDetector: 'ner'` (or into
11
+ * PII rejection) must get an actionable error, not a silent skip of the
12
+ * privacy control or a raw ERR_MODULE_NOT_FOUND.
13
+ *
14
+ * PR #2310 feedback (PRR-016): extends MemoryValidationError so gateway and
15
+ * tool-layer callers that branch on the validation-error family (and the
16
+ * `code` field) handle the opt-in-misconfiguration case through the same
17
+ * contract as every other write-boundary rejection.
18
+ */
19
+ export declare class MemoryPiiDetectorError extends MemoryValidationError {
20
+ constructor(message: string);
21
+ }
@@ -48,6 +48,8 @@ export declare class MemoryGateway {
48
48
  private readonly vettedRoot;
49
49
  private readonly now;
50
50
  private disposed;
51
+ /** #1466: cached PII detector instance (NER keeps its loaded pipeline here). */
52
+ private piiDetector;
51
53
  constructor(context: MemoryContext, options?: MemoryGatewayOptions);
52
54
  isEnabled(): boolean;
53
55
  dispose(): Promise<void>;
@@ -69,6 +71,34 @@ export declare class MemoryGateway {
69
71
  upsertCurated(record: MemoryRecord): Promise<MemoryRecord>;
70
72
  recordOutcome(input: RecordMemoryOutcomeInput): Promise<MemoryRecord>;
71
73
  applyCuratorDecision(decision: CuratorMemoryDecision): Promise<AppliedMemoryChange>;
74
+ /**
75
+ * #1466: single write-boundary validation for every durable-record-
76
+ * materializing path (propose, upsertCurated, recordOutcome-created
77
+ * records, applyCuratorDecision).
78
+ *
79
+ * ALWAYS runs the full `validateMemoryRecordRules` rule set exactly ONCE
80
+ * per record (evidence/secret/sentinel rules — including when PII policy
81
+ * is off, the default). When `detectPii`/`rejectDurablePii` is on, the
82
+ * configured detector runs first and its findings ride the same single
83
+ * validation call (threshold logic lives only in the schema funnel). On a
84
+ * PII rejection the `pii_rejected` audit event is logged (types/score
85
+ * only, never matched text) before rethrowing; the rejected record is NOT
86
+ * persisted — storing rejected PII text would defeat the control.
87
+ *
88
+ * Returns the parsed record plus a matched-text-free PII summary for
89
+ * metadata annotation (undefined when nothing was found or detection is
90
+ * off). A detector that cannot run (opt-in NER without the peer
91
+ * dependency) fails closed with a typed error — never a silent skip.
92
+ */
93
+ private validateRecordWithPiiPolicy;
94
+ /**
95
+ * PR #2310 feedback FB-3: free-text that becomes durable memory state
96
+ * WITHOUT being a MemoryRecord body (outcome `correction` text — up to
97
+ * 4000 chars, agent-callable, and re-injected into prompts via the
98
+ * reflection path) must pass the same write-boundary checks: the DD-14
99
+ * sentinel/bundle bans and, when configured, the PII threshold.
100
+ */
101
+ private enforcePiiTextPolicy;
72
102
  createRecord(input: {
73
103
  kind: MemoryKind;
74
104
  text: string;
@@ -81,6 +111,8 @@ export declare class MemoryGateway {
81
111
  metadata?: Record<string, unknown>;
82
112
  anchors?: MemoryAnchor[];
83
113
  outcomes?: MemoryOutcome[];
114
+ /** #1466: audit reason stamped when this record supersedes another. */
115
+ supersedesReason?: string;
84
116
  }): MemoryRecord;
85
117
  private resolveCuratorDecision;
86
118
  private createRecordFromNew;
@@ -1,6 +1,6 @@
1
1
  export type { MemoryConfig } from './config';
2
2
  export { DEFAULT_MEMORY_CONFIG, resolveMemoryConfig } from './config';
3
- export { MemoryDisabledError, MemoryValidationError } from './errors';
3
+ export { MemoryDisabledError, MemoryPiiDetectorError, MemoryValidationError, } from './errors';
4
4
  export { evaluateMemoryRecallFixtures, loadRecallEvaluationFixtures, type RecallEvaluationMetrics, type RecallEvaluationMode, type RecallEvaluationOptions, type RecallEvaluationProviderName, type RecallEvaluationReport, type RecallEvaluationRun, } from './evaluation';
5
5
  export type { MemoryGatewayOptions, ProposeMemoryInput, RecallMemoryInput, RecordMemoryOutcomeInput, } from './gateway';
6
6
  export { createConfiguredMemoryProvider, createConfiguredMemoryProviderForRoot, createMemoryGateway, MemoryGateway, } from './gateway';
@@ -10,6 +10,7 @@ export { LocalJsonlMemoryProvider } from './local-jsonl-provider';
10
10
  export { buildMemoryMaintenanceReport, type MemoryMaintenanceReport, type MemoryMaintenanceReportOptions, type MemoryRecallUsageByMemory, type MemoryRecallUsageByRole, type MemorySupersededChain, shouldCompactMemory, } from './maintenance';
11
11
  export { invalidateMemoryStoreDirCache, isMemoryLinked, MEMORY_LINK_POINTER_FILENAME, type MemoryLinkPointer, readMemoryLinkPointer, removeMemoryLinkPointer, resolveMemoryStoreDir, writeMemoryLinkPointer, } from './memory-link';
12
12
  export type { MemoryOutcomeEvent } from './outcome-events';
13
+ export { computePiiScore, createPiiDetector, type PiiDetector, type PiiFinding, type PiiType, RegexPiiDetector, summarizePiiFindings, } from './pii';
13
14
  export { buildRecallPromptBlock } from './prompt-block';
14
15
  export type { MemoryCompactOptions, MemoryCompactResult, MemoryProposalStore, MemoryProvider, MemoryRecallUsageEvent, MemoryRecallUsageFilter, } from './provider';
15
16
  export { buildMemoryRecallPlan, type MemoryRecallPlan, type MemoryRecallPlannerInput, } from './recall-planner';
@@ -18,6 +19,6 @@ export { readDeadAnchorMemoryIds, readReflectionDigest, recordOutcomeWithReflect
18
19
  export { MEMORY_RECALL_PROFILES, type MemoryRecallProfile, normalizeMemoryAgentRole, resolveMemoryRecallProfile, } from './role-profiles';
19
20
  export { appendMemoryRunLog, sanitizeRunId } from './run-log';
20
21
  export { computeMemoryContentHash, createBundleId, createMemoryId, createProposalId, isExpired, MemoryAnchorSchema, normalizeMemoryAnchorFile, normalizeMemoryText, validateCuratorMemoryDecision, validateMemoryProposal, validateMemoryRecordRules, } from './schema';
21
- export { SQLiteMemoryProvider } from './sqlite-provider';
22
+ export { EVENT_CHAIN_GENESIS, MEMORY_EVENTS_CHAIN_HEAD_KEY, type MemoryAuditVerificationReport, type MemoryEventRow, memoryEventRowHash, SQLiteMemoryProvider, verifyMemoryEventChainRows, } from './sqlite-provider';
22
23
  export { isCohortRoot, isLocalRoot, resolveVettedMemoryRoot, rootStoragePath, type VettedMemoryRoot, wrapLocalRoot, } from './storage-root';
23
24
  export type { AppliedMemoryChange, CuratorMemoryDecision, MemoryAnchor, MemoryContext, MemoryKind, MemoryListFilter, MemoryOutcome, MemoryPatch, MemoryProposal, MemoryRecord, MemoryScopeRef, MemoryScopeType, NewMemoryRecord, RecallBundle, RecallInjectionSkipReason, RecallMode, RecallRequest, RecallResultItem, ResolvedCuratorMemoryDecision, } from './types';
@@ -0,0 +1,66 @@
1
+ export type PiiType = 'email' | 'phone' | 'credit_card' | 'ssn' | 'ip_address' | 'person' | 'organization' | 'location';
2
+ export interface PiiFinding {
3
+ type: PiiType;
4
+ /** The matched text. NEVER persist or log this — findings summaries are
5
+ * reduced to type/counts/score at every logging boundary. */
6
+ match: string;
7
+ /** Detector confidence in [0,1]. The rejection threshold compares the
8
+ * maximum finding confidence against `memory.redaction.piiThreshold`. */
9
+ confidence: number;
10
+ }
11
+ export interface PiiDetector {
12
+ readonly id: 'regex' | 'ner';
13
+ /** Async because the NER implementation awaits model inference. */
14
+ detect(text: string): Promise<PiiFinding[]>;
15
+ }
16
+ /**
17
+ * Score model (#1466): the max finding confidence. A single high-confidence
18
+ * finding (a Luhn-valid card, an email) is enough to exceed the 0.7 default
19
+ * threshold; low-confidence families (bare IP addresses at 0.5) are reported
20
+ * but do not reject on their own.
21
+ */
22
+ export declare function computePiiScore(findings: PiiFinding[]): number;
23
+ /** Reduce findings to a loggable summary — no matched text leaves this module. */
24
+ export declare function summarizePiiFindings(findings: PiiFinding[]): {
25
+ score: number;
26
+ countsByType: Record<string, number>;
27
+ };
28
+ /** Dependency-free detector (default). */
29
+ export declare class RegexPiiDetector implements PiiDetector {
30
+ readonly id: "regex";
31
+ detect(rawText: string): Promise<PiiFinding[]>;
32
+ }
33
+ /**
34
+ * DI seam (repo convention, cf. `src/db/sqlite-loader.ts:_internals`). Tests
35
+ * inject a fake module loader to exercise both the loaded path and the
36
+ * absent-module typed-error path without installing a ~110MB model. Production
37
+ * code never mutates this.
38
+ */
39
+ export declare const _internals: {
40
+ requireModule: (id: string) => unknown;
41
+ reset: () => void;
42
+ };
43
+ /**
44
+ * Opt-in NER detector. Construction is cheap; the model loads on first
45
+ * `detect()` (memoized in-flight — PR #2310 feedback FB-5: concurrent first
46
+ * calls must share one load, the library does not dedupe internally) and is
47
+ * cached on the instance. Absent peer dependency → typed
48
+ * `MemoryPiiDetectorError` (fail-closed with an install hint).
49
+ */
50
+ export declare class NerPiiDetector implements PiiDetector {
51
+ readonly id: "ner";
52
+ private pipeline;
53
+ private pipelinePromise;
54
+ private loadError;
55
+ /**
56
+ * Eagerly verify module availability without loading the model. Available
57
+ * for callers that want to surface opt-in misconfiguration (e.g. a future
58
+ * doctor/validate surface) before any text is processed; ordinary callers
59
+ * hit the same typed error from the first detect().
60
+ */
61
+ assertAvailable(): void;
62
+ private loadModule;
63
+ private getPipeline;
64
+ detect(text: string): Promise<PiiFinding[]>;
65
+ }
66
+ export declare function createPiiDetector(kind: 'regex' | 'ner'): PiiDetector;
@@ -1,5 +1,5 @@
1
1
  import type { RecallBundle, RecallResultItem } from './types';
2
- export declare function buildRecallPromptBlock(items: RecallResultItem[], tokenBudget: number, generatedAt?: string): {
2
+ export declare function buildRecallPromptBlock(items: RecallResultItem[], tokenBudget: number, generatedAt?: string, bundleId?: string): {
3
3
  promptBlock: string;
4
4
  tokenEstimate: number;
5
5
  items: RecallResultItem[];
@@ -73,6 +73,14 @@ export interface MemoryProvider {
73
73
  readonly name: string;
74
74
  initialize?(): Promise<void>;
75
75
  close?(): Promise<void> | void;
76
+ /**
77
+ * Persist a record. #1466 NOTE: the GATEWAY is the PII write boundary —
78
+ * `MemoryGateway`'s validation funnel runs the configured PII detector
79
+ * and the full rule set before records reach this method. Direct
80
+ * provider-level `upsert` calls (dev tooling, evaluation fixtures,
81
+ * legacy-jsonl migration) intentionally bypass PII enforcement; route
82
+ * user-facing writes through the gateway when the privacy policy matters.
83
+ */
76
84
  upsert(record: MemoryRecord): Promise<MemoryRecord>;
77
85
  appendOutcome?(memoryId: string, event: {
78
86
  id: string;
@@ -87,6 +95,13 @@ export interface MemoryProvider {
87
95
  listRecallUsage?(filter?: MemoryRecallUsageFilter): Promise<MemoryRecallUsageEvent[]>;
88
96
  appendRewardEvent?(event: Omit<MemoryRewardEvent, 'id'>): Promise<void>;
89
97
  listRewardEvents?(filter?: MemoryRewardEventFilter): Promise<MemoryRewardEvent[]>;
98
+ /**
99
+ * #1466: append a gateway-emitted audit event to the provider's event log
100
+ * (`memory_events`, hash-chained on the SQLite provider). Narrowly typed to
101
+ * the PII-rejection operation — provider-internal operations stay internal.
102
+ * Optional: the local-jsonl provider has no event log and simply omits it.
103
+ */
104
+ recordEvent?(operation: 'pii_rejected', targetId: string, reason?: string): Promise<void>;
90
105
  compactMaintenance?(options?: MemoryCompactOptions): Promise<MemoryCompactResult>;
91
106
  list(filter: MemoryListFilter): Promise<MemoryRecord[]>;
92
107
  /**
@@ -4,15 +4,27 @@ export interface SecretFinding {
4
4
  }
5
5
  /**
6
6
  * #1850: Coarse redaction-policy fingerprint. Two cohort members with the same
7
- * count of secret-pattern families and the same `rejectDurableSecrets` setting
8
- * are considered policy-compatible; a mismatch fails closed at link time and on
7
+ * count of secret-pattern families and the same redaction toggles are
8
+ * considered policy-compatible; a mismatch fails closed at link time and on
9
9
  * cohort-root open. This is honest about what it measures (the regex pattern
10
- * count + the durable-rejection toggle) — it is NOT a content hash of stored
10
+ * count + the durable-rejection toggles) — it is NOT a content hash of stored
11
11
  * records. Bump the salt constant when the redaction contract changes
12
- * meaningfully (e.g. adding PII detection per #1466).
12
+ * meaningfully (done for #1466: PII detection settings joined the policy).
13
13
  */
14
- export declare const REDACTION_POLICY_SALT = 1;
15
- export declare function computeRedactionPolicyVersion(rejectDurableSecrets: boolean): number;
14
+ export declare const REDACTION_POLICY_SALT = 2;
15
+ /**
16
+ * #1466: redaction-policy input. `detectPii`/`rejectDurablePii`/`piiDetector`
17
+ * are optional so non-memory callers (e.g. the consensus miner, which applies
18
+ * the default-off policy) keep a one-field call site. Absent PII fields are
19
+ * treated as off/regex — the pre-#1466 behavior.
20
+ */
21
+ export interface RedactionPolicyVersionInput {
22
+ rejectDurableSecrets: boolean;
23
+ detectPii?: boolean;
24
+ rejectDurablePii?: boolean;
25
+ piiDetector?: 'regex' | 'ner';
26
+ }
27
+ export declare function computeRedactionPolicyVersion(policy: RedactionPolicyVersionInput): number;
16
28
  export interface MemoryCohortFingerprintInput {
17
29
  provider: string;
18
30
  redaction_policy_version: number;
@@ -26,10 +38,17 @@ export interface MemoryCohortFingerprintInput {
26
38
  * `memory-cohort-config.json` and read back by every consumer.
27
39
  *
28
40
  * Version 1 is the algorithm below (sorted-key canonical JSON -> sha256 -> first
29
- * 12 hex chars). Files written before this field existed are treated as
41
+ * 12 hex chars). Files written before the field existed are treated as
30
42
  * version 1, which is exactly correct: they were produced by this same
31
43
  * algorithm, so they keep validating with no forced re-link.
32
44
  *
45
+ * Version 2 (#1466): no change to canonicalization, hashing, or truncation —
46
+ * bumped because `redaction_policy_version` changed its value for unchanged
47
+ * user config (REDACTION_POLICY_SALT 1→2 with the PII policy fields joining
48
+ * the input, plus a new secret-pattern family). Per the rule below, a changed
49
+ * digest for an unchanged input is a version bump so legacy files surface as
50
+ * "re-run `/swarm memory link`" instead of a false config-mismatch throw.
51
+ *
33
52
  * BUMP THIS whenever a change would alter the digest for an unchanged input —
34
53
  * e.g. a different hash, a different truncation length, a change to
35
54
  * `stableCanonicalStringify`'s output for a reachable input, or a change to the
@@ -37,7 +56,7 @@ export interface MemoryCohortFingerprintInput {
37
56
  * comparing fingerprints, so a bump surfaces as "re-run `/swarm memory link`"
38
57
  * instead of being misreported as a real provider/embedding config mismatch.
39
58
  */
40
- export declare const FINGERPRINT_ALGORITHM_VERSION = 1;
59
+ export declare const FINGERPRINT_ALGORITHM_VERSION = 2;
41
60
  /**
42
61
  * #2062 F-012 (R3 fix): the algorithm version implied by a persisted cohort
43
62
  * config that has NO `algorithm_version` field. Every file written before the
@@ -117,6 +136,10 @@ export declare function buildMemoryCohortFingerprintInput(config: {
117
136
  provider: string;
118
137
  redaction: {
119
138
  rejectDurableSecrets: boolean;
139
+ /** #1466 PII policy fields (optional — pre-#1466 callers omit them). */
140
+ detectPii?: boolean;
141
+ rejectDurablePii?: boolean;
142
+ piiDetector?: 'regex' | 'ner';
120
143
  };
121
144
  embeddings: {
122
145
  model: string;
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { type PiiFinding } from './pii';
2
3
  import type { CuratorMemoryDecision, MemoryAnchor, MemoryKind, MemoryOutcome, MemoryPatch, MemoryProposal, MemoryRecord, MemoryScopeRef, NewMemoryRecord } from './types';
3
4
  export declare const MAX_MEMORY_TEXT_LENGTH = 2000;
4
5
  export declare const MAX_MEMORY_ANCHORS = 20;
@@ -147,6 +148,10 @@ export declare const MemoryRecordSchema: z.ZodObject<{
147
148
  schemaVersion: z.ZodOptional<z.ZodNumber>;
148
149
  providerVersion: z.ZodOptional<z.ZodString>;
149
150
  sourceRevision: z.ZodOptional<z.ZodString>;
151
+ sourceTaskId: z.ZodOptional<z.ZodString>;
152
+ embeddingModelVersion: z.ZodOptional<z.ZodString>;
153
+ validFrom: z.ZodOptional<z.ZodString>;
154
+ supersedesReason: z.ZodOptional<z.ZodString>;
150
155
  }, z.core.$strict>;
151
156
  export declare const MemoryProposalSchema: z.ZodObject<{
152
157
  id: z.ZodString;
@@ -237,6 +242,10 @@ export declare const MemoryProposalSchema: z.ZodObject<{
237
242
  schemaVersion: z.ZodOptional<z.ZodNumber>;
238
243
  providerVersion: z.ZodOptional<z.ZodString>;
239
244
  sourceRevision: z.ZodOptional<z.ZodString>;
245
+ sourceTaskId: z.ZodOptional<z.ZodString>;
246
+ embeddingModelVersion: z.ZodOptional<z.ZodString>;
247
+ validFrom: z.ZodOptional<z.ZodString>;
248
+ supersedesReason: z.ZodOptional<z.ZodString>;
240
249
  }, z.core.$strict>>;
241
250
  targetMemoryId: z.ZodOptional<z.ZodString>;
242
251
  relatedMemoryIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -290,6 +299,15 @@ export declare function isExpired(record: MemoryRecord, now?: Date): boolean;
290
299
  export declare function hasEvidenceSource(record: MemoryRecord): boolean;
291
300
  export declare function validateMemoryRecordRules(record: MemoryRecord, options: {
292
301
  rejectDurableSecrets: boolean;
302
+ /**
303
+ * #1466: precomputed PII findings for the record text. The DETECTOR is
304
+ * async (NER), so callers run it and pass results in; this function
305
+ * owns the threshold decision so it stays the single enforcement point.
306
+ * Absent findings = no enforcement (JSONL import, maintenance paths).
307
+ */
308
+ piiFindings?: PiiFinding[];
309
+ rejectDurablePii?: boolean;
310
+ piiThreshold?: number;
293
311
  }): MemoryRecord;
294
312
  export declare function validateMemoryProposal(proposal: MemoryProposal): MemoryProposal;
295
313
  export declare function validateCuratorMemoryDecision(decision: unknown): CuratorMemoryDecision;
@@ -13,3 +13,15 @@
13
13
  * the guard can never drift apart.
14
14
  */
15
15
  export declare const MEMORY_RECALL_SENTINEL = "## Retrieved Swarm Memory";
16
+ /**
17
+ * #1466 (DD-14): the unforgeable recall-injection anchor. Every injected
18
+ * recall block embeds the bundle id produced by `createBundleId`
19
+ * (schema.ts — `bundle_<14 digits>_<8 hex>`), and `messagesContainRecall`
20
+ * accepts ONLY that marker (plus the write-banned full sentinel header, for
21
+ * blocks injected by pre-#1466 builds still in flight). Stored memory text
22
+ * containing the literal `bundle_` prefix is rejected at write time
23
+ * (`validateMemoryRecordRules`), so no stored memory can forge the anchor.
24
+ * The regex pins the exact `createBundleId` shape to avoid prose false
25
+ * positives.
26
+ */
27
+ export declare const RECALL_BUNDLE_MARKER_RE: RegExp;
@@ -4,6 +4,52 @@ import { type MemoryOutcomeEvent } from './outcome-events';
4
4
  import type { MemoryCompactOptions, MemoryCompactResult, MemoryProposalStore, MemoryProvider, MemoryRecallUsageEvent, MemoryRecallUsageFilter, MemoryRewardEvent, MemoryRewardEventFilter, MemoryTransaction } from './provider';
5
5
  import type { RecallScoringDiagnostics } from './scoring';
6
6
  import type { AppliedMemoryChange, MemoryAnchor, MemoryListFilter, MemoryOutcome, MemoryProposal, MemoryRecord, RecallRequest, RecallResultItem, ResolvedCuratorMemoryDecision } from './types';
7
+ /** #1466: genesis anchor for the memory_events hash chain (mirrors the
8
+ * knowledge-receipt ledger's GENESIS convention). */
9
+ export declare const EVENT_CHAIN_GENESIS = "GENESIS";
10
+ export declare const MEMORY_EVENTS_CHAIN_HEAD_KEY = "memory_events_chain_head";
11
+ export interface MemoryEventRow {
12
+ id: string;
13
+ operation: string;
14
+ target_id: string;
15
+ reason: string | null;
16
+ timestamp: string;
17
+ event_json: string | null;
18
+ prev_hash: string | null;
19
+ }
20
+ /**
21
+ * #1466: SHA-256 over the canonical serialization of a FULL event row
22
+ * (including its own prev_hash — the chain link is "hash of the entire
23
+ * previous row", so tampering any field of row n breaks row n+1's link).
24
+ * Mirrors `receiptRecordHash` in knowledge-receipt-ledger-storage.ts.
25
+ */
26
+ export declare function memoryEventRowHash(row: MemoryEventRow): string;
27
+ export interface MemoryAuditVerificationReport {
28
+ supported: boolean;
29
+ totalRows: number;
30
+ /** Rows inside the verified hash chain. */
31
+ chainedRows: number;
32
+ /** Rows predating migration v13 (NULL prev_hash prefix) — reported, not verified. */
33
+ legacyRows: number;
34
+ verified: boolean;
35
+ /** First divergence, when the chain is broken. */
36
+ divergence?: {
37
+ rowId: string;
38
+ detail: string;
39
+ };
40
+ /** Chain-head check against _meta — catches last-row tampering. */
41
+ headMatch: boolean | null;
42
+ headExpected?: string;
43
+ headStored?: string | null;
44
+ }
45
+ /**
46
+ * #1466: lazily verify the memory_events hash chain over ordered rows. Pure —
47
+ * takes the already-read rows plus the stored head, returns a report. Used by
48
+ * `/swarm memory audit-verify` and tests. The chain link is "hash of the full
49
+ * previous row (including its prev_hash)", so tampering any field of row n
50
+ * breaks row n+1; the _meta head catches tampering of the LAST row.
51
+ */
52
+ export declare function verifyMemoryEventChainRows(rows: MemoryEventRow[], storedHead: string | null): MemoryAuditVerificationReport;
7
53
  interface Migration {
8
54
  version: number;
9
55
  name: string;
@@ -135,6 +181,47 @@ export declare class SQLiteMemoryProvider implements MemoryProvider, MemoryPropo
135
181
  private runMigrations;
136
182
  private backfillScopeKeys;
137
183
  private backfillRecallRunIds;
184
+ /**
185
+ * #1466 (migration v12 follow-up): backfill `valid_from` for pre-v12 rows
186
+ * from the record's createdAt (fallback: updated_at). source_task_id /
187
+ * agent_role / embedding_model_version keep their ALTER TABLE defaults per
188
+ * the issue's backfill note — migration must not fail on missing data.
189
+ *
190
+ * Implemented as a single JSON1 statement (no per-row loop) and executed
191
+ * via run(): bun's per-Database QUERY cache is strictly bounded (~20
192
+ * statements, bun 1.3.x) and EVICTED entries are never finalized on close
193
+ * — on Windows that keeps memory.db locked after close() until GC. run()
194
+ * does not use that cache; keep new db.query() SQL strings to a minimum.
195
+ */
196
+ private backfillProvenanceColumns;
197
+ /**
198
+ * #1466 (migration v13 follow-up): deterministically chain all existing
199
+ * event rows (first row anchored to GENESIS, row n prev_hash = hash of the
200
+ * full row n-1). Recomputes from row content only — existing prev_hash
201
+ * values are ignored — so a crash mid-backfill heals identically on the
202
+ * next init. The _meta head mirrors the last row's hash so last-row
203
+ * tampering is detectable.
204
+ */
205
+ private backfillEventHashChain;
206
+ /**
207
+ * #1466: `/swarm memory audit-verify` backend. Lazily recomputes the
208
+ * memory_events hash chain and the _meta head. Read-only.
209
+ */
210
+ verifyAuditChain(): Promise<MemoryAuditVerificationReport>;
211
+ /**
212
+ * #1466: true once the memory_events.prev_hash column exists (migration
213
+ * v13 applied). Memoized positive — the column never disappears — so the
214
+ * schema probe runs only until the first success.
215
+ */
216
+ private eventChainReadyCache;
217
+ private eventChainColumnReady;
218
+ /** #1466: gateway-emitted audit events (PII rejection). */
219
+ recordEvent(operation: 'pii_rejected', targetId: string, reason?: string): Promise<void>;
220
+ /**
221
+ * #1466: embedding model version for provenance stamping. Empty when
222
+ * embeddings are not active for this database.
223
+ */
224
+ private embeddingModelVersionStamp;
138
225
  private initializeFtsIndex;
139
226
  private initializeVecExtension;
140
227
  private recreateFtsIndex;
@@ -145,6 +232,8 @@ export declare class SQLiteMemoryProvider implements MemoryProvider, MemoryPropo
145
232
  private loadMemories;
146
233
  private loadProposals;
147
234
  private writeMemory;
235
+ /** Raw row (WITH provenance columns) for a single memory item. */
236
+ private readMemoryItemRowById;
148
237
  private readMemoryById;
149
238
  private readOutcomeEvents;
150
239
  private readOutcomeEventsForMemoryIds;
@@ -185,6 +274,10 @@ export declare class SQLiteMemoryProvider implements MemoryProvider, MemoryPropo
185
274
  private hasOutcomeEvent;
186
275
  private event;
187
276
  private insertEvent;
277
+ /** #1466: see runMigrations / backfillEventHashChain (fresh-DB fast path). */
278
+ private databaseStartedFresh;
279
+ private migrationsComplete;
280
+ private freshInitMigrationEvents;
188
281
  private requireDb;
189
282
  }
190
283
  declare function splitSql(sql: string): string[];
@@ -70,6 +70,15 @@ export interface MemoryRecord {
70
70
  providerVersion?: string;
71
71
  /** Source git revision of the producer, when available. */
72
72
  sourceRevision?: string;
73
+ /** Unit-of-work (plan task) identity that produced the record. Never
74
+ * defaulted to sessionID — see MemoryContext.unitId. */
75
+ sourceTaskId?: string;
76
+ /** Embedding model version at write time (Phase 4 swaps join on this). */
77
+ embeddingModelVersion?: string;
78
+ /** When this memory became authoritative (supersede chains). */
79
+ validFrom?: string;
80
+ /** Why this record superseded its predecessor. */
81
+ supersedesReason?: string;
73
82
  }
74
83
  export interface MemoryProposal {
75
84
  id: string;
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { type BackgroundDelegationRecord, type BackgroundDelegationWorkflowLaneRecovery } from '../background/pending-delegations.js';
3
3
  import { resolveExactMergeBaseAsync, resolvePrWorkflowRevisionDigestAsync } from '../background/workspace-snapshot.js';
4
+ import { loadPluginConfig } from '../config/loader.js';
4
5
  import { type PrReviewDepthTier, validatePrWorkflowTransportRecovery } from '../hooks/pr-workflow-gate.js';
5
6
  import { buildLaneOrientationBlock } from '../hooks/repo-graph-injection.js';
6
7
  import { createParallelDispatcher } from '../parallel/dispatcher/parallel-dispatcher.js';
@@ -90,6 +91,11 @@ declare const DispatchLanesAsyncArgsSchema: z.ZodObject<{
90
91
  evidence: z.ZodString;
91
92
  }, z.core.$strict>], "result">>>;
92
93
  feedback_inventory: z.ZodOptional<z.ZodArray<z.ZodString>>;
94
+ pr_review_wave_stage: z.ZodOptional<z.ZodEnum<{
95
+ canary: "canary";
96
+ fanout: "fanout";
97
+ }>>;
98
+ pr_review_wave_attempt: z.ZodOptional<z.ZodNumber>;
93
99
  }, z.core.$strip>;
94
100
  declare const CollectLaneResultsArgsSchema: z.ZodObject<{
95
101
  batch_id: z.ZodString;
@@ -158,7 +164,7 @@ export interface DispatchLanesResult {
158
164
  }
159
165
  export interface DispatchLanesAsyncResult {
160
166
  success: boolean;
161
- failure_class?: 'invalid_args' | 'no_client';
167
+ failure_class?: 'invalid_args' | 'no_client' | 'circuit_open' | 'retry_exhausted';
162
168
  message?: string;
163
169
  batch_id: string | null;
164
170
  dispatched: number;
@@ -295,6 +301,7 @@ export declare const _internals: {
295
301
  getSessionOps: () => SessionOps | null;
296
302
  getGeneratedAgentNames: () => readonly string[];
297
303
  createParallelDispatcher: typeof createParallelDispatcher;
304
+ loadPluginConfig: typeof loadPluginConfig;
298
305
  resolvePrWorkflowRevisionDigestAsync: typeof resolvePrWorkflowRevisionDigestAsync;
299
306
  resolveExactMergeBaseAsync: typeof resolveExactMergeBaseAsync;
300
307
  validatePrWorkflowTransportRecovery: typeof validatePrWorkflowTransportRecovery;
@@ -386,6 +393,11 @@ export declare const _test_exports: {
386
393
  evidence: z.ZodString;
387
394
  }, z.core.$strict>], "result">>>;
388
395
  feedback_inventory: z.ZodOptional<z.ZodArray<z.ZodString>>;
396
+ pr_review_wave_stage: z.ZodOptional<z.ZodEnum<{
397
+ canary: "canary";
398
+ fanout: "fanout";
399
+ }>>;
400
+ pr_review_wave_attempt: z.ZodOptional<z.ZodNumber>;
389
401
  }, z.core.$strip>;
390
402
  DEFAULT_TIMEOUT_MS: number;
391
403
  DEFAULT_ASYNC_LAUNCH_TIMEOUT_MS: number;
@@ -80,6 +80,19 @@ export interface CoderSettlementWalState {
80
80
  transitionId?: string;
81
81
  processId?: number;
82
82
  recordedAt?: string;
83
+ /**
84
+ * Whether the settlement attributed an accepted mutation. Only meaningful
85
+ * on COMMITTED WALs — the parser enforces the boolean only for PREPARED
86
+ * state (workflow-wal-schema.ts), so consumers must compare `=== true`.
87
+ */
88
+ accepted?: boolean;
89
+ /**
90
+ * The file paths the dispatch declared it would change, as recorded on the
91
+ * WAL at dispatch time. `null`/absent means no scope was declared. Used to
92
+ * bind durable Stage-A attribution to the files a gate-tool call actually
93
+ * scanned, rather than attributing on task-state cardinality alone.
94
+ */
95
+ declaredFiles?: string[] | null;
83
96
  /** The in-process ownership registry still holds this dispatch (toolAfter never drained it). */
84
97
  ownedInProcess: boolean;
85
98
  /** A different, still-alive host process owns the dispatch. Never force-releasable from here. */
@@ -0,0 +1,41 @@
1
+ export type StageARepairOutcome = {
2
+ taskId: string;
3
+ outcome: 'repaired';
4
+ generation: number;
5
+ transitionId: string;
6
+ } | {
7
+ taskId: string;
8
+ outcome: 'skipped_not_wedged';
9
+ state: string;
10
+ } | {
11
+ taskId: string;
12
+ outcome: 'skipped_not_green';
13
+ reason: 'no_pre_check_bundles' | 'pre_check_failed_or_stale';
14
+ } | {
15
+ taskId: string;
16
+ outcome: 'error';
17
+ message: string;
18
+ };
19
+ export interface StageARepairResult {
20
+ results: StageARepairOutcome[];
21
+ truncated: boolean;
22
+ }
23
+ export type PreCheckGreennessResult = {
24
+ green: true;
25
+ } | {
26
+ green: false;
27
+ reason: 'no_pre_check_bundles' | 'pre_check_failed_or_stale';
28
+ };
29
+ /**
30
+ * Repair path for tasks already wedged at `coder_delegated`
31
+ * (TASK_WORKFLOW_STAGE_A_REQUIRED post-reset wedge). For each flat task
32
+ * evidence file whose workflow store sits at `coder_delegated` with no
33
+ * pre_check gate proof AND a green post-settlement pre-check bundle, emits
34
+ * the missing `stage_a_passed` transition directly and appends an audit
35
+ * event. Never re-runs the coder; live DISPATCHED/PREPARED settlement WALs
36
+ * refuse the transition loudly (CODER_SETTLEMENT_IN_PROGRESS) and surface as
37
+ * per-task errors without blocking siblings.
38
+ */
39
+ export declare function repairWedgedStageA(directory: string, options?: {
40
+ taskIds?: string[];
41
+ }): Promise<StageARepairResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.145.2",
3
+ "version": "7.146.1",
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",
@@ -108,9 +108,11 @@
108
108
  "repro:1144": "bun scripts/repro-1144.mjs",
109
109
  "repro:1873": "bun build scripts/repro-1873-entry.ts --outdir dist-build-test/repro-1873 --target node --format esm && node scripts/repro-1873.mjs",
110
110
  "check:events": "bun run scripts/check-event-contract.ts",
111
+ "check:retention": "bun run scripts/check-retention-registry.ts",
111
112
  "check:test-file-cap": "bun run scripts/check-test-file-cap.ts",
112
113
  "check:gate-portability": "bun run scripts/check-gate-portability.ts",
113
- "check:bare-spawn": "bun run scripts/check-bare-executable-spawn.ts"
114
+ "check:bare-spawn": "bun run scripts/check-bare-executable-spawn.ts",
115
+ "check:memory-recall": "bun run scripts/memory-recall-regression.ts"
114
116
  },
115
117
  "dependencies": {
116
118
  "@opencode-ai/plugin": "^1.18.3",
@@ -130,5 +132,13 @@
130
132
  "bun-types": "1.3.8",
131
133
  "js-yaml": "^4.1.1",
132
134
  "typescript": "^5.7.3"
135
+ },
136
+ "peerDependencies": {
137
+ "@xenova/transformers": "^2.17.2"
138
+ },
139
+ "peerDependenciesMeta": {
140
+ "@xenova/transformers": {
141
+ "optional": true
142
+ }
133
143
  }
134
144
  }