opencode-swarm 7.43.1 → 7.44.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.
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Event-sourced knowledge lifecycle for opencode-swarm.
3
+ *
4
+ * `.swarm/knowledge-events.jsonl` is an append-only, immutable log of every
5
+ * meaningful knowledge interaction (retrieval, receipt, outcome, archival).
6
+ * It is the authoritative history: per-entry counters
7
+ * (`retrieval_outcomes.*`) become *derived rollups* recomputed deterministically
8
+ * from this log via {@link recomputeCounters}.
9
+ *
10
+ * Design contracts:
11
+ * - Append-only. We never rewrite or delete lines. OS-level atomic append is
12
+ * used (same pattern as `appendKnowledge` in knowledge-store.ts) — no lock is
13
+ * needed for append-only writes.
14
+ * - Fail-open. Event recording is telemetry; it must never break tool or hook
15
+ * execution. Use {@link recordKnowledgeEvent} (swallows + warns) on hot paths;
16
+ * {@link appendKnowledgeEvent} throws and is intended for tests / callers that
17
+ * want explicit error handling.
18
+ * - `.swarm/` containment (AGENTS.md invariant 4): the path is derived from the
19
+ * `directory` argument injected by `createSwarmTool` / hook constructors — never
20
+ * from the process working directory.
21
+ */
22
+ import type { KnowledgeApplicationRecord } from './knowledge-types.js';
23
+ /** Current event-log record schema version. Bump when the on-disk shape changes. */
24
+ export declare const KNOWLEDGE_EVENT_SCHEMA_VERSION = 1;
25
+ /**
26
+ * Soft cap on `.swarm/knowledge-events.jsonl` line count. Enforced FIFO after
27
+ * each append: oldest lines are trimmed when total exceeds the cap. Sized for
28
+ * months of activity on a typical project (~5k retrieval/receipt events).
29
+ */
30
+ export declare const MAX_EVENT_LOG_ENTRIES = 5000;
31
+ /** Retrieval modes that surface knowledge to an agent. */
32
+ export type RetrievalEventMode = 'manual' | 'auto_injection' | 'coder_context' | 'review_context' | 'curator';
33
+ /** A retrieval: a query returned a ranked set of knowledge entries. */
34
+ export interface RetrievedEvent {
35
+ type: 'retrieved';
36
+ schema_version?: number;
37
+ event_id: string;
38
+ trace_id: string;
39
+ timestamp: string;
40
+ session_id: string;
41
+ phase?: string;
42
+ task_id?: string;
43
+ agent: string;
44
+ query: string;
45
+ retrieval_mode: RetrievalEventMode;
46
+ result_ids: string[];
47
+ /** id → 1-based rank in the result list. */
48
+ ranks: Record<string, number>;
49
+ /** id → final score. */
50
+ scores: Record<string, number>;
51
+ score_breakdown?: Record<string, unknown>;
52
+ }
53
+ /** A receipt: an agent explicitly considered a specific knowledge entry. */
54
+ export interface ReceiptEvent {
55
+ type: 'acknowledged' | 'applied' | 'ignored' | 'contradicted' | 'violated';
56
+ schema_version?: number;
57
+ event_id: string;
58
+ trace_id: string;
59
+ knowledge_id: string;
60
+ timestamp: string;
61
+ session_id: string;
62
+ phase?: string;
63
+ task_id?: string;
64
+ agent: string;
65
+ reason?: string;
66
+ evidence?: {
67
+ files?: string[];
68
+ commands?: string[];
69
+ tests?: string[];
70
+ summary?: string;
71
+ };
72
+ }
73
+ /** An outcome: a task/phase succeeded or failed, optionally attributed to an entry. */
74
+ export interface OutcomeEvent {
75
+ type: 'outcome';
76
+ schema_version?: number;
77
+ event_id: string;
78
+ trace_id?: string;
79
+ knowledge_id?: string;
80
+ timestamp: string;
81
+ task_id?: string;
82
+ phase?: string;
83
+ outcome: 'success' | 'failure' | 'partial';
84
+ evidence_summary: string;
85
+ }
86
+ /** An audit tombstone: an entry was archived / quarantined / purged. */
87
+ export interface ArchivedEvent {
88
+ type: 'archived';
89
+ schema_version?: number;
90
+ event_id: string;
91
+ timestamp: string;
92
+ entry_id: string;
93
+ actor: string;
94
+ reason: string;
95
+ mode: 'archive' | 'quarantine' | 'purge';
96
+ evidence?: string;
97
+ previous_status?: string;
98
+ }
99
+ export type KnowledgeEvent = RetrievedEvent | ReceiptEvent | OutcomeEvent | ArchivedEvent;
100
+ export type KnowledgeEventType = KnowledgeEvent['type'];
101
+ /**
102
+ * Event shape accepted by {@link appendKnowledgeEvent} / {@link recordKnowledgeEvent}.
103
+ * `event_id` and `timestamp` are optional on input — they are filled in on write.
104
+ * Distributes over the union so each variant keeps its required discriminant
105
+ * fields.
106
+ */
107
+ export type KnowledgeEventInput = KnowledgeEvent extends infer T ? T extends KnowledgeEvent ? Omit<T, 'event_id' | 'timestamp'> & {
108
+ event_id?: string;
109
+ timestamp?: string;
110
+ } : never : never;
111
+ /** Receipt event verbs that reference a single knowledge_id. */
112
+ export declare const RECEIPT_EVENT_TYPES: ReadonlySet<string>;
113
+ /** Returns `.swarm/knowledge-events.jsonl` for the given project directory. */
114
+ export declare function resolveKnowledgeEventsPath(directory: string): string;
115
+ /** Generate a fresh trace id. One per retrieval; receipts reference it. */
116
+ export declare function newTraceId(): string;
117
+ /** Generate a fresh event id. Unique per appended event. */
118
+ export declare function newEventId(): string;
119
+ /**
120
+ * Append one event to the log, filling in event_id / timestamp if absent.
121
+ * Returns the fully-populated event that was written.
122
+ *
123
+ * Throws on I/O failure — callers on hot paths should prefer
124
+ * {@link recordKnowledgeEvent}, which swallows errors.
125
+ */
126
+ export declare function appendKnowledgeEvent(directory: string, event: KnowledgeEventInput): Promise<KnowledgeEvent>;
127
+ /**
128
+ * Fail-open variant of {@link appendKnowledgeEvent} for hot paths (hooks, tool
129
+ * execution). Never throws; logs a warning and returns null on failure.
130
+ */
131
+ export declare function recordKnowledgeEvent(directory: string, event: KnowledgeEventInput): Promise<KnowledgeEvent | null>;
132
+ /**
133
+ * Read all events from the log. Skips corrupted JSONL lines (logging a warning
134
+ * for each) and returns an empty array when the file does not exist — mirrors
135
+ * `readKnowledge` in knowledge-store.ts.
136
+ */
137
+ export declare function readKnowledgeEvents(directory: string): Promise<KnowledgeEvent[]>;
138
+ /**
139
+ * Derived per-entry counters. This is the rollup shape recomputed from the
140
+ * event log; it maps onto the v2 `RetrievalOutcome` counter fields plus the v3
141
+ * `contradicted_count`.
142
+ */
143
+ export interface CounterRollup {
144
+ shown_count: number;
145
+ acknowledged_count: number;
146
+ applied_explicit_count: number;
147
+ ignored_count: number;
148
+ violated_count: number;
149
+ contradicted_count: number;
150
+ succeeded_after_shown_count: number;
151
+ failed_after_shown_count: number;
152
+ /**
153
+ * Count of partial outcomes. Tracked separately so it surfaces in
154
+ * diagnostics but never contributes to `computeOutcomeSignal` (partial is
155
+ * deliberately ambiguous — it neither rewards nor penalizes).
156
+ */
157
+ partial_after_shown_count: number;
158
+ last_applied_at?: string;
159
+ last_acknowledged_at?: string;
160
+ }
161
+ /**
162
+ * Recompute per-entry counters deterministically from the immutable event log,
163
+ * optionally folding in legacy `knowledge-application.jsonl` records.
164
+ *
165
+ * Determinism & double-counting: the ONLY outcome our code writes to both logs
166
+ * is "shown" — the injector emits both a legacy `recordKnowledgeShown` record
167
+ * AND a `retrieved` event for the same injection. Every other legacy verb
168
+ * (`applied`/`ignored`/`violated`/`acknowledged`) originates from `knowledge_ack`
169
+ * and has no event-log counterpart; the event-sourced equivalents come from the
170
+ * separate `knowledge_receipt` tool. So the race-free rule is:
171
+ *
172
+ * - legacy `shown`: folded ONLY when the event log contains no `retrieved`
173
+ * event (i.e. a pure pre-migration install). Once any `retrieved` event
174
+ * exists, `shown_count` is derived from events alone, eliminating the
175
+ * timestamp-race double-count.
176
+ * - legacy non-`shown` verbs: always folded (no event counterpart, so no
177
+ * double count), preserving pre-migration history.
178
+ *
179
+ * The result depends only on the input arrays, not on wall-clock time or order.
180
+ *
181
+ * @param events Events from {@link readKnowledgeEvents}.
182
+ * @param legacyRecords Optional legacy application records (any order).
183
+ */
184
+ export declare function recomputeCounters(events: KnowledgeEvent[], legacyRecords?: KnowledgeApplicationRecord[]): Map<string, CounterRollup>;
185
+ export declare const _internals: {
186
+ resolveKnowledgeEventsPath: typeof resolveKnowledgeEventsPath;
187
+ appendKnowledgeEvent: typeof appendKnowledgeEvent;
188
+ recordKnowledgeEvent: typeof recordKnowledgeEvent;
189
+ readKnowledgeEvents: typeof readKnowledgeEvents;
190
+ recomputeCounters: typeof recomputeCounters;
191
+ newTraceId: typeof newTraceId;
192
+ newEventId: typeof newEventId;
193
+ };
@@ -18,7 +18,9 @@ export interface RankedEntry extends KnowledgeEntryBase {
18
18
  };
19
19
  finalScore: number;
20
20
  }
21
- export declare function readMergedKnowledge(directory: string, config: KnowledgeConfig, context?: ProjectContext): Promise<RankedEntry[]>;
21
+ export declare function readMergedKnowledge(directory: string, config: KnowledgeConfig, context?: ProjectContext, opts?: {
22
+ skipScopeFilter?: boolean;
23
+ }): Promise<RankedEntry[]>;
22
24
  export declare function updateRetrievalOutcome(directory: string, phaseInfo: string, phaseSucceeded: boolean): Promise<void>;
23
25
  /** Returns 0..1 score representing trigger/action match strength against the context. */
24
26
  export declare function scoreDirectiveAgainstContext(entry: KnowledgeEntryBase, ctx: KnowledgeRetrievalContext): {
@@ -27,17 +29,8 @@ export declare function scoreDirectiveAgainstContext(entry: KnowledgeEntryBase,
27
29
  agentHit: boolean;
28
30
  score: number;
29
31
  };
30
- /**
31
- * v2: Action-aware retrieval. Returns RankedEntry[] but uses the richer
32
- * KnowledgeRetrievalContext to bias ranking toward entries whose triggers,
33
- * applies_to_tools, applies_to_agents, or directive_priority match the
34
- * current decision point. Falls back to readMergedKnowledge ordering for
35
- * non-matching entries.
36
- */
37
- export declare function readContextualKnowledge(directory: string, config: KnowledgeConfig, ctx: KnowledgeRetrievalContext): Promise<RankedEntry[]>;
38
32
  export declare const _internals: {
39
33
  readMergedKnowledge: typeof readMergedKnowledge;
40
- readContextualKnowledge: typeof readContextualKnowledge;
41
34
  updateRetrievalOutcome: typeof updateRetrievalOutcome;
42
35
  scoreDirectiveAgainstContext: typeof scoreDirectiveAgainstContext;
43
36
  };
@@ -1,5 +1,5 @@
1
1
  /** Core storage layer for the opencode-swarm v6.17 two-tier knowledge system. */
2
- import type { KnowledgeEntryBase, RejectedLesson } from './knowledge-types.js';
2
+ import type { KnowledgeEntryBase, RejectedLesson, RetrievalOutcome } from './knowledge-types.js';
3
3
  export declare function getPlatformConfigDir(): string;
4
4
  export declare function resolveSwarmKnowledgePath(directory: string): string;
5
5
  export declare function resolveSwarmRejectedPath(directory: string): string;
@@ -40,6 +40,8 @@ export declare function findNearDuplicate<T extends {
40
40
  lesson: string;
41
41
  }>(candidate: string, entries: T[], threshold?: number): T | undefined;
42
42
  export declare function computeConfidence(confirmedByCount: number, autoGenerated: boolean): number;
43
+ export declare const OUTCOME_SIGNAL_SMOOTHING = 4;
44
+ export declare function computeOutcomeSignal(outcomes?: RetrievalOutcome): number;
43
45
  export declare function inferTags(lesson: string): string[];
44
46
  /**
45
47
  * Batch-update confidence scores on knowledge entries identified by their UUIDs.
@@ -80,6 +82,7 @@ export declare const _internals: {
80
82
  jaccardBigram: typeof jaccardBigram;
81
83
  findNearDuplicate: typeof findNearDuplicate;
82
84
  computeConfidence: typeof computeConfidence;
85
+ computeOutcomeSignal: typeof computeOutcomeSignal;
83
86
  inferTags: typeof inferTags;
84
87
  bumpKnowledgeConfidenceBatch: typeof bumpKnowledgeConfidenceBatch;
85
88
  };
@@ -37,6 +37,8 @@ export interface RetrievalOutcome {
37
37
  ignored_count?: number;
38
38
  /** v2: explicit/inferred violation count (KNOWLEDGE_VIOLATED: id reason=...). */
39
39
  violated_count?: number;
40
+ /** v3: explicit contradiction count (entry contradicted by current evidence). */
41
+ contradicted_count?: number;
40
42
  /** v2: phase-success count after a "shown" (replaces succeeded_after_count). */
41
43
  succeeded_after_shown_count?: number;
42
44
  /** v2: phase-failure count after a "shown" (replaces failed_after_count). */
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Unified knowledge retrieval service.
3
+ *
4
+ * This is the single core through which BOTH manual recall (`knowledge_recall`)
5
+ * and automatic injection (the phase-start injector) retrieve knowledge. It
6
+ * replaces the former split between Jaccard-only manual recall and the
7
+ * metadata-only action-aware injection ranker with one hybrid algorithm:
8
+ *
9
+ * finalScore = TEXT_WEIGHT * textScore (Jaccard query↔lesson)
10
+ * + META_WEIGHT * metadataScore (phase/confidence/keywords)
11
+ * + directiveScore (trigger/action/agent/priority)
12
+ * + boosts (status, generated-skill)
13
+ *
14
+ * Each signal degrades to 0 when its input is absent, so a query-only call
15
+ * (manual recall) is text-dominated while a context-only call (injection) is
16
+ * metadata/directive-dominated — without branching into two algorithms.
17
+ *
18
+ * Responsibilities (per the P0 plan):
19
+ * load → normalize → filter archived/quarantined → dedup (hive wins) →
20
+ * apply scope & agent-role constraints → hybrid score → rerank
21
+ * (critical force-include) → emit a `retrieved` event → return trace_id.
22
+ */
23
+ import { type RetrievalEventMode } from './knowledge-events.js';
24
+ import { type RankedEntry } from './knowledge-reader.js';
25
+ import type { KnowledgeConfig, KnowledgeRetrievalContext } from './knowledge-types.js';
26
+ export interface SearchKnowledgeParams {
27
+ directory: string;
28
+ config: KnowledgeConfig;
29
+ /** Free-text query (manual recall). */
30
+ query?: string;
31
+ /** Action-aware decision-point context (injection / context packs). */
32
+ context?: KnowledgeRetrievalContext;
33
+ /** Retrieval mode — recorded on the emitted event. */
34
+ mode: RetrievalEventMode;
35
+ /** Agent role doing the retrieval (used for role scoping + the event). */
36
+ agent?: string;
37
+ /** Session id for the emitted event. */
38
+ sessionId?: string;
39
+ /** Tier filter. Default 'all'. */
40
+ tier?: 'all' | 'swarm' | 'hive';
41
+ /** Max results to return. Falls back to config.max_inject_count. */
42
+ maxResults?: number;
43
+ /** Emit a `retrieved` event (default true). */
44
+ emitEvent?: boolean;
45
+ /**
46
+ * Apply config.scope_filter (default true). Manual recall passes false so an
47
+ * explicit query can surface non-global-scoped lessons.
48
+ */
49
+ applyScopeFilter?: boolean;
50
+ /**
51
+ * Read the hive tier regardless of config.hive_enabled (default false).
52
+ * Manual recall passes true so `hive_enabled:false` (an injection knob) does
53
+ * not also hide hive entries from explicit queries.
54
+ */
55
+ forceReadHive?: boolean;
56
+ /**
57
+ * Apply agent-role scoping via applies_to_agents (default true). Manual recall
58
+ * passes false so an explicit query is not silently role-gated.
59
+ */
60
+ applyRoleScope?: boolean;
61
+ }
62
+ export interface SearchKnowledgeResult {
63
+ trace_id: string;
64
+ results: RankedEntry[];
65
+ }
66
+ /**
67
+ * Run unified knowledge retrieval. Returns a trace_id (always minted, even when
68
+ * no entries match) and the ranked results. Reading/scoring failures degrade to
69
+ * an empty result set; the event emission is fail-open.
70
+ */
71
+ export declare function searchKnowledge(params: SearchKnowledgeParams): Promise<SearchKnowledgeResult>;
72
+ export declare const _internals: {
73
+ searchKnowledge: typeof searchKnowledge;
74
+ };