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.
Files changed (160) hide show
  1. package/.gitattributes +3 -0
  2. package/AGENTS.md +16 -0
  3. package/LICENSE +15 -0
  4. package/README.md +289 -0
  5. package/bin/pi.mjs +30 -0
  6. package/extensions/00-secrets/index.ts +1126 -0
  7. package/extensions/01-auth/auth.ts +401 -0
  8. package/extensions/01-auth/index.ts +289 -0
  9. package/extensions/auto-compact.ts +42 -0
  10. package/extensions/bootstrap/deps.ts +291 -0
  11. package/extensions/bootstrap/index.ts +811 -0
  12. package/extensions/chronos/chronos.sh +487 -0
  13. package/extensions/chronos/index.ts +148 -0
  14. package/extensions/cleave/assessment.ts +754 -0
  15. package/extensions/cleave/bridge.ts +31 -0
  16. package/extensions/cleave/conflicts.ts +250 -0
  17. package/extensions/cleave/dispatcher.ts +808 -0
  18. package/extensions/cleave/guardrails.ts +426 -0
  19. package/extensions/cleave/index.ts +3121 -0
  20. package/extensions/cleave/lifecycle-emitter.ts +20 -0
  21. package/extensions/cleave/openspec.ts +811 -0
  22. package/extensions/cleave/planner.ts +260 -0
  23. package/extensions/cleave/review.ts +579 -0
  24. package/extensions/cleave/skills.ts +355 -0
  25. package/extensions/cleave/types.ts +261 -0
  26. package/extensions/cleave/workspace.ts +861 -0
  27. package/extensions/cleave/worktree.ts +243 -0
  28. package/extensions/core-renderers.ts +253 -0
  29. package/extensions/dashboard/context-gauge.ts +58 -0
  30. package/extensions/dashboard/file-watch.ts +14 -0
  31. package/extensions/dashboard/footer.ts +1145 -0
  32. package/extensions/dashboard/git.ts +185 -0
  33. package/extensions/dashboard/index.ts +478 -0
  34. package/extensions/dashboard/memory-audit.ts +34 -0
  35. package/extensions/dashboard/overlay-data.ts +705 -0
  36. package/extensions/dashboard/overlay.ts +365 -0
  37. package/extensions/dashboard/render-utils.ts +54 -0
  38. package/extensions/dashboard/types.ts +191 -0
  39. package/extensions/dashboard/uri-helper.ts +45 -0
  40. package/extensions/debug.ts +69 -0
  41. package/extensions/defaults.ts +282 -0
  42. package/extensions/design-tree/dashboard-state.ts +161 -0
  43. package/extensions/design-tree/design-card.ts +362 -0
  44. package/extensions/design-tree/index.ts +2130 -0
  45. package/extensions/design-tree/lifecycle-emitter.ts +41 -0
  46. package/extensions/design-tree/tree.ts +1607 -0
  47. package/extensions/design-tree/types.ts +163 -0
  48. package/extensions/distill.ts +127 -0
  49. package/extensions/effort/index.ts +395 -0
  50. package/extensions/effort/tiers.ts +146 -0
  51. package/extensions/effort/types.ts +105 -0
  52. package/extensions/lib/git-state.ts +227 -0
  53. package/extensions/lib/local-models.ts +157 -0
  54. package/extensions/lib/model-preferences.ts +51 -0
  55. package/extensions/lib/model-routing.ts +720 -0
  56. package/extensions/lib/operator-fallback.ts +205 -0
  57. package/extensions/lib/operator-profile.ts +360 -0
  58. package/extensions/lib/slash-command-bridge.ts +253 -0
  59. package/extensions/lib/typebox-helpers.ts +16 -0
  60. package/extensions/local-inference/index.ts +727 -0
  61. package/extensions/mcp-bridge/README.md +220 -0
  62. package/extensions/mcp-bridge/index.ts +951 -0
  63. package/extensions/mcp-bridge/lib.ts +365 -0
  64. package/extensions/mcp-bridge/mcp.json +3 -0
  65. package/extensions/mcp-bridge/package.json +11 -0
  66. package/extensions/model-budget.ts +752 -0
  67. package/extensions/offline-driver.ts +403 -0
  68. package/extensions/openspec/archive-gate.ts +164 -0
  69. package/extensions/openspec/branch-cleanup.ts +64 -0
  70. package/extensions/openspec/dashboard-state.ts +50 -0
  71. package/extensions/openspec/index.ts +1917 -0
  72. package/extensions/openspec/lifecycle-emitter.ts +65 -0
  73. package/extensions/openspec/lifecycle-files.ts +70 -0
  74. package/extensions/openspec/lifecycle.ts +50 -0
  75. package/extensions/openspec/reconcile.ts +187 -0
  76. package/extensions/openspec/spec.ts +1385 -0
  77. package/extensions/openspec/types.ts +98 -0
  78. package/extensions/project-memory/DESIGN-global-mind.md +198 -0
  79. package/extensions/project-memory/README.md +202 -0
  80. package/extensions/project-memory/api-types.ts +382 -0
  81. package/extensions/project-memory/compaction-policy.ts +29 -0
  82. package/extensions/project-memory/core.ts +164 -0
  83. package/extensions/project-memory/embeddings.ts +230 -0
  84. package/extensions/project-memory/extraction-v2.ts +861 -0
  85. package/extensions/project-memory/factstore.ts +2177 -0
  86. package/extensions/project-memory/index.ts +3459 -0
  87. package/extensions/project-memory/injection-metrics.ts +91 -0
  88. package/extensions/project-memory/jsonl-io.ts +12 -0
  89. package/extensions/project-memory/lifecycle.ts +331 -0
  90. package/extensions/project-memory/migration.ts +293 -0
  91. package/extensions/project-memory/package.json +9 -0
  92. package/extensions/project-memory/sci-renderers.ts +7 -0
  93. package/extensions/project-memory/template.ts +103 -0
  94. package/extensions/project-memory/triggers.ts +52 -0
  95. package/extensions/project-memory/types.ts +102 -0
  96. package/extensions/render/composition/fonts/Inter-Bold.ttf +0 -0
  97. package/extensions/render/composition/fonts/Inter-Regular.ttf +0 -0
  98. package/extensions/render/composition/fonts/Tomorrow-Bold.ttf +0 -0
  99. package/extensions/render/composition/fonts/Tomorrow-Regular.ttf +0 -0
  100. package/extensions/render/composition/package-lock.json +534 -0
  101. package/extensions/render/composition/package.json +22 -0
  102. package/extensions/render/composition/render.mjs +246 -0
  103. package/extensions/render/composition/test-comp.tsx +87 -0
  104. package/extensions/render/composition/types.ts +24 -0
  105. package/extensions/render/excalidraw/UPSTREAM.md +81 -0
  106. package/extensions/render/excalidraw/elements.ts +764 -0
  107. package/extensions/render/excalidraw/index.ts +66 -0
  108. package/extensions/render/excalidraw/types.ts +223 -0
  109. package/extensions/render/excalidraw-renderer/pyproject.toml +8 -0
  110. package/extensions/render/excalidraw-renderer/render_excalidraw.py +182 -0
  111. package/extensions/render/excalidraw-renderer/render_template.html +59 -0
  112. package/extensions/render/index.ts +830 -0
  113. package/extensions/render/native-diagrams/index.ts +57 -0
  114. package/extensions/render/native-diagrams/motifs.ts +542 -0
  115. package/extensions/render/native-diagrams/raster.ts +8 -0
  116. package/extensions/render/native-diagrams/scene.ts +75 -0
  117. package/extensions/render/native-diagrams/spec.ts +204 -0
  118. package/extensions/render/native-diagrams/svg.ts +116 -0
  119. package/extensions/sci-ui.ts +304 -0
  120. package/extensions/session-log.ts +174 -0
  121. package/extensions/shared-state.ts +146 -0
  122. package/extensions/spinner-verbs.ts +91 -0
  123. package/extensions/style.ts +281 -0
  124. package/extensions/terminal-title.ts +191 -0
  125. package/extensions/tool-profile/index.ts +291 -0
  126. package/extensions/tool-profile/profiles.ts +290 -0
  127. package/extensions/types.d.ts +9 -0
  128. package/extensions/vault/index.ts +185 -0
  129. package/extensions/version-check.ts +90 -0
  130. package/extensions/view/index.ts +859 -0
  131. package/extensions/view/uri-resolver.ts +148 -0
  132. package/extensions/web-search/index.ts +182 -0
  133. package/extensions/web-search/providers.ts +121 -0
  134. package/extensions/web-ui/index.ts +110 -0
  135. package/extensions/web-ui/server.ts +265 -0
  136. package/extensions/web-ui/state.ts +462 -0
  137. package/extensions/web-ui/static/index.html +145 -0
  138. package/extensions/web-ui/types.ts +284 -0
  139. package/package.json +76 -0
  140. package/prompts/init.md +75 -0
  141. package/prompts/new-repo.md +54 -0
  142. package/prompts/oci-login.md +56 -0
  143. package/prompts/status.md +50 -0
  144. package/settings.json +4 -0
  145. package/skills/cleave/SKILL.md +218 -0
  146. package/skills/git/SKILL.md +209 -0
  147. package/skills/git/_reference/ci-validation.md +204 -0
  148. package/skills/oci/SKILL.md +338 -0
  149. package/skills/openspec/SKILL.md +346 -0
  150. package/skills/pi-extensions/SKILL.md +191 -0
  151. package/skills/pi-tui/SKILL.md +517 -0
  152. package/skills/python/SKILL.md +189 -0
  153. package/skills/rust/SKILL.md +268 -0
  154. package/skills/security/SKILL.md +206 -0
  155. package/skills/style/SKILL.md +264 -0
  156. package/skills/typescript/SKILL.md +225 -0
  157. package/skills/vault/SKILL.md +102 -0
  158. package/themes/alpharius-legacy.json +85 -0
  159. package/themes/alpharius.conf +59 -0
  160. package/themes/alpharius.json +88 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * openspec/types — Shared type definitions for the OpenSpec extension.
3
+ *
4
+ * OpenSpec is the specification layer that sits between design exploration
5
+ * and implementation. It defines what must be true (specs) before any code
6
+ * is written, making the development loop: spec → test → code → verify.
7
+ */
8
+
9
+ // ─── Spec Format ─────────────────────────────────────────────────────────────
10
+
11
+ /**
12
+ * A Given/When/Then scenario for a requirement.
13
+ *
14
+ * Format in spec.md files:
15
+ * #### Scenario: <title>
16
+ * Given <precondition>
17
+ * When <action>
18
+ * Then <expected outcome>
19
+ */
20
+ export interface Scenario {
21
+ title: string;
22
+ given: string;
23
+ when: string;
24
+ then: string;
25
+ /** Optional additional then-clauses ("And ...") */
26
+ and?: string[];
27
+ }
28
+
29
+ /**
30
+ * A requirement within a spec domain.
31
+ *
32
+ * Format in spec.md files:
33
+ * ### Requirement: <title>
34
+ * <description>
35
+ * #### Scenario: ...
36
+ */
37
+ export interface Requirement {
38
+ title: string;
39
+ description: string;
40
+ scenarios: Scenario[];
41
+ }
42
+
43
+ /**
44
+ * A section in a spec file grouping requirements by change type.
45
+ */
46
+ export interface SpecSection {
47
+ type: "added" | "modified" | "removed";
48
+ requirements: Requirement[];
49
+ }
50
+
51
+ /**
52
+ * A parsed spec file with domain name and sections.
53
+ */
54
+ export interface SpecFile {
55
+ /** Domain name derived from file path (e.g., "auth/tokens") */
56
+ domain: string;
57
+ /** Absolute path to the spec.md file */
58
+ filePath: string;
59
+ sections: SpecSection[];
60
+ }
61
+
62
+ // ─── Change Lifecycle ────────────────────────────────────────────────────────
63
+
64
+ /**
65
+ * Lifecycle stages of an OpenSpec change:
66
+ *
67
+ * proposed → specified → planned → implementing → verifying → archived
68
+ *
69
+ * - proposed: proposal.md exists, no specs yet
70
+ * - specified: specs/ directory has scenario files
71
+ * - planned: tasks.md exists (ready for /cleave)
72
+ * - implementing: tasks partially done
73
+ * - verifying: all tasks done, awaiting /assess spec
74
+ * - archived: moved to openspec/archive/
75
+ */
76
+ export type ChangeStage =
77
+ | "proposed"
78
+ | "specified"
79
+ | "planned"
80
+ | "implementing"
81
+ | "verifying"
82
+ | "archived";
83
+
84
+ /**
85
+ * Full status of an OpenSpec change including computed lifecycle stage.
86
+ */
87
+ export interface ChangeInfo {
88
+ name: string;
89
+ path: string;
90
+ stage: ChangeStage;
91
+ hasProposal: boolean;
92
+ hasDesign: boolean;
93
+ hasSpecs: boolean;
94
+ hasTasks: boolean;
95
+ totalTasks: number;
96
+ doneTasks: number;
97
+ specs: SpecFile[];
98
+ }
@@ -0,0 +1,198 @@
1
+ # Global Mind & Edges — Design
2
+
3
+ ## Architecture
4
+
5
+ ```
6
+ Project A (.pi/memory/facts.db) Project B (.pi/memory/facts.db)
7
+ └─ default mind (project facts) └─ default mind (project facts)
8
+ │ │
9
+ ▼ new facts trigger ▼ new facts trigger
10
+ ┌──────────────────────────────────────────────┐
11
+ │ Global FactStore (~/.pi/memory/facts.db) │
12
+ │ │
13
+ │ facts table ── generalized knowledge │
14
+ │ edges table ── relationships between facts │
15
+ │ provenance ── which project sourced what │
16
+ └──────────────────────────────────────────────┘
17
+ ```
18
+
19
+ ### Two databases, one extraction chain
20
+
21
+ - **Project DB** (`$CWD/.pi/memory/facts.db`): Scoped facts about this codebase.
22
+ Already exists.
23
+ - **Global DB** (`~/.pi/memory/facts.db`): Cross-cutting knowledge + edges.
24
+ New. Lives at user home, shared across all projects.
25
+
26
+ Both are SQLite with the same FactStore class. The global DB adds an `edges`
27
+ table for relationships between facts.
28
+
29
+ ## Schema Extension — edges table
30
+
31
+ ```sql
32
+ CREATE TABLE IF NOT EXISTS edges (
33
+ id TEXT PRIMARY KEY,
34
+ source_fact_id TEXT NOT NULL,
35
+ target_fact_id TEXT NOT NULL,
36
+ relation_type TEXT NOT NULL, -- 'relates_to', 'contradicts', 'depends_on', 'generalizes', 'instance_of'
37
+ description TEXT NOT NULL, -- LLM-generated edge label
38
+ confidence REAL NOT NULL DEFAULT 1.0,
39
+ last_reinforced TEXT NOT NULL,
40
+ reinforcement_count INTEGER NOT NULL DEFAULT 1,
41
+ decay_rate REAL NOT NULL DEFAULT 0.049,
42
+ status TEXT NOT NULL DEFAULT 'active',
43
+ created_at TEXT NOT NULL,
44
+ created_session TEXT,
45
+ source_mind TEXT, -- which project mind the source fact came from
46
+ target_mind TEXT, -- which project mind the target fact came from
47
+ FOREIGN KEY (source_fact_id) REFERENCES facts(id) ON DELETE CASCADE,
48
+ FOREIGN KEY (target_fact_id) REFERENCES facts(id) ON DELETE CASCADE
49
+ );
50
+
51
+ CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_fact_id) WHERE status = 'active';
52
+ CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_fact_id) WHERE status = 'active';
53
+ CREATE INDEX IF NOT EXISTS idx_edges_type ON edges(relation_type) WHERE status = 'active';
54
+ ```
55
+
56
+ Edges decay with the same math as facts. Reinforcement extends half-life.
57
+ When a fact is archived/superseded, CASCADE deletes its edges.
58
+
59
+ ## Extraction Chain
60
+
61
+ Single extraction event, two phases:
62
+
63
+ ### Phase 1: Project extraction (existing)
64
+ ```
65
+ conversation context + project facts → JSONL actions → processExtraction(project_mind)
66
+ returns { reinforced, added, newFactIds[] }
67
+ ```
68
+
69
+ ### Phase 2: Global extraction (new, conditional)
70
+ Only fires if Phase 1 produced new facts (`newFactIds.length > 0`).
71
+
72
+ ```
73
+ new project facts + global facts → JSONL actions → processExtraction(global_mind)
74
+ + processEdges(global_mind)
75
+ ```
76
+
77
+ The global extraction prompt is different:
78
+ - Receives the newly-created project facts (with project context)
79
+ - Receives existing global facts (with IDs)
80
+ - Asks: "What generalizable knowledge do these new facts represent?
81
+ What connections exist between these facts and existing global knowledge?"
82
+ - Outputs the same action types PLUS:
83
+ ```
84
+ {"type":"connect","source":"<fact_id>","target":"<fact_id>","relation":"relates_to","description":"Both involve..."}
85
+ ```
86
+
87
+ ### Cost control
88
+ - Phase 2 only fires when new facts are created (not on pure reinforcements)
89
+ - Phase 2 prompt is smaller: just new facts + global facts, no conversation replay
90
+ - Global mind decay is slower (longer base half-life) — fewer facts churn through
91
+
92
+ ## processExtraction changes
93
+
94
+ Current return: `{ reinforced: number, added: number }`
95
+ New return: `{ reinforced: number, added: number, newFactIds: string[] }`
96
+
97
+ This is the signal that chains Phase 1 → Phase 2.
98
+
99
+ ## ExtractionAction changes
100
+
101
+ ```typescript
102
+ export interface ExtractionAction {
103
+ type: "observe" | "reinforce" | "supersede" | "archive" | "connect";
104
+ id?: string;
105
+ section?: SectionName;
106
+ content?: string;
107
+ // connect-specific:
108
+ source?: string; // source fact ID
109
+ target?: string; // target fact ID
110
+ relation?: string; // relation_type
111
+ description?: string; // edge label
112
+ }
113
+ ```
114
+
115
+ `connect` actions are only valid in global extraction. processExtraction
116
+ ignores them; processEdges handles them.
117
+
118
+ ## Global extraction prompt
119
+
120
+ ```
121
+ You are a cross-project knowledge synthesizer. You receive:
122
+ 1. New facts just extracted from a coding session (with project context)
123
+ 2. Existing facts in the global knowledge base (with IDs)
124
+
125
+ Your job: identify generalizable knowledge and connections.
126
+
127
+ ACTIONS:
128
+
129
+ {"type":"observe","section":"Architecture","content":"..."}
130
+ → A new fact generalizes beyond its source project. Rewrite it to be project-agnostic.
131
+
132
+ {"type":"reinforce","id":"abc123"}
133
+ → An existing global fact is confirmed by this project's new evidence.
134
+
135
+ {"type":"connect","source":"<id>","target":"<id>","relation":"relates_to","description":"..."}
136
+ → Two facts are meaningfully related. Relation types:
137
+ - relates_to: general thematic connection
138
+ - contradicts: facts are in tension
139
+ - depends_on: source fact requires target fact
140
+ - generalizes: source is a more general form of target
141
+ - instance_of: source is a specific instance of target pattern
142
+
143
+ RULES:
144
+ - Only promote facts that would be useful across MULTIPLE projects.
145
+ - Rewrite promoted facts to remove project-specific details.
146
+ - Connections should represent genuine analytical insight, not surface similarity.
147
+ - Prefer fewer, high-quality connections over many weak ones.
148
+ - Output ONLY valid JSONL.
149
+ ```
150
+
151
+ ## Context injection
152
+
153
+ When rendering memory for LLM context:
154
+ 1. Project facts (current behavior)
155
+ 2. Relevant global facts (FTS5 query using project context as search terms)
156
+ 3. Edges connecting injected facts (shows the agent relationships it should be aware of)
157
+
158
+ This gives the agent both project-specific and cross-cutting context.
159
+
160
+ ## Rendering format
161
+
162
+ ```markdown
163
+ ## Global Knowledge
164
+
165
+ - **Architecture**: Pattern X applies across distributed systems [↔ relates_to: "Local finding Y"]
166
+ - **Decisions**: Embedded DBs preferred over client-server for CLI tooling [← generalizes: project-specific SQLite choice]
167
+ ```
168
+
169
+ Edges rendered inline as annotations on the facts they connect.
170
+
171
+ ## Decay parameters — global mind
172
+
173
+ Longer half-life baseline since global facts should be more durable:
174
+
175
+ ```typescript
176
+ const GLOBAL_DECAY = {
177
+ baseRate: 0.033, // ~21 day half-life (vs 14 for project)
178
+ reinforcementFactor: 2.0, // stronger reinforcement effect
179
+ minimumConfidence: 0.1,
180
+ };
181
+ ```
182
+
183
+ ## File changes
184
+
185
+ | File | Change |
186
+ |------|--------|
187
+ | `factstore.ts` | Add `edges` table to schema, `storeEdge()`, `getEdgesForFact()`, `processEdges()`. Return `newFactIds` from `processExtraction`. |
188
+ | `extraction-v2.ts` | Add `buildGlobalExtractionPrompt()`, `formatNewFactsForGlobalExtraction()`. |
189
+ | `index.ts` | Open global FactStore at `~/.pi/memory/facts.db`. Chain Phase 2 after Phase 1. Include global facts in context injection. |
190
+ | `triggers.ts` | No changes — trigger logic is Phase 1 only. Phase 2 is conditional on Phase 1 output. |
191
+ | `types.ts` | Add `globalMemoryDir` config option. |
192
+
193
+ ## Open questions
194
+
195
+ 1. **Edge rendering budget**: How many global facts + edges to inject alongside project facts? Need to avoid blowing context window.
196
+ 2. **Edge search**: Should `memory_search_archive` also search edges? FTS5 on edge descriptions?
197
+ 3. **Manual edge creation**: Should there be a `memory_connect` tool for explicit user-created edges?
198
+ 4. **Bidirectionality**: Are edges directional or bidirectional? Current design is directional (source → target) but `relates_to` is inherently bidirectional.
@@ -0,0 +1,202 @@
1
+ # Project Memory Extension
2
+
3
+ Persistent, semantic memory for the pi coding agent. Facts, decisions, and patterns survive across sessions and inform future work.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ index.ts — Extension entry: tools, lifecycle events, context injection
9
+ factstore.ts — SQLite-backed fact/episode/vector storage with FTS5
10
+ embeddings.ts — Ollama embedding client, cosine similarity, vector serialization
11
+ extraction-v2.ts — Subagent extraction (JSONL actions), episode generation
12
+ triggers.ts — Extraction trigger heuristics (token count, tool calls)
13
+ template.ts — Section definitions (Architecture, Decisions, etc.)
14
+ types.ts — Shared types and default config
15
+ migration.ts — One-time markdown → SQLite migration
16
+ ```
17
+
18
+ ### Data Model
19
+
20
+ **Facts** are atomic knowledge units stored in SQLite with:
21
+ - Section classification (Architecture, Decisions, Constraints, Known Issues, Patterns & Conventions, Specs)
22
+ - Confidence decay based on age and reinforcement count
23
+ - Content-hash deduplication
24
+ - Supersession chains for fact evolution
25
+ - FTS5 full-text search index
26
+
27
+ **Minds** are named memory stores. Each project gets a `default` mind. Additional minds can be created for branches, experiments, or ingested external knowledge.
28
+
29
+ **Episodes** are session narratives generated at shutdown — capturing what happened, what was decided, and what's still open. Linked to the facts created during that session.
30
+
31
+ **Edges** connect facts with typed relationships (depends_on, contradicts, enables, etc.) forming a knowledge graph.
32
+
33
+ **Vectors** (Float32Array BLOBs) enable semantic retrieval via cosine similarity against Ollama-generated embeddings.
34
+
35
+ ### Schema Versioning
36
+
37
+ The database uses incremental schema versioning via a `schema_version` table:
38
+
39
+ | Version | Description |
40
+ |---------|-------------|
41
+ | 1 | Core tables: minds, facts, facts_fts, edges |
42
+ | 2 | Vector + episode tables: facts_vec, episodes, episode_facts, episodes_vec |
43
+
44
+ Migrations run automatically on database open. `CREATE TABLE IF NOT EXISTS` ensures idempotency.
45
+
46
+ ## Tools
47
+
48
+ | Tool | Description |
49
+ |------|-------------|
50
+ | `memory_query` | Read all project memory (full dump) |
51
+ | `memory_recall(query)` | Semantic search — returns ranked facts by relevance × confidence |
52
+ | `memory_store(section, content)` | Store a new fact with pre-store conflict detection |
53
+ | `memory_supersede(fact_id, section, content)` | Atomically replace a fact |
54
+ | `memory_archive(fact_ids)` | Archive stale facts |
55
+ | `memory_search_archive(query)` | Search archived/superseded facts |
56
+ | `memory_connect(source, target, relation)` | Create a typed edge between facts |
57
+ | `memory_compact` | Trigger context compaction with extraction |
58
+ | `memory_recall(query)` | Semantic search over facts |
59
+ | `memory_episodes(query)` | Search session episode narratives |
60
+ | `memory_focus(fact_ids)` | Pin facts to working memory (survives compaction) |
61
+ | `memory_release` | Clear working memory buffer |
62
+
63
+ ## Semantic Features (v3 — Hippocampus)
64
+
65
+ Requires a local Ollama instance with an embedding model:
66
+
67
+ ```bash
68
+ ollama pull qwen3-embedding:0.6b # 639MB, 1024 dims, ~108ms/embed
69
+ # or
70
+ ollama pull qwen3-embedding:4b # 2.5GB, 2048 dims, higher quality
71
+ ```
72
+
73
+ ### How It Works
74
+
75
+ 1. **Background indexing**: On session start, all unembedded facts are vectorized asynchronously
76
+ 2. **Contextual injection**: `before_agent_start` embeds the user prompt and injects only the most relevant facts (top-20 semantic + core sections + working memory) instead of dumping all facts
77
+ 3. **Semantic recall**: `memory_recall(query)` returns facts ranked by cosine similarity × confidence
78
+ 4. **Conflict detection**: `memory_store` checks for >85% similar facts BEFORE storing and warns about potential duplicates
79
+ 5. **Episode search**: `memory_episodes(query)` finds relevant past session narratives
80
+ 6. **Working memory**: Facts accessed via recall/store enter a session-scoped buffer (cap 25) with priority injection
81
+
82
+ ### Graceful Degradation
83
+
84
+ All semantic features fall back when Ollama is unavailable:
85
+ - `memory_recall` → FTS5 keyword search
86
+ - Context injection → full fact dump
87
+ - Conflict detection → skipped
88
+ - Episode search → most recent episodes (chronological)
89
+ - Status bar shows `🧠⚡` when embeddings available, `🧠` when not
90
+
91
+ ### Dimension Mismatch Handling
92
+
93
+ If the embedding model changes between sessions (e.g., switching from 0.6b to 4b), vectors with the old dimensions are automatically purged and re-indexed. The `purgeStaleVectors()` method removes vectors whose `dims` column doesn't match the current model's output. Semantic search also skips individual vectors with mismatched dimensions as a safety net.
94
+
95
+ ## Context Injection
96
+
97
+ On each agent turn, memory is injected as a system message. The injection strategy depends on available capabilities:
98
+
99
+ | Condition | Strategy |
100
+ |-----------|----------|
101
+ | <3 facts | Welcome message only |
102
+ | No embeddings OR <50% vector coverage OR <20 facts | Full dump via `renderForInjection()` |
103
+ | Embeddings available + sufficient coverage | Semantic subset: core sections (Constraints, Specs) + working memory + top-20 relevant facts |
104
+
105
+ Recent episodes (last 3) and global knowledge are always appended when available.
106
+
107
+ ## Extraction
108
+
109
+ A background subagent periodically scans conversation history and emits JSONL actions:
110
+
111
+ ```jsonl
112
+ {"type":"observe","section":"Architecture","content":"System uses k8s 1.29"}
113
+ {"type":"reinforce","id":"abc123"}
114
+ {"type":"supersede","id":"def456","section":"Architecture","content":"Updated to k8s 1.30"}
115
+ {"type":"archive","id":"ghi789"}
116
+ {"type":"connect","source":"abc123","target":"def456","relation":"replaces"}
117
+ ```
118
+
119
+ Extraction triggers when:
120
+ - Token count exceeds threshold since last extraction
121
+ - Sufficient tool calls have occurred
122
+ - Manual `/memory refresh` command
123
+ - Session compaction event
124
+
125
+ ### Episode Generation
126
+
127
+ At session shutdown (if >5 messages exchanged), a subagent generates a session episode:
128
+
129
+ ```json
130
+ {"title":"Migrated auth from JWT to OIDC","narrative":"Goal was to replace JWT auth with OIDC. Updated middleware, added Keycloak config, fixed 3 test failures. Decision: use PKCE flow for all clients. Open: need to update API docs."}
131
+ ```
132
+
133
+ Episodes are stored with links to facts created during the session and optionally embedded for semantic search.
134
+
135
+ ## Configuration
136
+
137
+ Defined in `types.ts` as `MemoryConfig`:
138
+
139
+ | Key | Default | Description |
140
+ |-----|---------|-------------|
141
+ | `extractionModel` | `gpt-5.3-codex-spark` | Cheap GPT model for extraction subagent |
142
+ | `embeddingProvider` | `openai` | Embedding backend for semantic retrieval |
143
+ | `embeddingModel` | `text-embedding-3-small` | Cheap cloud embedding model |
144
+ | `maxLines` | 50 | Max facts before pruning |
145
+ | `minimumTokensToInit` | 10000 | Min tokens before first extraction |
146
+ | `minimumTokensBetweenUpdate` | 5000 | Min tokens between extractions |
147
+ | `toolCallsBetweenUpdates` | 8 | Min tool calls between extractions |
148
+ | `extractionTimeout` | 60000 | Extraction subprocess timeout (ms) |
149
+ | `shutdownExtractionTimeout` | 15000 | Episode generation timeout (ms) |
150
+
151
+ ## Confidence Decay
152
+
153
+ Facts decay exponentially based on time since last reinforcement:
154
+
155
+ ```
156
+ confidence = e^(-ln(2) × daysSince / halfLife)
157
+ halfLife = baseHalfLife × reinforcementFactor^(count - 1)
158
+ ```
159
+
160
+ | Profile | Base Half-Life | Reinforcement Factor |
161
+ |---------|---------------|---------------------|
162
+ | Project | 14 days | 1.8× per reinforcement |
163
+ | Global | 30 days | 2.5× per reinforcement |
164
+
165
+ **Specs section facts are exempt from decay** (always confidence 1.0).
166
+
167
+ ## Testing
168
+
169
+ ```bash
170
+ cd extensions/project-memory
171
+ npx tsx --test *.test.ts
172
+ ```
173
+
174
+ Test files:
175
+ - `factstore.test.ts` — Core CRUD, dedup, supersession, archival, FTS5, rendering, minds, extraction processing, decay math
176
+ - `embeddings.test.ts` — Cosine similarity, vector serialization roundtrips, dimension constants
177
+ - `vectors-episodes.test.ts` — Vector storage, semantic search, dimension mismatch handling, conflict detection, episode CRUD, episode vectors, renderFactList, JSONL export/import with episodes, schema versioning
178
+ - `edges.test.ts` — Edge CRUD, dedup, rendering, cross-mind edges, extraction processing
179
+ - `extraction.test.ts` — Trigger heuristics
180
+ - `template.test.ts` — Section definitions, markdown template, appendToSection
181
+ - `migration.test.ts` — Markdown → SQLite migration
182
+
183
+ ## Slash Commands
184
+
185
+ | Command | Description |
186
+ |---------|-------------|
187
+ | `/memory` | Show stats: fact count, confidence, vector coverage, episodes, working memory |
188
+ | `/memory refresh` | Force extraction cycle (prune, consolidate, reinforce) |
189
+ | `/memory export` | Export facts.jsonl for cross-machine sync |
190
+
191
+ ## File Layout
192
+
193
+ ```
194
+ .pi/memory/
195
+ ├── facts.db # SQLite database (gitignored)
196
+ ├── facts.db-wal # WAL journal (gitignored)
197
+ ├── facts.jsonl # Portable export (git-tracked)
198
+ └── memory.md.migrated # Pre-migration backup (if migrated)
199
+
200
+ ~/.pi/memory/
201
+ └── global.db # Cross-project global knowledge
202
+ ```