peon-mem 1.0.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 (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +301 -0
  3. package/bin/peon-mem.mjs +273 -0
  4. package/dist/brain.d.ts +72 -0
  5. package/dist/brain.js +224 -0
  6. package/dist/compression.d.ts +9 -0
  7. package/dist/compression.js +37 -0
  8. package/dist/config.d.ts +22 -0
  9. package/dist/config.js +99 -0
  10. package/dist/daemon-cli.d.ts +2 -0
  11. package/dist/daemon-cli.js +54 -0
  12. package/dist/daemon.d.ts +23 -0
  13. package/dist/daemon.js +1078 -0
  14. package/dist/embedding-store.d.ts +43 -0
  15. package/dist/embedding-store.js +169 -0
  16. package/dist/embeddings.d.ts +93 -0
  17. package/dist/embeddings.js +345 -0
  18. package/dist/entities.d.ts +61 -0
  19. package/dist/entities.js +191 -0
  20. package/dist/entity-extraction.d.ts +33 -0
  21. package/dist/entity-extraction.js +75 -0
  22. package/dist/eval-metrics.d.ts +27 -0
  23. package/dist/eval-metrics.js +50 -0
  24. package/dist/evaluation.d.ts +58 -0
  25. package/dist/evaluation.js +244 -0
  26. package/dist/global-extraction.d.ts +15 -0
  27. package/dist/global-extraction.js +61 -0
  28. package/dist/global-memory.d.ts +43 -0
  29. package/dist/global-memory.js +306 -0
  30. package/dist/global-promotion.d.ts +25 -0
  31. package/dist/global-promotion.js +29 -0
  32. package/dist/hyde.d.ts +31 -0
  33. package/dist/hyde.js +46 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +246 -0
  36. package/dist/injection.d.ts +38 -0
  37. package/dist/injection.js +133 -0
  38. package/dist/logger.d.ts +17 -0
  39. package/dist/logger.js +63 -0
  40. package/dist/memory-mutations.d.ts +24 -0
  41. package/dist/memory-mutations.js +57 -0
  42. package/dist/memory-store.d.ts +194 -0
  43. package/dist/memory-store.js +1205 -0
  44. package/dist/monitor.d.ts +13 -0
  45. package/dist/monitor.js +977 -0
  46. package/dist/overview.d.ts +73 -0
  47. package/dist/overview.js +104 -0
  48. package/dist/processor.d.ts +90 -0
  49. package/dist/processor.js +450 -0
  50. package/dist/quality.d.ts +86 -0
  51. package/dist/quality.js +338 -0
  52. package/dist/recuration.d.ts +13 -0
  53. package/dist/recuration.js +65 -0
  54. package/dist/reranker.d.ts +34 -0
  55. package/dist/reranker.js +89 -0
  56. package/dist/retrieval.d.ts +106 -0
  57. package/dist/retrieval.js +392 -0
  58. package/dist/session-index.d.ts +34 -0
  59. package/dist/session-index.js +87 -0
  60. package/dist/temporal.d.ts +20 -0
  61. package/dist/temporal.js +62 -0
  62. package/dist/token-ab-monitor.d.ts +1 -0
  63. package/dist/token-ab-monitor.js +7 -0
  64. package/dist/tools.d.ts +232 -0
  65. package/dist/tools.js +546 -0
  66. package/dist/types.d.ts +169 -0
  67. package/dist/types.js +1 -0
  68. package/docs/assets/neural-universe.png +0 -0
  69. package/package.json +57 -0
  70. package/scripts/claude-peon-hook.mjs +522 -0
  71. package/scripts/codex-peon-hook.mjs +4 -0
  72. package/scripts/eval-retrieval-labeled.mjs +135 -0
  73. package/scripts/eval-retrieval.mjs +96 -0
  74. package/scripts/evaluate-peon.mjs +47 -0
  75. package/scripts/install-peon-stl.mjs +82 -0
  76. package/scripts/install-peon.mjs +318 -0
  77. package/scripts/lib/eval-ledger.mjs +104 -0
  78. package/scripts/lib/stl-classify.mjs +44 -0
  79. package/scripts/longmemeval-eval.mjs +144 -0
  80. package/scripts/peon-report.mjs +155 -0
  81. package/scripts/peon-stl.mjs +506 -0
  82. package/scripts/token-ab-monitor.html +235 -0
@@ -0,0 +1,57 @@
1
+ function clamp(value) {
2
+ return Math.max(0, Math.min(1, Number.isFinite(value) ? value : 0));
3
+ }
4
+ function touch(record, now) {
5
+ return { ...record, updatedAt: now };
6
+ }
7
+ /** Edit a belief's content/scores/status/pin. Unknown id → unchanged array. */
8
+ export function applyUpdate(records, id, patch, now) {
9
+ return records.map((record) => {
10
+ if (record.id !== id)
11
+ return record;
12
+ const next = touch(record, now);
13
+ if (typeof patch.content === "string" && patch.content.trim())
14
+ next.content = patch.content.trim();
15
+ if (typeof patch.importance === "number")
16
+ next.score = { ...next.score, importance: clamp(patch.importance) };
17
+ if (typeof patch.confidence === "number")
18
+ next.score = { ...next.score, confidence: clamp(patch.confidence) };
19
+ if (patch.status)
20
+ next.status = patch.status;
21
+ if (typeof patch.pinned === "boolean")
22
+ next.pinned = patch.pinned;
23
+ return next;
24
+ });
25
+ }
26
+ /** Remove a belief outright. */
27
+ export function applyDelete(records, id) {
28
+ return records.filter((record) => record.id !== id);
29
+ }
30
+ /** Pin/unpin a belief — pinned beliefs are protected and rank first. */
31
+ export function applyPin(records, id, pinned, now) {
32
+ return applyUpdate(records, id, { pinned }, now);
33
+ }
34
+ /**
35
+ * Fold `dropId` into `keepId`: union the entities, take the higher importance and
36
+ * confidence, OR the pin flag, then remove the dropped record. Either id missing
37
+ * → unchanged array (no partial merge).
38
+ */
39
+ export function applyMerge(records, keepId, dropId, now) {
40
+ if (keepId === dropId)
41
+ return [...records];
42
+ const keep = records.find((r) => r.id === keepId);
43
+ const drop = records.find((r) => r.id === dropId);
44
+ if (!keep || !drop)
45
+ return [...records];
46
+ const merged = {
47
+ ...keep,
48
+ updatedAt: now,
49
+ pinned: Boolean(keep.pinned || drop.pinned),
50
+ score: {
51
+ importance: Math.max(keep.score.importance, drop.score.importance),
52
+ confidence: Math.max(keep.score.confidence, drop.score.confidence)
53
+ },
54
+ entities: Array.from(new Set([...keep.entities, ...drop.entities]))
55
+ };
56
+ return records.filter((r) => r.id !== dropId).map((r) => (r.id === keepId ? merged : r));
57
+ }
@@ -0,0 +1,194 @@
1
+ import { type PeonConfig } from "./config.js";
2
+ import { type EmbeddingClient } from "./embeddings.js";
3
+ import type { MemoryQualityReport } from "./quality.js";
4
+ import { type MemoryPatch } from "./memory-mutations.js";
5
+ import { type BrainAction, type Summarizer } from "./brain.js";
6
+ import { type RankedMemoryRecord } from "./retrieval.js";
7
+ import { type ChangeEntry } from "./temporal.js";
8
+ import type { BrainInspection, MemoryRecord, MemoryType, PeonEvent, PeonRole, PeonSession, ProcessedMemory, ProcessingState, ProjectContext } from "./types.js";
9
+ /** Counts of how applyStructuredMemory mutated the brain — surfaced for observability. */
10
+ export interface ApplyMemoryStats {
11
+ superseded: number;
12
+ obsoleted: number;
13
+ added: number;
14
+ }
15
+ export interface OpenMemoryStoreOptions {
16
+ projectPath: string;
17
+ memoryDirName?: string;
18
+ config?: PeonConfig;
19
+ /** Override the embedding client (null disables embeddings). Mainly for tests. */
20
+ embeddingClient?: EmbeddingClient | null;
21
+ }
22
+ export interface StartSessionInput {
23
+ client: string;
24
+ cwd: string;
25
+ }
26
+ export interface RecordMessageInput {
27
+ sessionId: string;
28
+ role: PeonRole;
29
+ content: string;
30
+ }
31
+ export interface RecordEventInput {
32
+ sessionId: string;
33
+ type: string;
34
+ content: string;
35
+ }
36
+ export interface EndSessionInput {
37
+ sessionId: string;
38
+ }
39
+ export interface GetContextInput {
40
+ query?: string;
41
+ maxChars?: number;
42
+ /** Also retrieve over raw conversational turns (episodic layer) and include them in the context. */
43
+ includeEpisodes?: boolean;
44
+ }
45
+ export declare class PeonMemoryStore {
46
+ private readonly projectPath;
47
+ private readonly memoryDir;
48
+ private readonly embeddingClient;
49
+ private readonly sessions;
50
+ private embeddingStore?;
51
+ private constructor();
52
+ static open(options: OpenMemoryStoreOptions): Promise<PeonMemoryStore>;
53
+ startSession(input: StartSessionInput): Promise<PeonSession>;
54
+ /**
55
+ * Rehydrate a session into memory if it isn't already known. Called by the
56
+ * tools layer after resolving a sessionId from the durable session index, so
57
+ * record/end operations succeed even after a daemon restart. Idempotent.
58
+ */
59
+ ensureSession(session: PeonSession): void;
60
+ recordMessage(input: RecordMessageInput): Promise<PeonEvent>;
61
+ recordEvent(input: RecordEventInput): Promise<PeonEvent>;
62
+ endSession(input: EndSessionInput): Promise<PeonSession>;
63
+ getContext(input?: GetContextInput): Promise<ProjectContext>;
64
+ inspectBrain(input?: GetContextInput): Promise<BrainInspection>;
65
+ listMemoryRecords(): Promise<MemoryRecord[]>;
66
+ /** Serialize a read-modify-write transaction against this project's brain (see projectWriteLocks). */
67
+ private withWriteLock;
68
+ /**
69
+ * Run a multi-step read-modify-write as ONE serialized critical section against this project's
70
+ * brain — even across separate store instances in the process. Callers must NOT invoke other
71
+ * locking mutators inside `fn` (the lock is not reentrant); use the lock-free internals
72
+ * (applyProcessedMemory, mergeSimilarActiveRecords, replaceMemoryRecords) directly.
73
+ */
74
+ runExclusive<T>(fn: () => Promise<T>): Promise<T>;
75
+ replaceMemoryRecords(records: MemoryRecord[]): Promise<void>;
76
+ /** Edit a belief in place (content, scores, status, or pin). Returns the updated record, or null if unknown. */
77
+ updateMemoryRecord(id: string, patch: MemoryPatch): Promise<MemoryRecord | null>;
78
+ /** Delete a belief outright. Returns true if a record was removed. */
79
+ deleteMemoryRecord(id: string): Promise<boolean>;
80
+ /** Pin/unpin a belief. Returns the updated record, or null if unknown. */
81
+ setMemoryRecordPinned(id: string, pinned: boolean): Promise<MemoryRecord | null>;
82
+ /** Fold one belief into another. Returns the surviving record, or null if either id is unknown. */
83
+ mergeMemoryRecords(keepId: string, dropId: string): Promise<MemoryRecord | null>;
84
+ /**
85
+ * Run one autonomous brain pass (the "sleep cycle"): snapshot a backup, then
86
+ * reinforce / resolve conflicts / merge duplicates / compress topic clusters.
87
+ * Every change is recoverable from the snapshot. Returns the actions taken.
88
+ */
89
+ runBrainPass(options?: {
90
+ recalledIds?: string[];
91
+ summarize?: Summarizer;
92
+ minClusterSize?: number;
93
+ }): Promise<BrainAction[]>;
94
+ /** Archive a set of beliefs (recoverable) after snapshotting a backup. Returns how many were archived. */
95
+ archiveRecords(ids: readonly string[], reason: string): Promise<number>;
96
+ /** Recent autonomous actions the brain took — powers the cockpit "what the brain did" feed. */
97
+ readBrainActions(limit?: number): Promise<Array<{
98
+ at: string;
99
+ actions: BrainAction[];
100
+ }>>;
101
+ private snapshotBackup;
102
+ /** Restore the project's beliefs from the most recent backup snapshot. Returns true if restored. */
103
+ restoreLatestBackup(): Promise<boolean>;
104
+ /**
105
+ * Rank memory records for a query using hybrid lexical + semantic retrieval.
106
+ * The single retrieval entry point: embeds the query (if embeddings are on),
107
+ * loads stored vectors, and blends cosine similarity into the lexical score.
108
+ */
109
+ /** Time-travel: the beliefs that were current as of `at`. */
110
+ currentAsOf(at: string | number | Date): Promise<MemoryRecord[]>;
111
+ /** Time-travel: the changelog (added / superseded / retired) over [from, to]. */
112
+ changesBetween(from: string | number | Date, to: string | number | Date): Promise<ChangeEntry[]>;
113
+ /**
114
+ * EPISODIC retrieval — rank the raw conversational turns (not the consolidated beliefs) by
115
+ * relevance to a query. Consolidation is lossy by design: it distills experience into durable
116
+ * beliefs and drops episodic specifics ("the GPS was not functioning" becomes "interested in
117
+ * GPS features"). For questions that hinge on those specifics, retrieving over the raw record
118
+ * recovers the detail the belief layer compressed away. This is the high-recall episodic layer
119
+ * that complements the high-precision belief layer; callers can blend both. Lexical-ranked
120
+ * (raw turns carry no precomputed embeddings) and read-only — it never mutates the store.
121
+ */
122
+ rankEpisodes(query: string | undefined, options?: {
123
+ limit?: number;
124
+ }): Promise<RankedMemoryRecord[]>;
125
+ rankRecords(query: string | undefined, options?: {
126
+ limit?: number;
127
+ expandGraph?: boolean;
128
+ }): Promise<RankedMemoryRecord[]>;
129
+ /**
130
+ * Rank records WITHOUT mutating anything — uses only embeddings already on disk
131
+ * (no sync, no recompute, no writes). For read-only cross-project recall, where
132
+ * we must never modify another project's brain. An optional precomputed query
133
+ * vector lets the caller embed the query once and reuse it across many projects.
134
+ */
135
+ rankRecordsReadonly(query: string | undefined, options?: {
136
+ limit?: number;
137
+ queryVector?: number[];
138
+ }): Promise<RankedMemoryRecord[]>;
139
+ private buildSemanticInput;
140
+ writeQualityReport(report: MemoryQualityReport): Promise<void>;
141
+ readRawMemory(maxChars?: number): Promise<string>;
142
+ /**
143
+ * Read only the raw events that arrived AFTER `afterEventId` (the delta cursor),
144
+ * so consolidation processes new experience instead of re-reading a window.
145
+ * Falls back to the full sliding window when the cursor is unset or no longer
146
+ * present (e.g. logs rotated) — never silently skips events. The delta is capped
147
+ * to `maxChars` (default 60k, env PEON_CONSOLIDATION_MAX_DELTA_CHARS); `lastEventId`
148
+ * is the last INCLUDED event (the next cursor) and `capped` is true when the delta
149
+ * was cut short — the caller must then keep the char-gate open so the rest drains.
150
+ */
151
+ readRawMemoryDelta(afterEventId?: string, maxChars?: number): Promise<{
152
+ text: string;
153
+ lastEventId?: string;
154
+ capped: boolean;
155
+ }>;
156
+ applyProcessedMemory(memory: ProcessedMemory, source?: {
157
+ reason?: string;
158
+ }, modelEntities?: Map<string, string[]>): Promise<ApplyMemoryStats>;
159
+ /**
160
+ * Merge near-duplicate ACTIVE records by embedding similarity. Models sometimes
161
+ * record the same belief twice (e.g. a supersede replacement AND a paraphrase in
162
+ * decisions[]); lexical dedup misses these because the wording differs. With real
163
+ * (API) embeddings this catches the paraphrase and keeps a single current truth.
164
+ * No-op when embeddings are unavailable. supersededBy links to a merged-away id
165
+ * are re-pointed at the surviving record so history stays intact.
166
+ */
167
+ mergeSimilarActiveRecords(records: MemoryRecord[], threshold?: number): Promise<{
168
+ records: MemoryRecord[];
169
+ merged: number;
170
+ }>;
171
+ readProcessingState(): Promise<ProcessingState>;
172
+ writeProcessingState(state: ProcessingState): Promise<void>;
173
+ private ensureLayout;
174
+ private ensureFile;
175
+ private requireSession;
176
+ private record;
177
+ private updateBrain;
178
+ private appendTimeline;
179
+ private writeSessionSummary;
180
+ private readBrainFile;
181
+ private applyStructuredMemory;
182
+ private readMemoryRecords;
183
+ private readMemoryGraph;
184
+ private appendJsonl;
185
+ private appendMarkdown;
186
+ private appendList;
187
+ private readJsonl;
188
+ }
189
+ /**
190
+ * The id a record gets for a given (type, content) — content-derived and stable.
191
+ * Exported so a supersede operation's `targetId` can be computed deterministically
192
+ * (e.g. in tests) without first reading the record back.
193
+ */
194
+ export declare function memoryRecordId(type: MemoryType, content: string): string;