opencode-swarm 7.122.1 → 7.124.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 (99) hide show
  1. package/.opencode/skills/issue-tracer/SKILL.md +341 -0
  2. package/.opencode/skills/issue-tracer/assets/pr-template.md +59 -0
  3. package/.opencode/skills/issue-tracer/references/critic-gate.md +245 -0
  4. package/.opencode/skills/issue-tracer/references/evidence-artifacts.md +267 -0
  5. package/.opencode/skills/issue-tracer/references/install.md +63 -0
  6. package/.opencode/skills/issue-tracer/references/localization-playbook.md +95 -0
  7. package/.opencode/skills/issue-tracer/references/method-provenance.md +13 -0
  8. package/.opencode/skills/issue-tracer/references/untrusted-content.md +22 -0
  9. package/.opencode/skills/issue-tracer/scripts/scan-deferred.sh +103 -0
  10. package/.opencode/skills/issue-tracer/scripts/trace-init.sh +119 -0
  11. package/README.md +3 -0
  12. package/dist/cli/{config-doctor-3ksh1534.js → config-doctor-53mnpeke.js} +6 -6
  13. package/dist/cli/{core-cwgb7ev8.js → core-d38hc85b.js} +4 -4
  14. package/dist/cli/{curation-policy-4j4qce5a.js → curation-policy-ny033330.js} +2 -2
  15. package/dist/cli/{curator-drift-6b2fjsgr.js → curator-drift-h9a2sjvf.js} +7 -7
  16. package/dist/cli/curator-llm-factory-e0tjz8qq.js +53 -0
  17. package/dist/cli/{curator-qfh07497.js → curator-nvn1pdq6.js} +30 -29
  18. package/dist/cli/{dispatch-07jgc3hn.js → dispatch-f4hj1cj7.js} +3 -3
  19. package/dist/cli/{evidence-summary-service-96ndczbk.js → evidence-summary-service-9hq7n9k9.js} +12 -12
  20. package/dist/cli/{gate-evidence-xqks3kk2.js → gate-evidence-cy2te3x8.js} +2 -2
  21. package/dist/cli/guardrail-explain-d3dd57nf.js +54 -0
  22. package/dist/cli/guardrail-log-3q2z9axs.js +16 -0
  23. package/dist/cli/{hive-promoter-00mktxkk.js → hive-promoter-9gbpepgm.js} +30 -29
  24. package/dist/cli/index-134d35c1.js +116 -0
  25. package/dist/cli/{index-cqsak7bf.js → index-13d2g7k9.js} +1 -1
  26. package/dist/cli/{index-m1abzg93.js → index-1kz6da87.js} +4 -2
  27. package/dist/cli/{index-22e127jw.js → index-23zsx7dm.js} +5 -3
  28. package/dist/cli/{index-kzj3fp7c.js → index-2445sx3d.js} +1 -1
  29. package/dist/cli/{index-ctpgsj84.js → index-2tn5h2zp.js} +1 -1
  30. package/dist/cli/{index-n2712byd.js → index-2x4maxte.js} +8 -5
  31. package/dist/cli/{index-6xnxz6z0.js → index-3x761jqv.js} +4 -2
  32. package/dist/cli/{index-3rr0ese8.js → index-3yyr4ynk.js} +41 -9
  33. package/dist/cli/{index-7cspygmb.js → index-4rzzzjtg.js} +11 -8
  34. package/dist/cli/{index-nprawcc0.js → index-6n2jpz7x.js} +1 -1
  35. package/dist/cli/{index-jm81nhks.js → index-8pz0ypxz.js} +38 -31
  36. package/dist/cli/index-bk5tah7q.js +19 -0
  37. package/dist/cli/{index-293f68mj.js → index-bpmtbmy9.js} +4680 -4342
  38. package/dist/cli/index-c6crt5rs.js +2494 -0
  39. package/dist/cli/{index-19zn67q6.js → index-e19mxn9f.js} +5 -1
  40. package/dist/cli/{index-hns7xjm7.js → index-eeg04r0x.js} +4 -2
  41. package/dist/cli/{index-ye3fndsw.js → index-gacm5s2n.js} +9 -5
  42. package/dist/cli/{index-d6h5hq91.js → index-gnvg72y1.js} +2 -2
  43. package/dist/cli/{index-17eybc3z.js → index-htbjqftb.js} +1 -1
  44. package/dist/cli/{index-vcgfqd8f.js → index-k5jrywpr.js} +4 -2
  45. package/dist/cli/{index-kanrv3sv.js → index-mkkx1a4f.js} +2436 -1435
  46. package/dist/cli/{index-gnp9x6se.js → index-n832052r.js} +4 -2
  47. package/dist/cli/{index-6srk9a5q.js → index-qd77aft9.js} +2 -2
  48. package/dist/cli/{index-816rkp4y.js → index-s9mj4rb9.js} +5 -3
  49. package/dist/cli/{index-5he0kwnx.js → index-tyg0zezw.js} +13 -8
  50. package/dist/cli/{index-gw3m5fw0.js → index-v2rdhwx8.js} +5 -3
  51. package/dist/cli/{index-det5jwfk.js → index-v5ecczts.js} +5 -1
  52. package/dist/cli/{index-2j25br0p.js → index-wqwpzhv1.js} +9 -9
  53. package/dist/cli/{index-dywekeen.js → index-x2mmnrnx.js} +1 -1
  54. package/dist/cli/{index-j9kxwshw.js → index-z0m4angq.js} +8 -6
  55. package/dist/cli/{index-5e4e2hvv.js → index-z6xqpmqg.js} +4 -0
  56. package/dist/cli/{index-m0bmh3w0.js → index-zjedv2bd.js} +2 -2
  57. package/dist/cli/{index-p0arc26j.js → index-zjygnfay.js} +7 -2
  58. package/dist/cli/index.js +33 -30
  59. package/dist/cli/{knowledge-escalator-dyxsm78k.js → knowledge-escalator-r1s01m3z.js} +4 -4
  60. package/dist/cli/{knowledge-events-36q6nvae.js → knowledge-events-a9hmxvke.js} +1 -1
  61. package/dist/cli/{knowledge-store-e07b4s54.js → knowledge-store-jt31br5k.js} +2 -2
  62. package/dist/cli/{knowledge-validator-sfyf7wn2.js → knowledge-validator-9vrwxr3z.js} +5 -5
  63. package/dist/cli/{pending-delegations-68cbaqv2.js → pending-delegations-e3bcqj77.js} +7 -5
  64. package/dist/cli/{pr-subscriptions-x653151h.js → pr-subscriptions-bxpqde8w.js} +5 -5
  65. package/dist/cli/{scan-cursor-hm0aqqae.js → scan-cursor-bkjtsawg.js} +3 -3
  66. package/dist/cli/{schema-13bnng2n.js → schema-y3z416z7.js} +7 -4
  67. package/dist/cli/{scope-persistence-d9erwnvb.js → scope-persistence-habn4q8a.js} +4 -4
  68. package/dist/cli/{skill-generator-g6r52x94.js → skill-generator-794hyndw.js} +7 -7
  69. package/dist/cli/sqlite-loader-r1kgt2pq.js +12 -0
  70. package/dist/commands/memory-link.d.ts +28 -0
  71. package/dist/commands/registry.d.ts +25 -0
  72. package/dist/config/bundled-skills.d.ts +1 -1
  73. package/dist/config/schema.d.ts +6 -0
  74. package/dist/index.js +331 -318
  75. package/dist/knowledge/family-migration-shared.d.ts +24 -0
  76. package/dist/knowledge/family-migration.d.ts +5 -0
  77. package/dist/memory/config.d.ts +11 -0
  78. package/dist/memory/consolidation-log.d.ts +8 -2
  79. package/dist/memory/finalize-reward-sweep.d.ts +2 -1
  80. package/dist/memory/gateway.d.ts +13 -0
  81. package/dist/memory/index.d.ts +4 -2
  82. package/dist/memory/local-jsonl-provider.d.ts +25 -1
  83. package/dist/memory/memory-family-manifest.d.ts +52 -0
  84. package/dist/memory/memory-family-migration.d.ts +94 -0
  85. package/dist/memory/memory-link.d.ts +103 -0
  86. package/dist/memory/provider-pool.d.ts +35 -0
  87. package/dist/memory/redaction.d.ts +31 -0
  88. package/dist/memory/schema.d.ts +21 -0
  89. package/dist/memory/sqlite-provider.d.ts +58 -1
  90. package/dist/memory/storage-root.d.ts +93 -0
  91. package/dist/memory/types.d.ts +20 -1
  92. package/dist/services/knowledge-diagnostics.d.ts +25 -0
  93. package/dist/services/status-service.d.ts +16 -0
  94. package/package.json +2 -1
  95. package/dist/cli/curator-llm-factory-x769hn66.js +0 -52
  96. package/dist/cli/guardrail-explain-kj0e9r25.js +0 -53
  97. package/dist/cli/guardrail-log-z291m4gw.js +0 -16
  98. package/dist/cli/index-4rhcth7a.js +0 -14
  99. package/dist/cli/index-wr8qgpz5.js +0 -2463
@@ -0,0 +1,24 @@
1
+ /**
2
+ * #1850: shared lock config for family-migration engines.
3
+ *
4
+ * Both the knowledge family (`src/knowledge/family-migration.ts`) and the
5
+ * memory family (`src/memory/memory-family-migration.ts`) use the SAME lock
6
+ * discipline. Centralizing it here ensures one source of truth — a change to
7
+ * the stale window or retry policy applies to both subsystems.
8
+ */
9
+ /**
10
+ * Lock stale window for the migration critical section. The default
11
+ * knowledge-store `stale: 5000` (5 s) is too short for an 8+ file merge under
12
+ * load; a long merge would have its lock stolen by a concurrent writer
13
+ * mid-flight (issue #1846 critic C9). 30 s comfortably covers worst-case
14
+ * merges of large corpora.
15
+ */
16
+ export declare const MIGRATION_LOCK_STALE_MS = 30000;
17
+ /** Retry config for acquiring the migration lock. */
18
+ export declare const MIGRATION_LOCK_RETRIES: {
19
+ readonly retries: {
20
+ readonly retries: 10;
21
+ readonly minTimeout: 100;
22
+ readonly maxTimeout: 500;
23
+ };
24
+ };
@@ -28,6 +28,11 @@
28
28
  import type { CounterRollup } from '../hooks/knowledge-events.js';
29
29
  import type { KnowledgeEntryBase } from '../hooks/knowledge-types.js';
30
30
  import { type KnowledgeFamilyMember } from './family-manifest.js';
31
+ /**
32
+ * Lock config for the migration critical section. Re-exported from
33
+ * `family-migration-shared.ts` (issue #1850) so the memory family migration
34
+ * engine reuses the SAME values. See the shared module for rationale.
35
+ */
31
36
  export interface FamilyMigrationCounts {
32
37
  /** Per-member counts (filename → {merged, skipped}). */
33
38
  readonly perMember: ReadonlyArray<{
@@ -38,6 +38,17 @@ export interface MemoryConfig {
38
38
  /** Reflection / consolidation pass (issue #1464, Phase 3). */
39
39
  consolidation: ConsolidationConfig;
40
40
  hardDelete: boolean;
41
+ /**
42
+ * #1850 Linked Knowledge 5/5: opt-in cohort memory sharing. Default off —
43
+ * memory stays worktree-local (today's behavior) unless explicitly enabled.
44
+ * When enabled AND a memory-link pointer is present, repository-scoped
45
+ * memory is shared across sibling worktrees through the #1846 cohort
46
+ * identity. Knowledge link and memory link are independent toggles: linking
47
+ * knowledge does not imply linking memory.
48
+ */
49
+ link: {
50
+ enabled: boolean;
51
+ };
41
52
  embeddings: {
42
53
  enabled: boolean;
43
54
  model: string;
@@ -1,9 +1,15 @@
1
+ import type { VettedMemoryRoot } from './storage-root';
1
2
  /**
2
3
  * Durable, append-only record of a completed consolidation pass. Persisted to
3
4
  * `.swarm/memory/consolidation-log.jsonl` (invariant #4: runtime state stays
4
5
  * under `.swarm/`). Serves two purposes:
5
6
  * - idempotency: a pass for a `phaseNumber` already present here is a no-op;
6
7
  * - observability: the `/swarm memory consolidation-log` CLI reads it.
8
+ *
9
+ * #1850 (critic GAP-3): when cohort sharing is active, the log follows the
10
+ * cohort root (acceptance #4 — consolidation state is part of the vetted-root
11
+ * surface). Callers may pass a `VettedMemoryRoot` to redirect; a raw directory
12
+ * string preserves today's local behavior.
7
13
  */
8
14
  export interface ConsolidationLogRecord {
9
15
  phaseNumber: number;
@@ -25,5 +31,5 @@ export interface ConsolidationLogRecord {
25
31
  errored: number;
26
32
  processedProposalIds: string[];
27
33
  }
28
- export declare function readConsolidationLog(directory: string): Promise<ConsolidationLogRecord[]>;
29
- export declare function appendConsolidationLog(directory: string, record: ConsolidationLogRecord): Promise<void>;
34
+ export declare function readConsolidationLog(target: string | VettedMemoryRoot): Promise<ConsolidationLogRecord[]>;
35
+ export declare function appendConsolidationLog(target: string | VettedMemoryRoot, record: ConsolidationLogRecord): Promise<void>;
@@ -81,7 +81,7 @@
81
81
  * call below).
82
82
  */
83
83
  import type { MemoryConfig } from '../config/schema';
84
- import { createConfiguredMemoryProvider } from './gateway';
84
+ import { createConfiguredMemoryProvider, createConfiguredMemoryProviderForRoot } from './gateway';
85
85
  import { applyCouncilReward } from './reward-capture';
86
86
  /**
87
87
  * Negative terminal reward for memories recalled into non-completed
@@ -128,5 +128,6 @@ export declare function runFinalizeRewardSweep(args: FinalizeRewardSweepArgs): P
128
128
  */
129
129
  export declare const _internals: {
130
130
  createConfiguredMemoryProvider: typeof createConfiguredMemoryProvider;
131
+ createConfiguredMemoryProviderForRoot: typeof createConfiguredMemoryProviderForRoot;
131
132
  applyCouncilReward: typeof applyCouncilReward;
132
133
  };
@@ -1,5 +1,6 @@
1
1
  import { type MemoryConfig } from './config';
2
2
  import type { MemoryProposalStore, MemoryProvider, MemoryRecallUsageEvent, MemoryRecallUsageFilter } from './provider';
3
+ import { type VettedMemoryRoot } from './storage-root';
3
4
  import type { AppliedMemoryChange, CuratorMemoryDecision, MemoryContext, MemoryKind, MemoryListFilter, MemoryProposal, MemoryRecord, MemoryScopeRef, MemorySource, RecallBundle, RecallMode } from './types';
4
5
  export interface MemoryGatewayOptions {
5
6
  config?: Partial<MemoryConfig>;
@@ -31,6 +32,12 @@ export declare class MemoryGateway {
31
32
  private readonly context;
32
33
  private readonly config;
33
34
  private readonly provider;
35
+ /**
36
+ * #1850: the resolved vetted memory root. Used by `deriveAllowedScopes` to
37
+ * emit a cohort scope when cohort sharing is active, and by `propose` to
38
+ * stamp provenance. Resolved once at construction (sync pointer read).
39
+ */
40
+ private readonly vettedRoot;
34
41
  private readonly now;
35
42
  private disposed;
36
43
  constructor(context: MemoryContext, options?: MemoryGatewayOptions);
@@ -71,3 +78,9 @@ export declare class MemoryGateway {
71
78
  }
72
79
  export declare function createMemoryGateway(context: MemoryContext, options?: MemoryGatewayOptions): MemoryGateway;
73
80
  export declare function createConfiguredMemoryProvider(directory: string, config: MemoryConfig): MemoryProvider & MemoryProposalStore;
81
+ /**
82
+ * #1850: cohort-aware provider factory. Takes a resolved `VettedMemoryRoot` so
83
+ * callers that have already resolved (e.g. the finalize-reward sweep) skip the
84
+ * redundant pointer read. Both overloads converge here.
85
+ */
86
+ export declare function createConfiguredMemoryProviderForRoot(root: VettedMemoryRoot, config: MemoryConfig): MemoryProvider & MemoryProposalStore;
@@ -3,17 +3,19 @@ export { DEFAULT_MEMORY_CONFIG, resolveMemoryConfig } from './config';
3
3
  export { MemoryDisabledError, 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, } from './gateway';
6
- export { createConfiguredMemoryProvider, createMemoryGateway, MemoryGateway, } from './gateway';
6
+ export { createConfiguredMemoryProvider, createConfiguredMemoryProviderForRoot, createMemoryGateway, MemoryGateway, } from './gateway';
7
7
  export { createMemoryLifecycleHooks, type MemoryLifecycleHookOptions, type MemoryLifecycleHooks, } from './injector';
8
8
  export { backupLegacyJsonl, getLegacyJsonlFileStatus, type JsonlBackupResult, type JsonlImportPayload, type JsonlInvalidRow, type JsonlMigrationReport, LEGACY_JSONL_MIGRATION_NAME, LEGACY_JSONL_MIGRATION_VERSION, readLegacyJsonl, readMigrationReport, resolveMemoryStorageDir, resolveSqliteDatabasePath, writeJsonlExport, writeMigrationReport, } from './jsonl-migration';
9
9
  export { LocalJsonlMemoryProvider } from './local-jsonl-provider';
10
10
  export { buildMemoryMaintenanceReport, type MemoryMaintenanceReport, type MemoryMaintenanceReportOptions, type MemoryRecallUsageByMemory, type MemoryRecallUsageByRole, type MemorySupersededChain, shouldCompactMemory, } from './maintenance';
11
+ export { invalidateMemoryStoreDirCache, isMemoryLinked, MEMORY_LINK_POINTER_FILENAME, type MemoryLinkPointer, readMemoryLinkPointer, removeMemoryLinkPointer, resolveMemoryStoreDir, writeMemoryLinkPointer, } from './memory-link';
11
12
  export { buildRecallPromptBlock } from './prompt-block';
12
13
  export type { MemoryCompactOptions, MemoryCompactResult, MemoryProposalStore, MemoryProvider, MemoryRecallUsageEvent, MemoryRecallUsageFilter, } from './provider';
13
14
  export { buildMemoryRecallPlan, type MemoryRecallPlan, type MemoryRecallPlannerInput, } from './recall-planner';
14
- export { findSecrets, redactSecrets } from './redaction';
15
+ export { buildMemoryCohortFingerprintInput, computeMemoryCohortFingerprint, computeRedactionPolicyVersion, findSecrets, redactSecrets, } from './redaction';
15
16
  export { MEMORY_RECALL_PROFILES, type MemoryRecallProfile, normalizeMemoryAgentRole, resolveMemoryRecallProfile, } from './role-profiles';
16
17
  export { appendMemoryRunLog, sanitizeRunId } from './run-log';
17
18
  export { computeMemoryContentHash, createBundleId, createMemoryId, createProposalId, isExpired, normalizeMemoryText, validateCuratorMemoryDecision, validateMemoryProposal, validateMemoryRecordRules, } from './schema';
18
19
  export { SQLiteMemoryProvider } from './sqlite-provider';
20
+ export { isCohortRoot, isLocalRoot, resolveVettedMemoryRoot, rootStoragePath, type VettedMemoryRoot, wrapLocalRoot, } from './storage-root';
19
21
  export type { AppliedMemoryChange, CuratorMemoryDecision, MemoryContext, MemoryKind, MemoryListFilter, MemoryPatch, MemoryProposal, MemoryRecord, MemoryScopeRef, MemoryScopeType, NewMemoryRecord, RecallBundle, RecallInjectionSkipReason, RecallMode, RecallRequest, RecallResultItem, ResolvedCuratorMemoryDecision, } from './types';
@@ -5,11 +5,22 @@ import type { AppliedMemoryChange, MemoryListFilter, MemoryProposal, MemoryRecor
5
5
  export declare class LocalJsonlMemoryProvider implements MemoryProvider, MemoryProposalStore {
6
6
  readonly name = "local-jsonl";
7
7
  private readonly rootDirectory;
8
+ /**
9
+ * #1850: when set, the provider serves a cohort-shared store. Cohort roots
10
+ * bypass `validateSwarmPath` (constructed by the resolver from a sanitized
11
+ * linkId). `null` for local-root providers (today's behavior).
12
+ */
13
+ private readonly cohortRoot;
8
14
  private readonly config;
9
15
  private initialized;
10
16
  private memories;
11
17
  private proposals;
12
- constructor(rootDirectory: string, config?: Partial<MemoryConfig>);
18
+ constructor(rootDirectory: string, config?: Partial<MemoryConfig>, vettedCohortRoot?: string | null);
19
+ /**
20
+ * #1850 (critic GAP-5): explicit cohort-root path branch.
21
+ * - cohort root → `<cohortRoot>/<filename>` (NO validateSwarmPath).
22
+ * - local root → existing behavior: strip `.swarm/`, validateSwarmPath.
23
+ */
13
24
  private pathFor;
14
25
  initialize(): Promise<void>;
15
26
  upsert(record: MemoryRecord): Promise<MemoryRecord>;
@@ -34,6 +45,19 @@ export declare class LocalJsonlMemoryProvider implements MemoryProvider, MemoryP
34
45
  compact(): Promise<void>;
35
46
  compactMaintenance(options?: MemoryCompactOptions): Promise<MemoryCompactResult>;
36
47
  private audit;
48
+ /**
49
+ * #1850 (critic CONCERN-1): bump the cohort generation marker so sibling
50
+ * worktrees observe this write on their next revalidation. Called at the
51
+ * provider layer so ALL write paths invalidate peers. Local writes are
52
+ * no-ops. Best-effort: a bump failure must never block the write.
53
+ */
54
+ private bumpCohortGeneration;
55
+ /**
56
+ * #1850 (KC-001 fix): mirror the SQLite provider's fingerprint check. Reads
57
+ * `memory-cohort-config.json` and throws on mismatch. Absent/malformed file
58
+ * is permissive (fail-open never strands memory).
59
+ */
60
+ private assertCohortConfigFingerprint;
37
61
  private activeMemory;
38
62
  private validateDecisionMemory;
39
63
  }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * #1850 Linked Knowledge 5/5: memory family manifest.
3
+ *
4
+ * One authoritative inventory of every memory artifact, classified by scope
5
+ * and merge strategy. Drives `migrateMemoryFamily` so a new artifact cannot be
6
+ * silently omitted from link/unlink migration (issue #1850 acceptance #7).
7
+ *
8
+ * Mirrors the structure of `KNOWLEDGE_FAMILY` in
9
+ * `src/knowledge/family-manifest.ts` but is memory-specific. The two
10
+ * manifests are deliberately separate: knowledge uses text/JSONL merge
11
+ * strategies; memory has a binary SQLite blob whose merge strategy is
12
+ * fundamentally different (ATTACH + INSERT OR IGNORE).
13
+ *
14
+ * Scope classifications:
15
+ * - `canonical` — data that must be migrated to preserve cohort state.
16
+ * - `derived` — rebuildable state (FTS shadow tables, vec0 index,
17
+ * embedding cache). Travels inside `memory.db` for SQLite;
18
+ * rebuilt on first open if missing. Not migrated separately.
19
+ *
20
+ * Merge strategies:
21
+ * - `sqlite-file-copy` — whole-DB file copy under exclusive lock; for
22
+ * non-empty destinations, ATTACH + INSERT OR IGNORE
23
+ * keyed by record id (critic CONCERN-6).
24
+ * - `append-union` — id-keyed union of append-only JSONL (reuses the
25
+ * knowledge-family pattern).
26
+ * - `skip` — not migrated (derived state rebuilt on open).
27
+ */
28
+ export type MemoryFamilyMergeStrategy = 'sqlite-file-copy' | 'append-union' | 'skip';
29
+ export type MemoryFamilyScope = 'canonical' | 'derived';
30
+ export interface MemoryFamilyMember {
31
+ filename: string;
32
+ scope: MemoryFamilyScope;
33
+ mergeStrategy: MemoryFamilyMergeStrategy;
34
+ /** Human-readable note for diagnostics. */
35
+ note?: string;
36
+ }
37
+ /**
38
+ * The canonical memory family manifest. Adding a member here automatically
39
+ * includes it in link + unlink migration (mirrors `KNOWLEDGE_FAMILY`).
40
+ *
41
+ * NOTE: for SQLite-provider cohort members, only `memory.db` is migrated as a
42
+ * binary blob; the JSONL members are migrated ONLY when a cohort member uses
43
+ * the `local-jsonl` provider (rare — JSONL is legacy/debug mode per schema).
44
+ * The migration engine skips JSONL members when the source uses SQLite, and
45
+ * vice versa, so a mixed-provider cohort fails closed with a diagnostic
46
+ * (acceptance #10) rather than silently losing data.
47
+ */
48
+ export declare const MEMORY_FAMILY: readonly MemoryFamilyMember[];
49
+ /** Filenames that are actually migrated (not skipped). */
50
+ export declare const MIGRATED_MEMORY_FILENAMES: readonly string[];
51
+ /** Look up a member by filename. */
52
+ export declare function findMemoryFamilyMember(filename: string): MemoryFamilyMember | undefined;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * #1850 Linked Knowledge 5/5: cohort memory family migration engine.
3
+ *
4
+ * Drives both `/swarm memory link` and `/swarm memory unlink` off the single
5
+ * {@link MEMORY_FAMILY} manifest. Mirrors the architecture of
6
+ * `src/knowledge/family-migration.ts` (issue #1846) but is memory-specific:
7
+ *
8
+ * - JSONL members use `append-union` (id-keyed, idempotent on retry) — the
9
+ * same primitive the knowledge family uses.
10
+ * - The SQLite `memory.db` uses `sqlite-file-copy`: under an exclusive lock,
11
+ * checkpoint+close the source, copy the file into a staging path, validate
12
+ * by opening read-only and counting rows, then either (a) atomically rename
13
+ * into an empty destination, or (b) for a non-empty destination, ATTACH the
14
+ * staged DB and `INSERT OR IGNORE` keyed by id (critic CONCERN-6).
15
+ *
16
+ * All-or-nothing commit (issue #1850 acceptance #7): stage → validate →
17
+ * commit. The memory-link pointer is flipped LAST by the caller
18
+ * (`handleMemoryLinkCommand`), so a mid-migration failure leaves the worktree
19
+ * in its prior link state and a retry is idempotent.
20
+ *
21
+ * Lock discipline: `proper-lockfile` on the destination cohort dir with a
22
+ * bumped `stale` (30s) for the migration critical section — reused from the
23
+ * knowledge family so there is one source of truth for the lock config.
24
+ *
25
+ * No writes happen here on the plugin-init path (invariant 1). Called only
26
+ * from the `/swarm memory link` / `/swarm memory unlink` command handlers.
27
+ */
28
+ import type { VettedMemoryRoot } from './storage-root.js';
29
+ export interface MemoryFamilyMigrationCounts {
30
+ readonly perMember: ReadonlyArray<{
31
+ filename: string;
32
+ merged: number;
33
+ skipped: number;
34
+ }>;
35
+ }
36
+ /** Id-keyed union of two append-only JSONL arrays (idempotent on retry). */
37
+ declare function appendUnionById<T>(destination: T[], source: T[]): {
38
+ merged: T[];
39
+ added: number;
40
+ skipped: number;
41
+ };
42
+ /** Serialize a merged JSONL member. */
43
+ declare function serializeJsonl(values: unknown[]): string;
44
+ /** Validate a serialized JSONL member: every line must parse and carry an id. */
45
+ declare function validateSerializedJsonl(serialized: string): boolean;
46
+ /**
47
+ * Copy the SQLite DB file (+ non-empty WAL/SHM sidecars) from source to a
48
+ * staging path under the destination directory. The caller MUST have drained
49
+ * and closed the source provider first (the command handler does this via
50
+ * `evictAndCloseForRoot`).
51
+ *
52
+ * Returns the staging path, or null if the source DB does not exist.
53
+ */
54
+ declare function stageSqliteDb(sourceStoragePath: string, destDir: string): {
55
+ stagedPath: string;
56
+ } | null;
57
+ /**
58
+ * Merge a staged SQLite DB into the destination. If the destination has no
59
+ * existing DB, atomically rename the staged file into place. If the
60
+ * destination has an existing DB, ATTACH the staged DB and INSERT OR IGNORE
61
+ * rows keyed by id (critic CONCERN-6 — two worktrees linking concurrently
62
+ * must not overwrite each other's data).
63
+ *
64
+ * NOTE: this function does NOT open the DB via the provider (that would
65
+ * re-enter the pool). It uses a minimal direct-open to copy rows. The
66
+ * destination provider will re-open and rebuild FTS/vec on next access.
67
+ */
68
+ declare function mergeStagedSqlite(destStoragePath: string, stagedPath: string): Promise<{
69
+ merged: number;
70
+ skipped: number;
71
+ }>;
72
+ /** Best-effort row count via a direct DB open (does not enter the pool). */
73
+ declare function countRowsSafe(dbPath: string): Promise<number>;
74
+ /**
75
+ * Migrate the complete memory family from `sourceRoot` into `destRoot`,
76
+ * merging each member according to its manifest strategy. All-or-nothing:
77
+ * stage → validate → commit. The memory-link pointer is NOT touched here
78
+ * (caller flips it).
79
+ *
80
+ * @param destRoot the cohort root that absorbs the merge (link → cohort;
81
+ * unlink → local).
82
+ * @param sourceRoot the root whose family is merged in (link → local;
83
+ * unlink → cohort).
84
+ */
85
+ export declare function migrateMemoryFamily(destRoot: VettedMemoryRoot, sourceRoot: VettedMemoryRoot): Promise<MemoryFamilyMigrationCounts>;
86
+ export declare const _internals: {
87
+ appendUnionById: typeof appendUnionById;
88
+ serializeJsonl: typeof serializeJsonl;
89
+ validateSerializedJsonl: typeof validateSerializedJsonl;
90
+ stageSqliteDb: typeof stageSqliteDb;
91
+ mergeStagedSqlite: typeof mergeStagedSqlite;
92
+ countRowsSafe: typeof countRowsSafe;
93
+ };
94
+ export {};
@@ -0,0 +1,103 @@
1
+ /**
2
+ * #1850 Linked Knowledge 5/5: memory link pointer + resolution cache.
3
+ *
4
+ * Mirrors `src/hooks/knowledge-link.ts` for the memory subsystem. The pointer
5
+ * lives at `<directory>/.swarm/memory-link.json` — a SEPARATE pointer from the
6
+ * knowledge `link.json`. This deliberate split satisfies issue #1850 acceptance
7
+ * #1 (memory sharing is independently opt-in) and #2 (status distinguishes
8
+ * knowledge link from memory link). Both pointers carry the same canonical
9
+ * cohort id from #1846 — they identify the same cohort — but toggling one does
10
+ * not toggle the other.
11
+ *
12
+ * The shared cohort store lives at `<dataDir>/links/<linkId>/` (reusing
13
+ * `resolveLinkBaseDir` / `resolveLinkDir` from `knowledge-link.ts`). Memory
14
+ * occupies the `memory/` subdirectory of that cohort container, keeping
15
+ * knowledge and memory family files siblings without filename collisions.
16
+ *
17
+ * Reuses from `knowledge-link.ts`:
18
+ * - `resolveLinkBaseDir`, `resolveLinkDir`, `sanitizeLinkId` — the cohort
19
+ * directory layout and id sanitization are shared infrastructure (one
20
+ * cohort container per cohort id).
21
+ *
22
+ * Does NOT duplicate the platform data-dir logic (Windows LOCALAPPDATA, macOS
23
+ * Application Support, linux XDG) — that lives once in `knowledge-link.ts`.
24
+ *
25
+ * Cross-process revalidation (issue #1850 §6 "bounded revalidation window"):
26
+ * like the knowledge resolver, a cache hit re-stats the pointer file and
27
+ * invalidates on `mtimeMs:ctimeMs:size` change, so a sibling worktree's
28
+ * `/swarm memory link` / `/swarm memory unlink` is observed without restart
29
+ * and without waiting for the TTL backstop.
30
+ */
31
+ import { resolveLinkBaseDir, resolveLinkDir, sanitizeLinkId } from '../hooks/knowledge-link.js';
32
+ /** On-disk pointer at `<directory>/.swarm/memory-link.json`. */
33
+ export interface MemoryLinkPointer {
34
+ /** Pointer schema version. */
35
+ version: 1 | 2;
36
+ /** Sanitized link id (cohort directory segment). */
37
+ linkId: string;
38
+ /** Human-friendly name when the link was created from an explicit name. */
39
+ name?: string;
40
+ /** ISO 8601 creation timestamp. */
41
+ createdAt: string;
42
+ /** Canonical cohort id from `resolveCohortId` (issue #1846). */
43
+ cohortId?: string;
44
+ /** How the cohort id was derived. */
45
+ identitySource?: 'remote' | 'git-common-dir' | 'path';
46
+ /** True when the cohort id is machine-local (not portable). */
47
+ degraded?: boolean;
48
+ /** Memory cohort config fingerprint (provider/schema/embedding/redaction). */
49
+ configFingerprint?: string;
50
+ /** Monotonic generation counter, bumped on each link/unlink. */
51
+ generation?: number;
52
+ }
53
+ export declare const MEMORY_LINK_POINTER_FILENAME = "memory-link.json";
54
+ /**
55
+ * Read and validate the memory link pointer for a worktree. Null if absent or
56
+ * malformed (fail-open — a corrupt pointer never strands memory; it degrades
57
+ * to local).
58
+ */
59
+ export declare function readMemoryLinkPointer(directory: string): MemoryLinkPointer | null;
60
+ /** Write the memory link pointer atomically and invalidate the cache. */
61
+ export declare function writeMemoryLinkPointer(directory: string, pointer: MemoryLinkPointer): Promise<void>;
62
+ /** Remove the memory link pointer (idempotent) and invalidate the cache. */
63
+ export declare function removeMemoryLinkPointer(directory: string): Promise<void>;
64
+ /**
65
+ * Build the stat fingerprint for the pointer file. Returns null when absent
66
+ * (so an absent→present transition is detected as a change). Includes ctimeMs
67
+ * because Windows rename preserves mtimeMs (mirrors knowledge-link.ts:365).
68
+ */
69
+ declare function pointerStatFingerprint(directory: string): string | null;
70
+ /**
71
+ * Resolve the directory that holds the cohort store for `directory`.
72
+ *
73
+ * Returns the shared link directory (`<dataDir>/links/<linkId>`) when an active
74
+ * memory-link pointer is present, otherwise the local `<directory>/.swarm`.
75
+ * Fail-open: any read/parse error degrades to local. Synchronous and cached;
76
+ * on a cache hit a cheap `stat` revalidates cross-process changes so the hot
77
+ * recall path stays fast while observing link/unlink without TTL wait.
78
+ *
79
+ * NOTE: when unlinked, the return value is byte-identical to
80
+ * `path.join(directory, '.swarm')`, so callers are unaffected.
81
+ *
82
+ * Memory occupies `<result>/memory/`. The caller (`resolveVettedMemoryRoot`)
83
+ * appends `memory` to namespace within the cohort container.
84
+ */
85
+ export declare function resolveMemoryStoreDir(directory: string): string;
86
+ /** Drop cached resolution(s). Pass a directory to invalidate one, omit for all. */
87
+ export declare function invalidateMemoryStoreDirCache(directory?: string): void;
88
+ /** True when the worktree currently redirects to a shared cohort store. */
89
+ export declare function isMemoryLinked(directory: string): boolean;
90
+ /** Surface an orphaned local memory store if the link pointer is active. */
91
+ export declare function warnIfMemoryOrphaned(directory: string): void;
92
+ export declare const _internals: {
93
+ resolveMemoryStoreDir: typeof resolveMemoryStoreDir;
94
+ readMemoryLinkPointer: typeof readMemoryLinkPointer;
95
+ writeMemoryLinkPointer: typeof writeMemoryLinkPointer;
96
+ removeMemoryLinkPointer: typeof removeMemoryLinkPointer;
97
+ invalidateMemoryStoreDirCache: typeof invalidateMemoryStoreDirCache;
98
+ resolveLinkDir: typeof resolveLinkDir;
99
+ resolveLinkBaseDir: typeof resolveLinkBaseDir;
100
+ sanitizeLinkId: typeof sanitizeLinkId;
101
+ pointerStatFingerprint: typeof pointerStatFingerprint;
102
+ };
103
+ export { resolveLinkDir, resolveLinkBaseDir, sanitizeLinkId };
@@ -1,5 +1,6 @@
1
1
  import type { MemoryConfig } from './config';
2
2
  import type { MemoryProposalStore, MemoryProvider } from './provider';
3
+ import type { VettedMemoryRoot } from './storage-root';
3
4
  /**
4
5
  * Return true if the provider is currently managed by the pool.
5
6
  *
@@ -22,8 +23,42 @@ export declare function isPooledProvider(provider: MemoryProvider & Partial<Memo
22
23
  * Note: this function returns synchronously. The provider's actual database
23
24
  * open happens lazily inside `provider.initialize()`, which callers await
24
25
  * separately (and which is protected by the provider's own init mutex).
26
+ *
27
+ * #1850: callers that have resolved a `VettedMemoryRoot` should prefer
28
+ * `getOrCreateProviderForRoot` so the cohort dimension (cohort id + generation)
29
+ * is part of the pool key. This legacy entry treats `directory` as a local
30
+ * root (cohort sharing inactive for that caller — emits a debug log per
31
+ * critic CONCERN-5 so incomplete wiring is visible).
25
32
  */
26
33
  export declare function getOrCreateProvider(directory: string, config: MemoryConfig): MemoryProvider & MemoryProposalStore;
34
+ /**
35
+ * #1850: cohort-aware provider acquisition. The pool key incorporates the
36
+ * root kind + canonical path + cohort generation, so:
37
+ * - the same cohort root always converges on the same pool entry (acceptance #5);
38
+ * - a generation bump (observed via the `memory-link.json` pointer-stat
39
+ * revalidation in `resolveVettedMemoryRoot`) invalidates the stale entry
40
+ * so the in-memory mirror is reloaded (acceptance #8).
41
+ *
42
+ * NOTE (final-critic correction): the `memory.gen` marker written by providers
43
+ * on cohort writes is NOT consumed by this pool today. Cross-process write
44
+ * visibility relies on SQLite WAL mode (readers see committed writes on the
45
+ * next query) plus the 2s TTL + pointer-stat revalidation in
46
+ * `resolveMemoryStoreDir`. The `memory.gen` marker is reserved for a future
47
+ * tighter revalidation loop and is safe to write (best-effort, never blocks).
48
+ *
49
+ * Revalidation: on each call for a cohort root, the caller is expected to have
50
+ * already resolved the current generation (the resolver does this via
51
+ * `memory-link.json` pointer-stat). If the generation in the supplied root
52
+ * differs from the cached entry's generation, the cached entry is evicted and
53
+ * a fresh provider is constructed (which reloads the in-memory mirror).
54
+ */
55
+ export declare function getOrCreateProviderForRoot(root: VettedMemoryRoot, config: MemoryConfig): MemoryProvider & MemoryProposalStore;
56
+ /**
57
+ * #1850: scoped eviction — close and drop the pool entry for a specific root
58
+ * (used by the migration engine before file copy). Waits for refCount=0 via
59
+ * the deferred mechanism if held; callers should retry-acquire or fail closed.
60
+ */
61
+ export declare function evictAndCloseForRoot(root: VettedMemoryRoot): void;
27
62
  /**
28
63
  * Release a pooled provider back to the pool by decrementing its refCount.
29
64
  *
@@ -2,6 +2,37 @@ export interface SecretFinding {
2
2
  type: string;
3
3
  match: string;
4
4
  }
5
+ /**
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
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
11
+ * records. Bump the salt constant when the redaction contract changes
12
+ * meaningfully (e.g. adding PII detection per #1466).
13
+ */
14
+ export declare const REDACTION_POLICY_SALT = 1;
15
+ export declare function computeRedactionPolicyVersion(rejectDurableSecrets: boolean): number;
16
+ export interface MemoryCohortFingerprintInput {
17
+ provider: string;
18
+ redaction_policy_version: number;
19
+ embedding_model: string;
20
+ embedding_dimension: number;
21
+ embedding_version: string;
22
+ }
23
+ export declare function computeMemoryCohortFingerprint(input: MemoryCohortFingerprintInput): string;
24
+ /** Convenience: build the fingerprint input from a memory config subset. */
25
+ export declare function buildMemoryCohortFingerprintInput(config: {
26
+ provider: string;
27
+ redaction: {
28
+ rejectDurableSecrets: boolean;
29
+ };
30
+ embeddings: {
31
+ model: string;
32
+ dimension: number;
33
+ version?: string;
34
+ };
35
+ }): MemoryCohortFingerprintInput;
5
36
  export declare function findSecrets(text: string): SecretFinding[];
6
37
  export declare function containsSecret(text: string): boolean;
7
38
  export declare function redactSecrets(text: string): string;
@@ -7,6 +7,7 @@ export declare const MemoryScopeTypeSchema: z.ZodEnum<{
7
7
  global_user: "global_user";
8
8
  repository: "repository";
9
9
  run: "run";
10
+ cohort: "cohort";
10
11
  }>;
11
12
  export declare const MemoryScopeRefSchema: z.ZodObject<{
12
13
  type: z.ZodEnum<{
@@ -16,6 +17,7 @@ export declare const MemoryScopeRefSchema: z.ZodObject<{
16
17
  global_user: "global_user";
17
18
  repository: "repository";
18
19
  run: "run";
20
+ cohort: "cohort";
19
21
  }>;
20
22
  userId: z.ZodOptional<z.ZodString>;
21
23
  workspaceId: z.ZodOptional<z.ZodString>;
@@ -24,6 +26,7 @@ export declare const MemoryScopeRefSchema: z.ZodObject<{
24
26
  repoRoot: z.ZodOptional<z.ZodString>;
25
27
  runId: z.ZodOptional<z.ZodString>;
26
28
  agentId: z.ZodOptional<z.ZodString>;
29
+ cohortId: z.ZodOptional<z.ZodString>;
27
30
  }, z.core.$strict>;
28
31
  export declare const MemoryKindSchema: z.ZodEnum<{
29
32
  evidence: "evidence";
@@ -67,6 +70,7 @@ export declare const MemoryRecordSchema: z.ZodObject<{
67
70
  global_user: "global_user";
68
71
  repository: "repository";
69
72
  run: "run";
73
+ cohort: "cohort";
70
74
  }>;
71
75
  userId: z.ZodOptional<z.ZodString>;
72
76
  workspaceId: z.ZodOptional<z.ZodString>;
@@ -75,6 +79,7 @@ export declare const MemoryRecordSchema: z.ZodObject<{
75
79
  repoRoot: z.ZodOptional<z.ZodString>;
76
80
  runId: z.ZodOptional<z.ZodString>;
77
81
  agentId: z.ZodOptional<z.ZodString>;
82
+ cohortId: z.ZodOptional<z.ZodString>;
78
83
  }, z.core.$strict>;
79
84
  kind: z.ZodEnum<{
80
85
  evidence: "evidence";
@@ -125,6 +130,13 @@ export declare const MemoryRecordSchema: z.ZodObject<{
125
130
  supersededBy: z.ZodOptional<z.ZodString>;
126
131
  contentHash: z.ZodString;
127
132
  metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
133
+ cohortId: z.ZodOptional<z.ZodString>;
134
+ producerSessionId: z.ZodOptional<z.ZodString>;
135
+ producerAgentRole: z.ZodOptional<z.ZodString>;
136
+ redactionPolicyVersion: z.ZodOptional<z.ZodNumber>;
137
+ schemaVersion: z.ZodOptional<z.ZodNumber>;
138
+ providerVersion: z.ZodOptional<z.ZodString>;
139
+ sourceRevision: z.ZodOptional<z.ZodString>;
128
140
  }, z.core.$strict>;
129
141
  export declare const MemoryProposalSchema: z.ZodObject<{
130
142
  id: z.ZodString;
@@ -146,6 +158,7 @@ export declare const MemoryProposalSchema: z.ZodObject<{
146
158
  global_user: "global_user";
147
159
  repository: "repository";
148
160
  run: "run";
161
+ cohort: "cohort";
149
162
  }>;
150
163
  userId: z.ZodOptional<z.ZodString>;
151
164
  workspaceId: z.ZodOptional<z.ZodString>;
@@ -154,6 +167,7 @@ export declare const MemoryProposalSchema: z.ZodObject<{
154
167
  repoRoot: z.ZodOptional<z.ZodString>;
155
168
  runId: z.ZodOptional<z.ZodString>;
156
169
  agentId: z.ZodOptional<z.ZodString>;
170
+ cohortId: z.ZodOptional<z.ZodString>;
157
171
  }, z.core.$strict>;
158
172
  kind: z.ZodEnum<{
159
173
  evidence: "evidence";
@@ -204,6 +218,13 @@ export declare const MemoryProposalSchema: z.ZodObject<{
204
218
  supersededBy: z.ZodOptional<z.ZodString>;
205
219
  contentHash: z.ZodString;
206
220
  metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
221
+ cohortId: z.ZodOptional<z.ZodString>;
222
+ producerSessionId: z.ZodOptional<z.ZodString>;
223
+ producerAgentRole: z.ZodOptional<z.ZodString>;
224
+ redactionPolicyVersion: z.ZodOptional<z.ZodNumber>;
225
+ schemaVersion: z.ZodOptional<z.ZodNumber>;
226
+ providerVersion: z.ZodOptional<z.ZodString>;
227
+ sourceRevision: z.ZodOptional<z.ZodString>;
207
228
  }, z.core.$strict>>;
208
229
  targetMemoryId: z.ZodOptional<z.ZodString>;
209
230
  relatedMemoryIds: z.ZodOptional<z.ZodArray<z.ZodString>>;