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,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
|
+
```
|