opencode-swarm 7.88.3 → 7.89.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 (49) hide show
  1. package/dist/agents/agent-output-schema.d.ts +1 -1
  2. package/dist/agents/curator-agent.d.ts +1 -1
  3. package/dist/agents/explorer.d.ts +1 -0
  4. package/dist/cli/{config-doctor-jzbgpbdh.js → config-doctor-g04wdz19.js} +2 -2
  5. package/dist/cli/{explorer-gz70sm9b.js → explorer-h2fnj343.js} +4 -2
  6. package/dist/cli/{guardrail-explain-eqypvw60.js → guardrail-explain-hy0zz0p6.js} +8 -8
  7. package/dist/cli/{guardrail-log-c7egm5km.js → guardrail-log-m3285thy.js} +3 -3
  8. package/dist/cli/{index-0asbrmdx.js → index-123s7kjc.js} +88 -2
  9. package/dist/cli/{index-32axfg6h.js → index-1ccnwh54.js} +98 -25
  10. package/dist/cli/{index-ds057q5k.js → index-6k31ysgd.js} +2 -2
  11. package/dist/cli/{index-g00qm2gf.js → index-6tnmt41c.js} +1 -1
  12. package/dist/cli/{index-g6f4tt38.js → index-9w07ye9b.js} +906 -431
  13. package/dist/cli/{index-e8pk68cc.js → index-axwxkbdd.js} +166 -23
  14. package/dist/cli/{index-yhsmmv2z.js → index-bm4f0nme.js} +25 -1
  15. package/dist/cli/{index-819xp49y.js → index-bywt2171.js} +1 -1
  16. package/dist/cli/{index-0rt5aamg.js → index-dprk5c5f.js} +13 -9
  17. package/dist/cli/{index-vjsr9bqt.js → index-gg589mfw.js} +1 -1
  18. package/dist/cli/index.js +7 -7
  19. package/dist/cli/{knowledge-store-n4x6zyk7.js → knowledge-store-gsy6p46z.js} +1 -1
  20. package/dist/cli/{schema-vb6jkxgg.js → schema-t9th7frq.js} +1 -1
  21. package/dist/cli/{skill-generator-kz4q8e49.js → skill-generator-1hzfyhth.js} +2 -2
  22. package/dist/commands/index.d.ts +2 -0
  23. package/dist/commands/link.d.ts +19 -0
  24. package/dist/commands/memory.d.ts +1 -0
  25. package/dist/commands/registry.d.ts +31 -0
  26. package/dist/commands/unlink.d.ts +13 -0
  27. package/dist/config/agent-names.d.ts +2 -2
  28. package/dist/config/schema.d.ts +60 -0
  29. package/dist/hooks/curator-llm-factory.d.ts +1 -1
  30. package/dist/hooks/knowledge-events.d.ts +3 -3
  31. package/dist/hooks/knowledge-link.d.ts +82 -0
  32. package/dist/hooks/knowledge-validator.d.ts +1 -1
  33. package/dist/index.js +3177 -1765
  34. package/dist/knowledge/identity.d.ts +9 -0
  35. package/dist/memory/config.d.ts +35 -0
  36. package/dist/memory/consolidation-log.d.ts +29 -0
  37. package/dist/memory/consolidation.d.ts +124 -0
  38. package/dist/memory/decay.d.ts +24 -0
  39. package/dist/memory/gateway.d.ts +14 -2
  40. package/dist/memory/maintenance.d.ts +18 -0
  41. package/dist/memory/run-log.d.ts +8 -1
  42. package/dist/memory/schema.d.ts +3 -3
  43. package/dist/memory/scoring.d.ts +45 -0
  44. package/dist/memory/sentinel.d.ts +15 -0
  45. package/dist/services/memory-consolidation.d.ts +32 -0
  46. package/dist/services/skill-generator.d.ts +8 -1
  47. package/dist/session/worktree-link-suggestion.d.ts +27 -0
  48. package/dist/state.d.ts +4 -2
  49. package/package.json +1 -1
@@ -14,6 +14,15 @@ export interface ProjectIdentity {
14
14
  * Path: {platform-config-dir}/projects/{projectHash}/identity.json
15
15
  */
16
16
  export declare function resolveIdentityPath(projectHash: string): string;
17
+ /**
18
+ * Derive a deterministic project hash from a directory.
19
+ * Uses git remote URL if available, otherwise falls back to absolute path.
20
+ *
21
+ * Worktrees of the same repository share a git remote, so they derive the same
22
+ * hash — which is what lets `/swarm link` (with no name) tie them to one shared
23
+ * knowledge store by default.
24
+ */
25
+ export declare function deriveProjectHash(directory: string): string;
17
26
  /**
18
27
  * Read existing identity.json or return null if it doesn't exist.
19
28
  */
@@ -26,12 +26,47 @@ export interface MemoryConfig {
26
26
  rejectDurableSecrets: boolean;
27
27
  };
28
28
  maintenance: {
29
+ /** @deprecated superseded by `maintenance.importance` (issue #1464); retained for back-compat. */
29
30
  lowUtilityMaxConfidence: number;
31
+ /** @deprecated superseded by `maintenance.importance` (issue #1464); retained for back-compat. */
30
32
  lowUtilityMinAgeDays: number;
31
33
  autoCompactEveryNRecalls?: number;
34
+ /** Importance-formula weights + low-utility threshold (DD-11). */
35
+ importance: ImportanceConfig;
32
36
  };
37
+ /** Reflection / consolidation pass (issue #1464, Phase 3). */
38
+ consolidation: ConsolidationConfig;
33
39
  hardDelete: boolean;
34
40
  }
41
+ export interface ImportanceConfig {
42
+ wRecency: number;
43
+ wFrequency: number;
44
+ wFreshness: number;
45
+ wConfidence: number;
46
+ lambda: number;
47
+ mu: number;
48
+ n: number;
49
+ /** A memory is low-utility when importance < threshold. */
50
+ threshold: number;
51
+ }
52
+ export interface ConsolidationConfig {
53
+ /** Run episodic→semantic consolidation at phase_complete. Gated by `enabled` too. */
54
+ enabled: boolean;
55
+ /** Max LLM-distilled clusters processed per pass (cost control). */
56
+ maxClustersPerPass: number;
57
+ /** Jaccard token-overlap threshold for lexical clustering. */
58
+ jaccardThreshold: number;
59
+ /** Distilled facts below this confidence are filed as proposals, not auto-applied. */
60
+ autoApplyMinConfidence: number;
61
+ /**
62
+ * Kind-specific decay half-lives in days. A value of 0 means "never auto-expire"
63
+ * (the issue's "365+ days, no decay unless superseded" durable kinds).
64
+ */
65
+ decayHalfLifeDays: Record<MemoryKind, number>;
66
+ }
67
+ export declare const DEFAULT_DECAY_HALF_LIFE_DAYS: Record<MemoryKind, number>;
68
+ export declare const DEFAULT_IMPORTANCE_CONFIG: ImportanceConfig;
69
+ export declare const DEFAULT_CONSOLIDATION_CONFIG: ConsolidationConfig;
35
70
  export declare const DEFAULT_MEMORY_CONFIG: MemoryConfig;
36
71
  export declare const DURABLE_MEMORY_KINDS: ReadonlySet<MemoryKind>;
37
72
  export declare const EVIDENCE_REQUIRED_KINDS: ReadonlySet<MemoryKind>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Durable, append-only record of a completed consolidation pass. Persisted to
3
+ * `.swarm/memory/consolidation-log.jsonl` (invariant #4: runtime state stays
4
+ * under `.swarm/`). Serves two purposes:
5
+ * - idempotency: a pass for a `phaseNumber` already present here is a no-op;
6
+ * - observability: the `/swarm memory consolidation-log` CLI reads it.
7
+ */
8
+ export interface ConsolidationLogRecord {
9
+ phaseNumber: number;
10
+ /** Session/run that produced this pass, for multi-session observability.
11
+ * Idempotency remains keyed on phaseNumber (the memory store is per
12
+ * directory), so this is informational only. */
13
+ runId?: string;
14
+ startedAt: string;
15
+ completedAt: string;
16
+ clusterCount: number;
17
+ clustersDeferred: number;
18
+ decisionsEmitted: number;
19
+ added: number;
20
+ superseded: number;
21
+ contradictionsDetected: number;
22
+ deduped: number;
23
+ proposed: number;
24
+ memoriesDecayed: number;
25
+ errored: number;
26
+ processedProposalIds: string[];
27
+ }
28
+ export declare function readConsolidationLog(directory: string): Promise<ConsolidationLogRecord[]>;
29
+ export declare function appendConsolidationLog(directory: string, record: ConsolidationLogRecord): Promise<void>;
@@ -0,0 +1,124 @@
1
+ import { z } from 'zod';
2
+ import { type MemoryConfig } from './config';
3
+ import type { ConsolidationLogRecord } from './consolidation-log';
4
+ import type { ProposeMemoryInput } from './gateway';
5
+ import type { MemoryRunLogEvent } from './run-log';
6
+ import type { AppliedMemoryChange, CuratorMemoryDecision, MemoryListFilter, MemoryProposal, MemoryRecord } from './types';
7
+ /** Agent role stamped on consolidation-emitted proposals (used to exclude them
8
+ * from the engine's own episodic input — see reviewer finding #3). The
9
+ * fire-and-forget wrapper sets this as the gateway context's agentRole. */
10
+ export declare const CONSOLIDATION_AGENT_ROLE = "curator_consolidation";
11
+ /** Narrow gateway surface the engine depends on (MemoryGateway satisfies it). */
12
+ export interface ConsolidationGateway {
13
+ isEnabled(): boolean;
14
+ listProposals(filter?: {
15
+ status?: MemoryProposal['status'];
16
+ limit?: number;
17
+ }): Promise<MemoryProposal[]>;
18
+ listMemories(filter?: MemoryListFilter): Promise<MemoryRecord[]>;
19
+ propose(input: ProposeMemoryInput): Promise<MemoryProposal>;
20
+ applyCuratorDecision(decision: CuratorMemoryDecision): Promise<AppliedMemoryChange>;
21
+ upsertCurated(record: MemoryRecord): Promise<MemoryRecord>;
22
+ }
23
+ export type DistillLLMDelegate = (systemPrompt: string, userInput: string, signal?: AbortSignal) => Promise<string>;
24
+ export interface ConsolidationDeps {
25
+ gateway: ConsolidationGateway;
26
+ llmDelegate?: DistillLLMDelegate;
27
+ now: () => Date;
28
+ logEvent: (event: MemoryRunLogEvent) => Promise<void>;
29
+ readLog: () => Promise<ConsolidationLogRecord[]>;
30
+ appendLog: (record: ConsolidationLogRecord) => Promise<void>;
31
+ signal?: AbortSignal;
32
+ }
33
+ export interface ConsolidationInput {
34
+ directory: string;
35
+ phaseNumber: number;
36
+ runId?: string;
37
+ config: MemoryConfig;
38
+ }
39
+ export interface ConsolidationResult {
40
+ skipped: boolean;
41
+ skipReason?: 'disabled' | 'already_consolidated' | 'no_llm_delegate_decay_only' | 'aborted';
42
+ phaseNumber: number;
43
+ clusterCount: number;
44
+ clustersDeferred: number;
45
+ decisionsEmitted: number;
46
+ added: number;
47
+ superseded: number;
48
+ contradictionsDetected: number;
49
+ deduped: number;
50
+ proposed: number;
51
+ memoriesDecayed: number;
52
+ errored: number;
53
+ }
54
+ declare const DistilledFactSchema: z.ZodObject<{
55
+ text: z.ZodString;
56
+ kind: z.ZodEnum<{
57
+ evidence: "evidence";
58
+ user_preference: "user_preference";
59
+ project_fact: "project_fact";
60
+ architecture_decision: "architecture_decision";
61
+ repo_convention: "repo_convention";
62
+ api_finding: "api_finding";
63
+ code_pattern: "code_pattern";
64
+ test_pattern: "test_pattern";
65
+ failure_pattern: "failure_pattern";
66
+ security_note: "security_note";
67
+ todo: "todo";
68
+ scratch: "scratch";
69
+ }>;
70
+ confidence: z.ZodNumber;
71
+ contradictsMemoryId: z.ZodOptional<z.ZodString>;
72
+ }, z.core.$strip>;
73
+ export type DistilledFact = z.infer<typeof DistilledFactSchema>;
74
+ /**
75
+ * Parse the curator LLM's distillation output. Tolerant of ```json fences and
76
+ * surrounding prose. Returns [] on any parse/validation failure (caller counts
77
+ * the cluster as skipped rather than aborting the pass).
78
+ */
79
+ export declare function parseDistillationOutput(raw: string): DistilledFact[];
80
+ export type DecisionPlan = {
81
+ type: 'add';
82
+ fact: DistilledFact;
83
+ } | {
84
+ type: 'supersede';
85
+ fact: DistilledFact;
86
+ oldMemoryId: string;
87
+ } | {
88
+ type: 'proposal';
89
+ fact: DistilledFact;
90
+ reason: string;
91
+ } | {
92
+ type: 'dedup';
93
+ fact: DistilledFact;
94
+ duplicateOf: string;
95
+ } | {
96
+ type: 'skip';
97
+ fact: DistilledFact;
98
+ reason: string;
99
+ };
100
+ /**
101
+ * Pure decision derivation for a single distilled fact against existing items.
102
+ * Encapsulates the auto-apply eligibility rules so they are unit-testable
103
+ * without a gateway:
104
+ * - sentinel-bearing text is skipped (defense in depth; the write guard also
105
+ * rejects it);
106
+ * - `scratch` is never promoted to semantic memory;
107
+ * - only DURABLE_MEMORY_KINDS, ≤500-char, ≥autoApplyMinConfidence facts are
108
+ * auto-applied; everything else is filed as a pending proposal;
109
+ * - a contradiction (explicit `contradictsMemoryId` that resolves to an
110
+ * existing active memory) becomes a supersede;
111
+ * - a near-duplicate (Jaccard ≥ threshold) becomes a no-op dedup.
112
+ */
113
+ export declare function deriveDecision(fact: DistilledFact, existing: MemoryRecord[], options: {
114
+ autoApplyMinConfidence: number;
115
+ jaccardThreshold: number;
116
+ }): DecisionPlan;
117
+ /**
118
+ * Run one episodic→semantic consolidation pass for a completed phase.
119
+ * Idempotent per `phaseNumber` (a completed log record short-circuits a rerun).
120
+ * All IO is injected via {@link ConsolidationDeps} so the orchestration is
121
+ * deterministically testable with a fake gateway and fake LLM delegate.
122
+ */
123
+ export declare function runConsolidationPass(input: ConsolidationInput, deps: ConsolidationDeps): Promise<ConsolidationResult>;
124
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { MemoryKind, MemoryRecord } from './types';
2
+ /**
3
+ * Compute the kind-specific decay expiry for a memory (issue #1464).
4
+ *
5
+ * Returns the new `expiresAt` ISO string to apply, or `undefined` when no
6
+ * change should be made. Rules:
7
+ * - `scratch` is never re-decayed here — it already receives a ≤7-day expiry
8
+ * at creation time and is bounded by `validateMemoryRecordRules`.
9
+ * - A half-life of `0` means "never auto-expire" (the durable 365+ kinds).
10
+ * - Decay sets `expiresAt = createdAt + halfLifeDays`, but never SHORTENS an
11
+ * existing earlier expiry (a memory already set to expire sooner keeps that
12
+ * sooner expiry).
13
+ *
14
+ * Pure and deterministic: callers pass the existing record and apply the
15
+ * returned expiry by patching only `expiresAt` (preserving id/hash/timestamps).
16
+ */
17
+ export declare function computeDecayExpiry(memory: Pick<MemoryRecord, 'kind' | 'createdAt' | 'expiresAt'>, halfLifeDays: Record<MemoryKind, number>, _now?: Date): string | undefined;
18
+ /**
19
+ * Check whether a record's natural half-life date is in the past (i.e., the record
20
+ * is older than its decay horizon). Used to guard against upgrade-time auto-expiry
21
+ * when migrating from pre-decay code: a record written under a previous config
22
+ * without decay should not be silently expired on the first consolidation pass.
23
+ */
24
+ export declare function isPastDecayHorizon(memory: Pick<MemoryRecord, 'kind' | 'createdAt'>, halfLifeDays: Record<MemoryKind, number>, now?: Date): boolean;
@@ -1,6 +1,6 @@
1
1
  import { type MemoryConfig } from './config';
2
- import type { MemoryProposalStore, MemoryProvider } from './provider';
3
- import type { AppliedMemoryChange, CuratorMemoryDecision, MemoryContext, MemoryKind, MemoryProposal, MemoryRecord, MemoryScopeRef, MemorySource, RecallBundle, RecallMode } from './types';
2
+ import type { MemoryProposalStore, MemoryProvider, MemoryRecallUsageEvent, MemoryRecallUsageFilter } from './provider';
3
+ import type { AppliedMemoryChange, CuratorMemoryDecision, MemoryContext, MemoryKind, MemoryListFilter, MemoryProposal, MemoryRecord, MemoryScopeRef, MemorySource, RecallBundle, RecallMode } from './types';
4
4
  export interface MemoryGatewayOptions {
5
5
  config?: Partial<MemoryConfig>;
6
6
  provider?: MemoryProvider & Partial<MemoryProposalStore>;
@@ -39,6 +39,18 @@ export declare class MemoryGateway {
39
39
  deriveAllowedScopes(): MemoryScopeRef[];
40
40
  recall(input: RecallMemoryInput): Promise<RecallBundle>;
41
41
  propose(input: ProposeMemoryInput): Promise<MemoryProposal>;
42
+ /**
43
+ * Read-only passthroughs used by the consolidation engine. They assert the
44
+ * feature is enabled and that the underlying provider supports the
45
+ * capability (mirroring the `createProposal` guard), so the engine can
46
+ * depend on a stable gateway surface rather than the `Partial` provider.
47
+ */
48
+ listMemories(filter?: MemoryListFilter): Promise<MemoryRecord[]>;
49
+ listProposals(filter?: {
50
+ status?: MemoryProposal['status'];
51
+ limit?: number;
52
+ }): Promise<MemoryProposal[]>;
53
+ listRecallUsage(filter?: MemoryRecallUsageFilter): Promise<MemoryRecallUsageEvent[]>;
42
54
  upsertCurated(record: MemoryRecord): Promise<MemoryRecord>;
43
55
  applyCuratorDecision(decision: CuratorMemoryDecision): Promise<AppliedMemoryChange>;
44
56
  createRecord(input: {
@@ -1,5 +1,19 @@
1
1
  import type { MemoryProposalStore, MemoryProvider } from './provider';
2
+ import { type ImportanceWeights } from './scoring';
2
3
  import type { MemoryProposal, MemoryRecord } from './types';
4
+ /**
5
+ * Default importance-formula weights (issue #1464). Match the zod and runtime
6
+ * config defaults; used when the maintenance report is built without explicit
7
+ * importance options (e.g. legacy callers).
8
+ */
9
+ export declare const DEFAULT_IMPORTANCE_WEIGHTS: ImportanceWeights;
10
+ /**
11
+ * A never-recalled memory's importance is driven by freshness+confidence
12
+ * (recency/frequency terms are 0). 0.2 keeps high-confidence durable facts
13
+ * above the line regardless of age (the DD-11 fix) while still flagging
14
+ * low-confidence and very stale never-recalled memories. Pinned by tests.
15
+ */
16
+ export declare const DEFAULT_IMPORTANCE_THRESHOLD = 0.2;
3
17
  export interface MemoryRecallUsageByMemory {
4
18
  memoryId: string;
5
19
  count: number;
@@ -38,6 +52,10 @@ export interface MemoryMaintenanceReportOptions {
38
52
  limit?: number;
39
53
  lowUtilityMaxConfidence?: number;
40
54
  lowUtilityMinAgeDays?: number;
55
+ /** Importance-formula weights (DD-11). Defaults to DEFAULT_IMPORTANCE_WEIGHTS. */
56
+ importanceWeights?: ImportanceWeights;
57
+ /** A memory is low-utility when importance < threshold. Defaults to DEFAULT_IMPORTANCE_THRESHOLD. */
58
+ importanceThreshold?: number;
41
59
  }
42
60
  type ObservableProvider = MemoryProvider & Partial<MemoryProposalStore> & {
43
61
  listRecallUsage?: MemoryProvider['listRecallUsage'];
@@ -1,4 +1,4 @@
1
- export type MemoryRunLogEventName = 'recall_requested' | 'recall_returned' | 'prompt_injection_skipped' | 'prompt_injected' | 'proposal_created' | 'proposal_rejected_by_validation' | 'curator_decision_applied' | 'curator_decision_rejected_by_validation';
1
+ export type MemoryRunLogEventName = 'recall_requested' | 'recall_returned' | 'prompt_injection_skipped' | 'prompt_injected' | 'proposal_created' | 'proposal_rejected_by_validation' | 'curator_decision_applied' | 'curator_decision_rejected_by_validation' | 'consolidation_started' | 'cluster_count' | 'decisions_emitted' | 'contradictions_detected' | 'memories_decayed' | 'consolidation_completed';
2
2
  export interface MemoryRunLogEvent {
3
3
  event: MemoryRunLogEventName;
4
4
  runId: string;
@@ -11,6 +11,13 @@ export interface MemoryRunLogEvent {
11
11
  proposalId?: string;
12
12
  rejectionReason?: string;
13
13
  timestamp?: string;
14
+ /** Consolidation pass identifier (phase boundary that triggered the pass). */
15
+ phaseNumber?: number;
16
+ /** Consolidation metrics (issue #1464). */
17
+ clusterCount?: number;
18
+ decisionsEmitted?: number;
19
+ contradictionsDetected?: number;
20
+ memoriesDecayed?: number;
14
21
  metadata?: Record<string, unknown>;
15
22
  }
16
23
  export declare function appendMemoryRunLog(directory: string, runId: string | undefined, event: MemoryRunLogEvent): Promise<void>;
@@ -27,7 +27,6 @@ export declare const MemoryScopeRefSchema: z.ZodObject<{
27
27
  }, z.core.$strict>;
28
28
  export declare const MemoryKindSchema: z.ZodEnum<{
29
29
  evidence: "evidence";
30
- todo: "todo";
31
30
  user_preference: "user_preference";
32
31
  project_fact: "project_fact";
33
32
  architecture_decision: "architecture_decision";
@@ -37,6 +36,7 @@ export declare const MemoryKindSchema: z.ZodEnum<{
37
36
  test_pattern: "test_pattern";
38
37
  failure_pattern: "failure_pattern";
39
38
  security_note: "security_note";
39
+ todo: "todo";
40
40
  scratch: "scratch";
41
41
  }>;
42
42
  export declare const MemorySourceSchema: z.ZodObject<{
@@ -78,7 +78,6 @@ export declare const MemoryRecordSchema: z.ZodObject<{
78
78
  }, z.core.$strict>;
79
79
  kind: z.ZodEnum<{
80
80
  evidence: "evidence";
81
- todo: "todo";
82
81
  user_preference: "user_preference";
83
82
  project_fact: "project_fact";
84
83
  architecture_decision: "architecture_decision";
@@ -88,6 +87,7 @@ export declare const MemoryRecordSchema: z.ZodObject<{
88
87
  test_pattern: "test_pattern";
89
88
  failure_pattern: "failure_pattern";
90
89
  security_note: "security_note";
90
+ todo: "todo";
91
91
  scratch: "scratch";
92
92
  }>;
93
93
  text: z.ZodString;
@@ -156,7 +156,6 @@ export declare const MemoryProposalSchema: z.ZodObject<{
156
156
  }, z.core.$strict>;
157
157
  kind: z.ZodEnum<{
158
158
  evidence: "evidence";
159
- todo: "todo";
160
159
  user_preference: "user_preference";
161
160
  project_fact: "project_fact";
162
161
  architecture_decision: "architecture_decision";
@@ -166,6 +165,7 @@ export declare const MemoryProposalSchema: z.ZodObject<{
166
165
  test_pattern: "test_pattern";
167
166
  failure_pattern: "failure_pattern";
168
167
  security_note: "security_note";
168
+ todo: "todo";
169
169
  scratch: "scratch";
170
170
  }>;
171
171
  text: z.ZodString;
@@ -25,6 +25,51 @@ export declare const SCORING_WEIGHTS: {
25
25
  readonly roleBoost: 0.05;
26
26
  readonly confidence: 0.08;
27
27
  };
28
+ export declare function tokenize(text: string): Set<string>;
29
+ /**
30
+ * True Jaccard similarity |A∩B| / |A∪B| over two token sets. Distinct from the
31
+ * recall-scoring `overlap` helper (which is a precision-like |A∩B|/max(|A|,|B|)).
32
+ * Consolidation clustering uses Jaccard per the issue spec (threshold 0.30).
33
+ */
34
+ export declare function jaccard(a: Set<string>, b: Set<string>): number;
35
+ /**
36
+ * Single-pass greedy lexical clustering by Jaccard token overlap. Each item is
37
+ * compared against the representative (first member) of each existing cluster;
38
+ * it joins the first cluster whose representative meets `threshold`, else seeds
39
+ * a new cluster. Deterministic for a given input order. Sufficient for v1
40
+ * (embedding-based clustering is Phase 4, out of scope).
41
+ */
42
+ export declare function clusterByJaccard<T>(items: T[], getText: (item: T) => string, threshold: number): T[][];
43
+ /**
44
+ * Continuous importance score in [0, sum-of-weights]. Replaces the boolean
45
+ * `isLowUtility` heuristic (DD-11). Implements the issue formula:
46
+ *
47
+ * importance = w_recency · exp(-λ · days_since_last_recall)
48
+ * + w_frequency · log1p(retrieval_count) / log1p(N)
49
+ * + w_freshness · exp(-μ · days_since_created)
50
+ * + w_confidence · confidence
51
+ *
52
+ * A never-recalled memory contributes 0 to the recency and frequency terms
53
+ * (days_since_last_recall is null, retrieval_count is 0), so for never-recalled
54
+ * items importance is driven by freshness and confidence — which is exactly why
55
+ * a high-confidence, never-recalled, aged memory is no longer mislabeled
56
+ * low-utility under the old OR condition.
57
+ */
58
+ export interface ImportanceWeights {
59
+ wRecency: number;
60
+ wFrequency: number;
61
+ wFreshness: number;
62
+ wConfidence: number;
63
+ lambda: number;
64
+ mu: number;
65
+ n: number;
66
+ }
67
+ export declare function importanceScore(input: {
68
+ confidence: number;
69
+ retrievalCount: number;
70
+ daysSinceLastRecall: number | null;
71
+ daysSinceCreated: number;
72
+ }, weights: ImportanceWeights): number;
28
73
  export declare function sameScope(a: MemoryScopeRef, b: MemoryScopeRef): boolean;
29
74
  export declare function scopeAllowed(recordScope: MemoryScopeRef, allowedScopes: MemoryScopeRef[]): boolean;
30
75
  export declare function scoreMemoryRecord(record: MemoryRecord, request: RecallRequest): RecallResultItem | null;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Single source of truth for the recall-injection sentinel header.
3
+ *
4
+ * DEPENDENCY-FREE leaf module. The injector emits this header to mark an
5
+ * injected "## Retrieved Swarm Memory" block, and `messagesContainRecall`
6
+ * uses it to avoid double-injection. Because that check trusts the substring,
7
+ * stored memory text must never be allowed to contain it (DD-14): a memory
8
+ * whose text embeds the sentinel, once injected, would make a later injection
9
+ * believe recall already happened and silently skip it.
10
+ *
11
+ * Both the emitter (`prompt-block.ts`) and the write-time guard
12
+ * (`schema.ts:validateMemoryRecordRules`) import from here so the header and
13
+ * the guard can never drift apart.
14
+ */
15
+ export declare const MEMORY_RECALL_SENTINEL = "## Retrieved Swarm Memory";
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Memory consolidation trigger (issue #1464, Phase 3).
3
+ *
4
+ * Thin, fail-open wrapper that runs the episodic→semantic consolidation engine
5
+ * at a phase boundary. Mirrors `runSkillConsolidationFireAndForget`: it is
6
+ * launched off the phase_complete success path, holds a per-directory in-flight
7
+ * guard, and never throws into the caller.
8
+ *
9
+ * Cancellation is cooperative: a deadline aborts an AbortController whose signal
10
+ * the engine checks before each write, and the wrapper always AWAITS the pass to
11
+ * settle before disposing the gateway and releasing the in-flight guard. This
12
+ * avoids orphaning the pass (using the gateway after dispose / racing a second
13
+ * pass on the same store) that a bare Promise.race timeout would cause.
14
+ */
15
+ import type { MemoryConfig } from '../memory/config.js';
16
+ import { type ConsolidationResult } from '../memory/consolidation.js';
17
+ export interface MemoryConsolidationRequest {
18
+ directory: string;
19
+ config: MemoryConfig;
20
+ phase: number;
21
+ sessionId?: string;
22
+ }
23
+ export interface MemoryConsolidationOutcome {
24
+ started: boolean;
25
+ reason?: string;
26
+ result?: ConsolidationResult;
27
+ }
28
+ export declare function runMemoryConsolidation(req: MemoryConsolidationRequest): Promise<MemoryConsolidationOutcome>;
29
+ export declare function runMemoryConsolidationFireAndForget(req: MemoryConsolidationRequest, onComplete?: (outcome: MemoryConsolidationOutcome) => void, onError?: (error: unknown) => void): void;
30
+ export declare const _internals: {
31
+ runningByDirectory: Map<string, Promise<MemoryConsolidationOutcome>>;
32
+ };
@@ -80,6 +80,7 @@ export interface GenerateResult {
80
80
  path: string;
81
81
  mode: GenerateMode;
82
82
  sourceKnowledgeIds: string[];
83
+ missingSourceKnowledgeIds?: string[];
83
84
  preserved: boolean;
84
85
  evaluation?: SkillEvaluationResult;
85
86
  }>;
@@ -95,8 +96,14 @@ export declare function generateSkills(req: GenerateRequest): Promise<GenerateRe
95
96
  * `generated_skill_path` metadata. Refactored in Phase G′ to take
96
97
  * `(directory, slug, ids)` so it can be called both from direct active-mode
97
98
  * generation AND from `activateProposal` after parsing the draft frontmatter.
99
+ *
100
+ * Returns the IDs that were successfully stamped and the IDs that were not
101
+ * found in swarm/hive knowledge (so callers can surface staleness).
98
102
  */
99
- declare function stampSourceEntries(directory: string, slug: string, ids: string[]): Promise<void>;
103
+ declare function stampSourceEntries(directory: string, slug: string, ids: string[]): Promise<{
104
+ stamped: string[];
105
+ missing: string[];
106
+ }>;
100
107
  /**
101
108
  * Bounded YAML frontmatter parser for generated drafts. Recognises the exact
102
109
  * shape we emit in renderSkillMarkdown — no full YAML lib required.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Worktree link suggestion (auto-detect + manual confirm).
3
+ *
4
+ * When a session starts in a repository that has more than one git worktree and
5
+ * this worktree is NOT yet linked to a shared knowledge store, emit a one-time,
6
+ * non-blocking suggestion to run `/swarm link`. Detection is advisory only —
7
+ * sharing is never enabled automatically; the user confirms with `/swarm link`.
8
+ *
9
+ * Safety (AGENTS.md Invariants 1, 3, 8):
10
+ * - Fully fail-open: any error is swallowed; never throws.
11
+ * - Bounded subprocess: array-form `git`, explicit `-C <dir>`, timeout, no shell.
12
+ * - Off the init critical path: callers invoke this fire-and-forget (not awaited).
13
+ * - Suggested-once per session, tracked in a FIFO-bounded module map.
14
+ */
15
+ /** Count worktrees via `git worktree list --porcelain`. Returns 0 on any failure. */
16
+ declare function countWorktrees(directory: string): Promise<number>;
17
+ /**
18
+ * Best-effort, once-per-session suggestion to link this worktree. Never throws.
19
+ * Intended to be called fire-and-forget (not awaited) from the session-start path.
20
+ */
21
+ export declare function maybeSuggestWorktreeLink(directory: string, sessionId: string): Promise<void>;
22
+ export declare const _internals: {
23
+ maybeSuggestWorktreeLink: typeof maybeSuggestWorktreeLink;
24
+ countWorktrees: typeof countWorktrees;
25
+ resetSuggested: () => void;
26
+ };
27
+ export {};
package/dist/state.d.ts CHANGED
@@ -316,6 +316,7 @@ export declare const swarmState: {
316
316
  curatorInitAgentNames: string[];
317
317
  curatorPhaseAgentNames: string[];
318
318
  curatorPostmortemAgentNames: string[];
319
+ curatorConsolidationAgentNames: string[];
319
320
  /** All registered skill_improver / spec_writer agent names across swarms,
320
321
  * mirroring curatorInitAgentNames so the LLM delegate factory can resolve
321
322
  * the correct prefixed agent under multi-swarm configs. */
@@ -369,11 +370,12 @@ export declare function resetSwarmState(): void;
369
370
  * The preserved fields are:
370
371
  * - opencodeClient (SDK client for curator/full-auto delegation)
371
372
  * - fullAutoEnabledInConfig (config flag read at init)
372
- * - curatorInitAgentNames, curatorPhaseAgentNames, curatorPostmortemAgentNames (curator registry)
373
+ * - curatorInitAgentNames, curatorPhaseAgentNames, curatorPostmortemAgentNames,
374
+ * curatorConsolidationAgentNames (curator registry)
373
375
  * - skillImproverAgentNames, specWriterAgentNames (skill/spec registry)
374
376
  * - generatedAgentNames (full-auto delegation guard registry)
375
377
  *
376
- * Implementation: save all 8 to locals, call resetSwarmState(), restore all 8.
378
+ * Implementation: save all to locals, call resetSwarmState(), restore all.
377
379
  * Synchronous (matches resetSwarmState contract). Errors from resetSwarmState
378
380
  * propagate to caller (no try/catch wrapper).
379
381
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.88.3",
3
+ "version": "7.89.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",