omegon 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +3 -0
- package/AGENTS.md +16 -0
- package/LICENSE +15 -0
- package/README.md +289 -0
- package/bin/pi.mjs +30 -0
- package/extensions/00-secrets/index.ts +1126 -0
- package/extensions/01-auth/auth.ts +401 -0
- package/extensions/01-auth/index.ts +289 -0
- package/extensions/auto-compact.ts +42 -0
- package/extensions/bootstrap/deps.ts +291 -0
- package/extensions/bootstrap/index.ts +811 -0
- package/extensions/chronos/chronos.sh +487 -0
- package/extensions/chronos/index.ts +148 -0
- package/extensions/cleave/assessment.ts +754 -0
- package/extensions/cleave/bridge.ts +31 -0
- package/extensions/cleave/conflicts.ts +250 -0
- package/extensions/cleave/dispatcher.ts +808 -0
- package/extensions/cleave/guardrails.ts +426 -0
- package/extensions/cleave/index.ts +3121 -0
- package/extensions/cleave/lifecycle-emitter.ts +20 -0
- package/extensions/cleave/openspec.ts +811 -0
- package/extensions/cleave/planner.ts +260 -0
- package/extensions/cleave/review.ts +579 -0
- package/extensions/cleave/skills.ts +355 -0
- package/extensions/cleave/types.ts +261 -0
- package/extensions/cleave/workspace.ts +861 -0
- package/extensions/cleave/worktree.ts +243 -0
- package/extensions/core-renderers.ts +253 -0
- package/extensions/dashboard/context-gauge.ts +58 -0
- package/extensions/dashboard/file-watch.ts +14 -0
- package/extensions/dashboard/footer.ts +1145 -0
- package/extensions/dashboard/git.ts +185 -0
- package/extensions/dashboard/index.ts +478 -0
- package/extensions/dashboard/memory-audit.ts +34 -0
- package/extensions/dashboard/overlay-data.ts +705 -0
- package/extensions/dashboard/overlay.ts +365 -0
- package/extensions/dashboard/render-utils.ts +54 -0
- package/extensions/dashboard/types.ts +191 -0
- package/extensions/dashboard/uri-helper.ts +45 -0
- package/extensions/debug.ts +69 -0
- package/extensions/defaults.ts +282 -0
- package/extensions/design-tree/dashboard-state.ts +161 -0
- package/extensions/design-tree/design-card.ts +362 -0
- package/extensions/design-tree/index.ts +2130 -0
- package/extensions/design-tree/lifecycle-emitter.ts +41 -0
- package/extensions/design-tree/tree.ts +1607 -0
- package/extensions/design-tree/types.ts +163 -0
- package/extensions/distill.ts +127 -0
- package/extensions/effort/index.ts +395 -0
- package/extensions/effort/tiers.ts +146 -0
- package/extensions/effort/types.ts +105 -0
- package/extensions/lib/git-state.ts +227 -0
- package/extensions/lib/local-models.ts +157 -0
- package/extensions/lib/model-preferences.ts +51 -0
- package/extensions/lib/model-routing.ts +720 -0
- package/extensions/lib/operator-fallback.ts +205 -0
- package/extensions/lib/operator-profile.ts +360 -0
- package/extensions/lib/slash-command-bridge.ts +253 -0
- package/extensions/lib/typebox-helpers.ts +16 -0
- package/extensions/local-inference/index.ts +727 -0
- package/extensions/mcp-bridge/README.md +220 -0
- package/extensions/mcp-bridge/index.ts +951 -0
- package/extensions/mcp-bridge/lib.ts +365 -0
- package/extensions/mcp-bridge/mcp.json +3 -0
- package/extensions/mcp-bridge/package.json +11 -0
- package/extensions/model-budget.ts +752 -0
- package/extensions/offline-driver.ts +403 -0
- package/extensions/openspec/archive-gate.ts +164 -0
- package/extensions/openspec/branch-cleanup.ts +64 -0
- package/extensions/openspec/dashboard-state.ts +50 -0
- package/extensions/openspec/index.ts +1917 -0
- package/extensions/openspec/lifecycle-emitter.ts +65 -0
- package/extensions/openspec/lifecycle-files.ts +70 -0
- package/extensions/openspec/lifecycle.ts +50 -0
- package/extensions/openspec/reconcile.ts +187 -0
- package/extensions/openspec/spec.ts +1385 -0
- package/extensions/openspec/types.ts +98 -0
- package/extensions/project-memory/DESIGN-global-mind.md +198 -0
- package/extensions/project-memory/README.md +202 -0
- package/extensions/project-memory/api-types.ts +382 -0
- package/extensions/project-memory/compaction-policy.ts +29 -0
- package/extensions/project-memory/core.ts +164 -0
- package/extensions/project-memory/embeddings.ts +230 -0
- package/extensions/project-memory/extraction-v2.ts +861 -0
- package/extensions/project-memory/factstore.ts +2177 -0
- package/extensions/project-memory/index.ts +3459 -0
- package/extensions/project-memory/injection-metrics.ts +91 -0
- package/extensions/project-memory/jsonl-io.ts +12 -0
- package/extensions/project-memory/lifecycle.ts +331 -0
- package/extensions/project-memory/migration.ts +293 -0
- package/extensions/project-memory/package.json +9 -0
- package/extensions/project-memory/sci-renderers.ts +7 -0
- package/extensions/project-memory/template.ts +103 -0
- package/extensions/project-memory/triggers.ts +52 -0
- package/extensions/project-memory/types.ts +102 -0
- package/extensions/render/composition/fonts/Inter-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Inter-Regular.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Regular.ttf +0 -0
- package/extensions/render/composition/package-lock.json +534 -0
- package/extensions/render/composition/package.json +22 -0
- package/extensions/render/composition/render.mjs +246 -0
- package/extensions/render/composition/test-comp.tsx +87 -0
- package/extensions/render/composition/types.ts +24 -0
- package/extensions/render/excalidraw/UPSTREAM.md +81 -0
- package/extensions/render/excalidraw/elements.ts +764 -0
- package/extensions/render/excalidraw/index.ts +66 -0
- package/extensions/render/excalidraw/types.ts +223 -0
- package/extensions/render/excalidraw-renderer/pyproject.toml +8 -0
- package/extensions/render/excalidraw-renderer/render_excalidraw.py +182 -0
- package/extensions/render/excalidraw-renderer/render_template.html +59 -0
- package/extensions/render/index.ts +830 -0
- package/extensions/render/native-diagrams/index.ts +57 -0
- package/extensions/render/native-diagrams/motifs.ts +542 -0
- package/extensions/render/native-diagrams/raster.ts +8 -0
- package/extensions/render/native-diagrams/scene.ts +75 -0
- package/extensions/render/native-diagrams/spec.ts +204 -0
- package/extensions/render/native-diagrams/svg.ts +116 -0
- package/extensions/sci-ui.ts +304 -0
- package/extensions/session-log.ts +174 -0
- package/extensions/shared-state.ts +146 -0
- package/extensions/spinner-verbs.ts +91 -0
- package/extensions/style.ts +281 -0
- package/extensions/terminal-title.ts +191 -0
- package/extensions/tool-profile/index.ts +291 -0
- package/extensions/tool-profile/profiles.ts +290 -0
- package/extensions/types.d.ts +9 -0
- package/extensions/vault/index.ts +185 -0
- package/extensions/version-check.ts +90 -0
- package/extensions/view/index.ts +859 -0
- package/extensions/view/uri-resolver.ts +148 -0
- package/extensions/web-search/index.ts +182 -0
- package/extensions/web-search/providers.ts +121 -0
- package/extensions/web-ui/index.ts +110 -0
- package/extensions/web-ui/server.ts +265 -0
- package/extensions/web-ui/state.ts +462 -0
- package/extensions/web-ui/static/index.html +145 -0
- package/extensions/web-ui/types.ts +284 -0
- package/package.json +76 -0
- package/prompts/init.md +75 -0
- package/prompts/new-repo.md +54 -0
- package/prompts/oci-login.md +56 -0
- package/prompts/status.md +50 -0
- package/settings.json +4 -0
- package/skills/cleave/SKILL.md +218 -0
- package/skills/git/SKILL.md +209 -0
- package/skills/git/_reference/ci-validation.md +204 -0
- package/skills/oci/SKILL.md +338 -0
- package/skills/openspec/SKILL.md +346 -0
- package/skills/pi-extensions/SKILL.md +191 -0
- package/skills/pi-tui/SKILL.md +517 -0
- package/skills/python/SKILL.md +189 -0
- package/skills/rust/SKILL.md +268 -0
- package/skills/security/SKILL.md +206 -0
- package/skills/style/SKILL.md +264 -0
- package/skills/typescript/SKILL.md +225 -0
- package/skills/vault/SKILL.md +102 -0
- package/themes/alpharius-legacy.json +85 -0
- package/themes/alpharius.conf +59 -0
- package/themes/alpharius.json +88 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* project-memory/api-types — Omega /api/memory/* HTTP contract
|
|
3
|
+
*
|
|
4
|
+
* This file is the source of truth for the wire protocol between:
|
|
5
|
+
* - The TypeScript auspex bridge (TS extension, pi API surface)
|
|
6
|
+
* - The Rust Omega daemon (/api/memory/* Axum routes)
|
|
7
|
+
*
|
|
8
|
+
* Field names are snake_case throughout to match Rust serde conventions
|
|
9
|
+
* (#[serde(rename_all = "snake_case")] or #[serde(deny_unknown_fields)]).
|
|
10
|
+
*
|
|
11
|
+
* When the Rust implementation is built, each interface here becomes a
|
|
12
|
+
* corresponding Rust struct deriving Serialize, Deserialize, with field
|
|
13
|
+
* names identical to these. Any deviation is a bug in the Rust port.
|
|
14
|
+
*
|
|
15
|
+
* Versioning: the HTTP API is versioned via the Accept header or a /v1/ prefix.
|
|
16
|
+
* Breaking changes increment the major version. Additive fields use Option<T>
|
|
17
|
+
* with serde defaults in Rust and optional typing here.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { SectionName } from "./template.ts";
|
|
21
|
+
import type { DecayProfileName } from "./core.ts";
|
|
22
|
+
|
|
23
|
+
// ─── Core record types ────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A memory fact as returned by the API.
|
|
27
|
+
* Mirrors the `facts` SQLite table — all fields except `embedding` (DB-only).
|
|
28
|
+
*
|
|
29
|
+
* Rust: src/memory/types.rs::Fact
|
|
30
|
+
*/
|
|
31
|
+
export interface FactRecord {
|
|
32
|
+
id: string;
|
|
33
|
+
mind: string;
|
|
34
|
+
content: string;
|
|
35
|
+
section: SectionName;
|
|
36
|
+
status: FactStatus;
|
|
37
|
+
confidence: number;
|
|
38
|
+
reinforcement_count: number;
|
|
39
|
+
decay_rate: number;
|
|
40
|
+
/** Discriminant for the decay profile used at write time. Persisted in DB.
|
|
41
|
+
* Allows correct confidence computation at read time. Default: "standard". */
|
|
42
|
+
decay_profile: DecayProfileName;
|
|
43
|
+
last_reinforced: string; // ISO 8601
|
|
44
|
+
created_at: string; // ISO 8601
|
|
45
|
+
/** Lamport logical timestamp. Incremented on every mutation.
|
|
46
|
+
* On git-sync conflict: higher version always wins. Default on import: 0. */
|
|
47
|
+
version: number;
|
|
48
|
+
superseded_by?: string; // fact ID; present only when status === "superseded"
|
|
49
|
+
source?: string; // origin annotation: "lifecycle:openspec:X" | "extraction" | "manual"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type FactStatus = "active" | "archived" | "superseded";
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* A session episode narrative.
|
|
56
|
+
* Rust: src/memory/types.rs::Episode
|
|
57
|
+
*/
|
|
58
|
+
export interface EpisodeRecord {
|
|
59
|
+
id: string;
|
|
60
|
+
mind: string;
|
|
61
|
+
date: string; // ISO 8601 date
|
|
62
|
+
title: string;
|
|
63
|
+
narrative: string; // free-form prose summary
|
|
64
|
+
created_at: string; // ISO 8601
|
|
65
|
+
/** Design node IDs touched this session. Optional; populated by extraction subagent. */
|
|
66
|
+
affected_nodes?: string[];
|
|
67
|
+
/** OpenSpec change names touched this session. */
|
|
68
|
+
affected_changes?: string[];
|
|
69
|
+
/** git diff --stat summary of files changed. */
|
|
70
|
+
files_changed?: string[];
|
|
71
|
+
/** Semantic tags: "architecture", "bugfix", "refactor", "investigation", etc. */
|
|
72
|
+
tags?: string[];
|
|
73
|
+
/** Rough activity measure — number of LLM tool calls in this session. */
|
|
74
|
+
tool_calls_count?: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A directional relationship between two facts.
|
|
79
|
+
* Rust: src/memory/types.rs::Edge
|
|
80
|
+
*/
|
|
81
|
+
export interface EdgeRecord {
|
|
82
|
+
id: string;
|
|
83
|
+
source_id: string;
|
|
84
|
+
target_id: string;
|
|
85
|
+
relation: string; // "depends_on", "contradicts", "generalizes", etc.
|
|
86
|
+
description?: string;
|
|
87
|
+
weight: number; // 0.0–1.0; higher = stronger relationship
|
|
88
|
+
created_at: string; // ISO 8601
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─── POST /api/memory/facts ───────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
export interface StoreFactRequest {
|
|
94
|
+
mind: string;
|
|
95
|
+
content: string;
|
|
96
|
+
section: SectionName;
|
|
97
|
+
/** Decay profile to use for this fact. Defaults to "standard". */
|
|
98
|
+
decay_profile?: DecayProfileName;
|
|
99
|
+
/** Optional initial confidence override (0.0–1.0). Default: 1.0. */
|
|
100
|
+
confidence?: number;
|
|
101
|
+
/** Origin annotation for provenance tracking. */
|
|
102
|
+
source?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface StoreFactResponse {
|
|
106
|
+
/** The ID of the stored or matched fact. */
|
|
107
|
+
id: string;
|
|
108
|
+
/** What happened: new fact stored, existing fact reinforced, or exact duplicate found. */
|
|
109
|
+
action: "stored" | "reinforced" | "deduplicated";
|
|
110
|
+
fact: FactRecord;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ─── GET /api/memory/context ──────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
export interface ContextRequest {
|
|
116
|
+
mind: string;
|
|
117
|
+
/** Raw text query used for semantic fact selection. Omega embeds it.
|
|
118
|
+
* Required when mode === "semantic". */
|
|
119
|
+
query?: string;
|
|
120
|
+
mode: InjectionMode;
|
|
121
|
+
/** Pinned fact IDs — always included first, before semantic or bulk selection. */
|
|
122
|
+
working_memory?: string[];
|
|
123
|
+
/** Maximum character budget for the rendered context block. Default: 12000.
|
|
124
|
+
* Omega stops adding facts when the budget would be exceeded. */
|
|
125
|
+
max_chars?: number;
|
|
126
|
+
/** Per-section maximum fact counts. Overrides Omega's default caps. */
|
|
127
|
+
section_caps?: Partial<Record<SectionName, number>>;
|
|
128
|
+
/** Number of recent session episodes to include. Default: 1. 0 to suppress. */
|
|
129
|
+
episodes?: number;
|
|
130
|
+
/** Whether to include global knowledge (cross-project facts).
|
|
131
|
+
* In semantic mode: included only if relevant (similarity ≥ 0.45).
|
|
132
|
+
* In bulk mode: excluded unless explicitly true. Default: false. */
|
|
133
|
+
include_global?: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export type InjectionMode = "semantic" | "bulk";
|
|
137
|
+
|
|
138
|
+
export interface ContextResponse {
|
|
139
|
+
/** Pre-rendered markdown block suitable for direct LLM context injection. */
|
|
140
|
+
markdown: string;
|
|
141
|
+
stats: ContextStats;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface ContextStats {
|
|
145
|
+
facts_injected: number;
|
|
146
|
+
mode: InjectionMode;
|
|
147
|
+
episodes_injected: number;
|
|
148
|
+
global_facts_injected: number;
|
|
149
|
+
char_count: number;
|
|
150
|
+
/** True if some facts were dropped to stay within max_chars budget. */
|
|
151
|
+
budget_exhausted: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── POST /api/memory/recall ──────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
export interface RecallRequest {
|
|
157
|
+
mind: string;
|
|
158
|
+
/** Raw text query. Omega embeds it and runs cosine similarity × decay scoring. */
|
|
159
|
+
query: string;
|
|
160
|
+
/** Number of results. Default: 10. */
|
|
161
|
+
k?: number;
|
|
162
|
+
/** Minimum cosine similarity. Default: 0.3. */
|
|
163
|
+
min_similarity?: number;
|
|
164
|
+
/** Optional section filter. */
|
|
165
|
+
section?: SectionName;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface RecallResponse {
|
|
169
|
+
facts: ScoredFact[];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface ScoredFact extends FactRecord {
|
|
173
|
+
/** Raw cosine similarity between query vector and fact vector (0.0–1.0). */
|
|
174
|
+
similarity: number;
|
|
175
|
+
/** Combined score: similarity × decay-adjusted confidence. Used for ranking. */
|
|
176
|
+
score: number;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ─── GET /api/memory/facts ────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
export interface ListFactsRequest {
|
|
182
|
+
mind: string;
|
|
183
|
+
section?: SectionName;
|
|
184
|
+
status?: FactStatus;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface ListFactsResponse {
|
|
188
|
+
facts: FactRecord[];
|
|
189
|
+
total: number;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ─── PATCH /api/memory/facts/:id ─────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
export interface UpdateFactRequest {
|
|
195
|
+
action: UpdateFactAction;
|
|
196
|
+
/** For "supersede": the replacement fact content. */
|
|
197
|
+
content?: string;
|
|
198
|
+
/** For "supersede": the section for the replacement fact.
|
|
199
|
+
* Defaults to the original fact's section. */
|
|
200
|
+
section?: SectionName;
|
|
201
|
+
/** For "supersede": the decay profile for the replacement fact.
|
|
202
|
+
* Defaults to the original fact's decay profile. */
|
|
203
|
+
decay_profile?: DecayProfileName;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export type UpdateFactAction = "reinforce" | "archive" | "supersede";
|
|
207
|
+
|
|
208
|
+
export interface UpdateFactResponse {
|
|
209
|
+
action: UpdateFactAction;
|
|
210
|
+
fact: FactRecord;
|
|
211
|
+
/** Present when action === "supersede". The new replacement fact. */
|
|
212
|
+
new_fact?: FactRecord;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ─── POST /api/memory/edges ───────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
export interface CreateEdgeRequest {
|
|
218
|
+
source_id: string;
|
|
219
|
+
target_id: string;
|
|
220
|
+
/** Short verb phrase: "depends_on", "contradicts", "enables", "generalizes", etc. */
|
|
221
|
+
relation: string;
|
|
222
|
+
description?: string;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface CreateEdgeResponse {
|
|
226
|
+
edge: EdgeRecord;
|
|
227
|
+
/** True if an existing edge was reinforced (weight increased) rather than created. */
|
|
228
|
+
reinforced: boolean;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ─── GET /api/memory/export ───────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Response: Content-Type: application/x-ndjson
|
|
235
|
+
* Each line is one JSONL record: FactRecord | EpisodeRecord | EdgeRecord | MindRecord
|
|
236
|
+
* Line ordering: minds first, then facts, then edges, then episodes.
|
|
237
|
+
* Deterministic output (sorted by id within each type) for stable git diffs.
|
|
238
|
+
*/
|
|
239
|
+
export interface ExportOptions {
|
|
240
|
+
mind: string;
|
|
241
|
+
/** Include archived and superseded facts. Default: true (full history). */
|
|
242
|
+
include_archived?: boolean;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ─── POST /api/memory/import ─────────────────────────────────────────────────
|
|
246
|
+
|
|
247
|
+
export interface ImportRequest {
|
|
248
|
+
/** Raw JSONL content. Each line is a fact, episode, edge, or mind record.
|
|
249
|
+
* Conflict resolution: higher `version` (Lamport timestamp) always wins.
|
|
250
|
+
* Facts without `version` field get version=0 on import. */
|
|
251
|
+
jsonl: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface ImportResponse {
|
|
255
|
+
imported: number; // new records
|
|
256
|
+
reinforced: number; // existing records with lower version updated
|
|
257
|
+
skipped: number; // same or lower version, no change
|
|
258
|
+
errors: number; // lines that failed to parse
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ─── POST /api/memory/episodes ───────────────────────────────────────────────
|
|
262
|
+
|
|
263
|
+
export interface StoreEpisodeRequest {
|
|
264
|
+
mind: string;
|
|
265
|
+
title: string;
|
|
266
|
+
narrative: string;
|
|
267
|
+
/** ISO 8601 date. Default: today. */
|
|
268
|
+
date?: string;
|
|
269
|
+
affected_nodes?: string[];
|
|
270
|
+
affected_changes?: string[];
|
|
271
|
+
files_changed?: string[];
|
|
272
|
+
tags?: string[];
|
|
273
|
+
tool_calls_count?: number;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export interface StoreEpisodeResponse {
|
|
277
|
+
episode: EpisodeRecord;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ─── GET /api/memory/episodes ────────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
export interface ListEpisodesRequest {
|
|
283
|
+
mind: string;
|
|
284
|
+
/** Number of most recent episodes to return. Default: 3. */
|
|
285
|
+
k?: number;
|
|
286
|
+
/** Optional semantic query — returns episodes ranked by narrative similarity. */
|
|
287
|
+
query?: string;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export interface ListEpisodesResponse {
|
|
291
|
+
episodes: EpisodeRecord[];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ─── POST /api/memory/parse-extraction ───────────────────────────────────────
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Validate and parse raw extraction subagent output.
|
|
298
|
+
* The TS extraction subagent sends LLM-generated JSONL here for typed validation.
|
|
299
|
+
* Omega parses each line against the ExtractionAction schema and returns
|
|
300
|
+
* validated actions + a list of lines that failed parsing.
|
|
301
|
+
*
|
|
302
|
+
* Rust: src/memory/extraction.rs::parse_extraction_output
|
|
303
|
+
*/
|
|
304
|
+
export interface ParseExtractionRequest {
|
|
305
|
+
/** Raw JSONL output from the extraction subagent. */
|
|
306
|
+
raw_output: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export interface ParseExtractionResponse {
|
|
310
|
+
actions: ExtractionAction[];
|
|
311
|
+
/** Lines that failed schema validation — for debugging. */
|
|
312
|
+
errors: string[];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export type ExtractionAction =
|
|
316
|
+
| { type: "store"; content: string; section: SectionName; decay_profile?: DecayProfileName }
|
|
317
|
+
| { type: "archive"; id: string }
|
|
318
|
+
| { type: "supersede"; id: string; content: string; section?: SectionName }
|
|
319
|
+
| { type: "connect"; source_id: string; target_id: string; relation: string; description?: string };
|
|
320
|
+
|
|
321
|
+
// ─── GET /api/memory/stats ────────────────────────────────────────────────────
|
|
322
|
+
|
|
323
|
+
export interface MemoryStatsResponse {
|
|
324
|
+
mind: string;
|
|
325
|
+
total_facts: number;
|
|
326
|
+
active_facts: number;
|
|
327
|
+
archived_facts: number;
|
|
328
|
+
superseded_facts: number;
|
|
329
|
+
facts_with_vectors: number;
|
|
330
|
+
embedding_model?: string; // null if no vectors exist
|
|
331
|
+
embedding_dims?: number;
|
|
332
|
+
episodes: number;
|
|
333
|
+
edges: number;
|
|
334
|
+
db_size_bytes: number;
|
|
335
|
+
/** Lamport clock high-water mark — max version seen in this DB. */
|
|
336
|
+
version_hwm: number;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ─── JSONL wire format ────────────────────────────────────────────────────────
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* The canonical JSONL line types for git-sync.
|
|
343
|
+
* Each line is one of these discriminated union members.
|
|
344
|
+
* Rust: src/memory/jsonl.rs — use serde(tag = "type") for tagged deserialization.
|
|
345
|
+
*/
|
|
346
|
+
export type JsonlRecord =
|
|
347
|
+
| ({ type: "fact" } & FactRecord)
|
|
348
|
+
| ({ type: "episode" } & EpisodeRecord)
|
|
349
|
+
| ({ type: "edge" } & EdgeRecord)
|
|
350
|
+
| ({ type: "mind" } & MindRecord);
|
|
351
|
+
|
|
352
|
+
export interface MindRecord {
|
|
353
|
+
name: string;
|
|
354
|
+
created_at: string;
|
|
355
|
+
description?: string;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ─── Embedding metadata ───────────────────────────────────────────────────────
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Embedding model registration.
|
|
362
|
+
* Stored in `embedding_metadata` table — one row per model ever used.
|
|
363
|
+
* Facts in `facts_vec` carry a `model_name` FK to this table.
|
|
364
|
+
*
|
|
365
|
+
* Mismatch between query vector dims and stored model dims is a hard error:
|
|
366
|
+
* EmbeddingDimensionMismatch { expected: u32, got: u32, model: String }
|
|
367
|
+
*
|
|
368
|
+
* Rust: src/memory/vectors.rs::EmbeddingMetadata
|
|
369
|
+
*/
|
|
370
|
+
export interface EmbeddingMetadata {
|
|
371
|
+
model_name: string; // e.g., "qwen3-embedding:0.6b"
|
|
372
|
+
dims: number; // e.g., 384
|
|
373
|
+
inserted_at: string; // ISO 8601 — when first used
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export interface EmbeddingDimensionMismatchError {
|
|
377
|
+
error: "EmbeddingDimensionMismatch";
|
|
378
|
+
expected_dims: number;
|
|
379
|
+
got_dims: number;
|
|
380
|
+
stored_model: string;
|
|
381
|
+
message: string;
|
|
382
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for compaction policy and prompt hygiene.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { MemoryConfig } from "./types.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Redact transient clipboard image temp paths captured from pi's clipboard paste flow.
|
|
9
|
+
* These files live under macOS temp directories and become stale/noisy immediately.
|
|
10
|
+
*/
|
|
11
|
+
export function sanitizeCompactionText(input: string): string {
|
|
12
|
+
return input.replace(
|
|
13
|
+
/\/var\/folders\/[A-Za-z0-9_-]+\/[A-Za-z0-9_-]+\/T\/pi-clipboard-[A-Fa-f0-9-]+\.(?:png|jpe?g|gif|webp)/g,
|
|
14
|
+
"[clipboard image attachment]",
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Whether project-memory should intercept compaction before pi core.
|
|
20
|
+
* Local interception is only enabled for explicit local policy or fallback retry.
|
|
21
|
+
*/
|
|
22
|
+
export function shouldInterceptCompaction(
|
|
23
|
+
effortCompaction: "local" | "retribution" | "victory" | "gloriana" | undefined,
|
|
24
|
+
config: MemoryConfig,
|
|
25
|
+
useLocalCompaction: boolean,
|
|
26
|
+
): boolean {
|
|
27
|
+
const liveCompactionLocal = effortCompaction ? effortCompaction === "local" : config.compactionLocalFirst;
|
|
28
|
+
return (liveCompactionLocal || useLocalCompaction) && config.compactionLocalFallback;
|
|
29
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* project-memory/core — Pure computation functions
|
|
3
|
+
*
|
|
4
|
+
* Zero dependencies: no DB, no pi API, no Node.js built-ins except crypto
|
|
5
|
+
* (available in all JS environments). This module is the direct Rust port target.
|
|
6
|
+
*
|
|
7
|
+
* Rust equivalents:
|
|
8
|
+
* computeConfidence → src/memory/decay.rs::compute_confidence
|
|
9
|
+
* cosineSimilarity → src/memory/vectors.rs::cosine_similarity
|
|
10
|
+
* vectorToBlob → src/memory/vectors.rs::vector_to_blob
|
|
11
|
+
* blobToVector → src/memory/vectors.rs::blob_to_vector
|
|
12
|
+
* contentHash → src/memory/store.rs::content_hash
|
|
13
|
+
* normalizeForHash → src/memory/store.rs::normalize_for_hash
|
|
14
|
+
*
|
|
15
|
+
* Any behavioural change here must be reflected in the Rust implementation
|
|
16
|
+
* and verified by cross-impl tests (same inputs → same outputs).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import * as crypto from "node:crypto";
|
|
20
|
+
|
|
21
|
+
// ─── Decay profiles ──────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
/** Project-level decay. Base half-life 14d; each reinforcement extends by 1.8×. */
|
|
24
|
+
export const DECAY = {
|
|
25
|
+
baseRate: 0.05, // ≈ ln(2)/14 — single unreinforced fact fades in ~2 weeks
|
|
26
|
+
reinforcementFactor: 1.8,
|
|
27
|
+
minimumConfidence: 0.1,
|
|
28
|
+
halfLifeDays: 14,
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
/** Global-level decay. Shorter base (30d); cross-project reinforcement dramatically extends. */
|
|
32
|
+
export const GLOBAL_DECAY = {
|
|
33
|
+
baseRate: Math.LN2 / 30,
|
|
34
|
+
reinforcementFactor: 2.5,
|
|
35
|
+
minimumConfidence: 0.1,
|
|
36
|
+
halfLifeDays: 30,
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Recent Work decay — ephemeral session breadcrumbs.
|
|
41
|
+
* halfLifeDays=2: written Monday, gone by Wednesday at ~50%.
|
|
42
|
+
* reinforcementFactor=1.0: reinforcement does NOT extend half-life.
|
|
43
|
+
*/
|
|
44
|
+
export const RECENT_WORK_DECAY = {
|
|
45
|
+
baseRate: Math.LN2 / 2,
|
|
46
|
+
reinforcementFactor: 1.0,
|
|
47
|
+
minimumConfidence: 0.01,
|
|
48
|
+
halfLifeDays: 2,
|
|
49
|
+
} as const;
|
|
50
|
+
|
|
51
|
+
export type DecayProfile = typeof DECAY | typeof GLOBAL_DECAY | typeof RECENT_WORK_DECAY;
|
|
52
|
+
|
|
53
|
+
/** Stored profile discriminant — persisted in the `decay_profile` DB column. */
|
|
54
|
+
export type DecayProfileName = "standard" | "global" | "recent_work";
|
|
55
|
+
|
|
56
|
+
/** Map DB column value → DecayProfile object. Exhaustive — all names must be handled. */
|
|
57
|
+
export function resolveDecayProfile(name: DecayProfileName): DecayProfile {
|
|
58
|
+
switch (name) {
|
|
59
|
+
case "standard": return DECAY;
|
|
60
|
+
case "global": return GLOBAL_DECAY;
|
|
61
|
+
case "recent_work": return RECENT_WORK_DECAY;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Maximum effective half-life regardless of reinforcement count.
|
|
67
|
+
* Prevents immortal facts — even highly reinforced facts decay eventually.
|
|
68
|
+
* Facts needing longer survival must be pinned via memory_focus.
|
|
69
|
+
*/
|
|
70
|
+
const MAX_HALF_LIFE_DAYS = 90;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Compute current confidence for a fact based on time since last reinforcement.
|
|
74
|
+
*
|
|
75
|
+
* halfLife = profile.halfLifeDays × (profile.reinforcementFactor ^ (reinforcement_count - 1))
|
|
76
|
+
* halfLife = clamp(halfLife, 0, MAX_HALF_LIFE_DAYS)
|
|
77
|
+
* confidence = e^(−ln(2) × daysSinceReinforced / halfLife)
|
|
78
|
+
*
|
|
79
|
+
* Rust port: src/memory/decay.rs::compute_confidence
|
|
80
|
+
* Must produce bit-compatible results for the same float inputs.
|
|
81
|
+
*/
|
|
82
|
+
export function computeConfidence(
|
|
83
|
+
daysSinceReinforced: number,
|
|
84
|
+
reinforcementCount: number,
|
|
85
|
+
profile: DecayProfile = DECAY,
|
|
86
|
+
): number {
|
|
87
|
+
const rawHalfLife = profile.halfLifeDays * Math.pow(profile.reinforcementFactor, reinforcementCount - 1);
|
|
88
|
+
const halfLife = Math.min(rawHalfLife, MAX_HALF_LIFE_DAYS);
|
|
89
|
+
const confidence = Math.exp(-Math.LN2 * daysSinceReinforced / halfLife);
|
|
90
|
+
return Math.max(confidence, 0);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ─── Vector math ─────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Cosine similarity between two Float32Arrays.
|
|
97
|
+
* Returns 0 if either vector has zero norm or dimensions differ.
|
|
98
|
+
*
|
|
99
|
+
* Rust port: src/memory/vectors.rs::cosine_similarity
|
|
100
|
+
* LLVM auto-vectorizes the inner loop on x86 (SSE/AVX) and ARM (NEON).
|
|
101
|
+
*/
|
|
102
|
+
export function cosineSimilarity(a: Float32Array, b: Float32Array): number {
|
|
103
|
+
if (a.length !== b.length) return 0;
|
|
104
|
+
let dot = 0, normA = 0, normB = 0;
|
|
105
|
+
for (let i = 0; i < a.length; i++) {
|
|
106
|
+
dot += a[i] * b[i];
|
|
107
|
+
normA += a[i] * a[i];
|
|
108
|
+
normB += b[i] * b[i];
|
|
109
|
+
}
|
|
110
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
111
|
+
return denom === 0 ? 0 : dot / denom;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Serialize Float32Array to Buffer for SQLite BLOB storage.
|
|
116
|
+
* Layout: raw IEEE 754 little-endian f32 array (matches Rust's [f32] memory layout).
|
|
117
|
+
*
|
|
118
|
+
* Rust port: src/memory/vectors.rs — use bytemuck::cast_slice or std::mem::transmute.
|
|
119
|
+
*/
|
|
120
|
+
export function vectorToBlob(vec: Float32Array): Buffer {
|
|
121
|
+
return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Deserialize Buffer from SQLite BLOB to Float32Array.
|
|
126
|
+
* Allocates a fresh aligned ArrayBuffer — safe regardless of Buffer alignment.
|
|
127
|
+
*
|
|
128
|
+
* Rust port: src/memory/vectors.rs — bytemuck::cast_slice::<u8, f32>.
|
|
129
|
+
*/
|
|
130
|
+
export function blobToVector(blob: Buffer): Float32Array {
|
|
131
|
+
const aligned = new ArrayBuffer(blob.length);
|
|
132
|
+
const view = new Uint8Array(aligned);
|
|
133
|
+
view.set(blob);
|
|
134
|
+
return new Float32Array(aligned);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ─── Content hashing ─────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Normalize content for dedup hashing.
|
|
141
|
+
* Strips leading bullet dash, trims whitespace, lowercases, collapses runs of spaces.
|
|
142
|
+
*
|
|
143
|
+
* Rust port: src/memory/store.rs::normalize_for_hash
|
|
144
|
+
*/
|
|
145
|
+
export function normalizeForHash(content: string): string {
|
|
146
|
+
return content
|
|
147
|
+
.replace(/^-\s*/, "")
|
|
148
|
+
.trim()
|
|
149
|
+
.toLowerCase()
|
|
150
|
+
.replace(/\s+/g, " ");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Compute a 16-hex-char content hash for deduplication.
|
|
155
|
+
* Uses sha256 truncated to 64 bits — collision probability negligible at expected fact counts.
|
|
156
|
+
*
|
|
157
|
+
* Rust port: src/memory/store.rs::content_hash — sha2::Sha256, hex encode, truncate to 16.
|
|
158
|
+
*/
|
|
159
|
+
export function contentHash(content: string): string {
|
|
160
|
+
return crypto.createHash("sha256")
|
|
161
|
+
.update(normalizeForHash(content))
|
|
162
|
+
.digest("hex")
|
|
163
|
+
.slice(0, 16);
|
|
164
|
+
}
|