@yeaft/webchat-agent 0.1.665 → 0.1.667

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 (41) hide show
  1. package/connection/message-router.js +1 -24
  2. package/package.json +1 -1
  3. package/unify/cli.js +5 -84
  4. package/unify/config.js +2 -2
  5. package/unify/dream-v2/apply.js +1 -1
  6. package/unify/dream-v2/limits.js +1 -1
  7. package/unify/dream-v2/merge.js +1 -1
  8. package/unify/dream-v2/runner.js +2 -2
  9. package/unify/dream-v2/schedule.js +2 -2
  10. package/unify/dream-v2/segment.js +1 -1
  11. package/unify/dream-v2/session-wiring.js +2 -3
  12. package/unify/dream-v2/snapshot.js +1 -1
  13. package/unify/dream-v2/state.js +1 -1
  14. package/unify/dream-v2/triage.js +1 -1
  15. package/unify/engine.js +23 -133
  16. package/unify/eval/cases/memory.js +9 -142
  17. package/unify/features/summary.js +15 -98
  18. package/unify/index.js +0 -2
  19. package/unify/memory/ams.js +1 -1
  20. package/unify/memory/consolidate.js +10 -125
  21. package/unify/memory/segment-store.js +1 -1
  22. package/unify/memory/store-v2.js +6 -9
  23. package/unify/prompts.js +8 -50
  24. package/unify/session.js +2 -22
  25. package/unify/stop-hooks.js +8 -44
  26. package/unify/tools/index.js +0 -11
  27. package/unify/web-bridge.js +0 -98
  28. package/unify/memory/dream-shard.js +0 -722
  29. package/unify/memory/extract.js +0 -101
  30. package/unify/memory/layout.js +0 -358
  31. package/unify/memory/schema.js +0 -166
  32. package/unify/memory/shard-store.js +0 -373
  33. package/unify/memory/store.js +0 -578
  34. package/unify/memory/types.js +0 -139
  35. package/unify/memory/user-memory-store.js +0 -452
  36. package/unify/tools/memory-query.js +0 -134
  37. package/unify/tools/memory-read.js +0 -90
  38. package/unify/tools/memory-search.js +0 -140
  39. package/unify/tools/memory-trace.js +0 -135
  40. package/unify/tools/memory-write.js +0 -113
  41. package/unify/user-memory.js +0 -107
@@ -1,134 +1,11 @@
1
1
  /**
2
2
  * eval/cases/memory.js — Memory recall eval cases
3
3
  *
4
- * Tests the memory recall pipeline:
5
- * - Keyword extraction accuracy
6
- * - Scope + tag filtering
7
- * - LLM selection (when >7 candidates)
8
- * - Fingerprint caching
9
- * - Memory injection into system prompt
4
+ * Smoke-level evals for the H2-AMS memory pipeline. Detailed mocks live
5
+ * in unit tests; this file only declares the eval scenarios.
10
6
  */
11
7
 
12
- import {
13
- noError,
14
- containsText,
15
- custom,
16
- } from '../runner.js';
17
-
18
- // ─── Memory Recall Test Helpers ──────────────────────────────
19
-
20
- /**
21
- * Create an engine with pre-loaded memory entries for eval.
22
- * Uses a mock MemoryStore that returns predefined entries.
23
- */
24
- function createMockMemoryStore(entries) {
25
- return {
26
- readProfile: () => 'User is a senior TypeScript developer who prefers functional programming.',
27
- readEntry: (name) => entries.find(e => e.name === name) || null,
28
- readSection: () => '',
29
- listEntries: () => entries,
30
- findByFilter: ({ scope, tags, limit = 15 }) => {
31
- // Simple scoring: scope match + tag overlap
32
- return entries
33
- .map(e => {
34
- let score = 0;
35
- if (scope && e.scope === scope) score += 3;
36
- if (scope && e.scope === 'global') score += 1;
37
- if (tags) {
38
- for (const t of tags) {
39
- if (e.tags && e.tags.includes(t)) score += 1;
40
- }
41
- }
42
- return { ...e, _score: score };
43
- })
44
- .filter(e => e._score > 0)
45
- .sort((a, b) => b._score - a._score)
46
- .slice(0, limit);
47
- },
48
- bumpFrequency: () => {},
49
- search: (keyword) => entries.filter(e =>
50
- e.content.toLowerCase().includes(keyword.toLowerCase()) ||
51
- e.name.toLowerCase().includes(keyword.toLowerCase()),
52
- ),
53
- stats: () => ({ entryCount: entries.length, scopes: [], kinds: {} }),
54
- writeEntry: () => 'test-entry',
55
- writeEntries: () => [],
56
- deleteEntry: () => true,
57
- rebuildScopes: () => {},
58
- addToSection: () => {},
59
- writeProfile: () => {},
60
- clear: () => {},
61
- };
62
- }
63
-
64
- const sampleMemoryEntries = [
65
- {
66
- name: 'typescript-strict-mode',
67
- kind: 'preference',
68
- scope: 'global',
69
- tags: ['typescript', 'config', 'strict'],
70
- importance: 'high',
71
- frequency: 5,
72
- content: 'User always uses TypeScript strict mode with noImplicitAny enabled.',
73
- created_at: '2026-03-01T00:00:00Z',
74
- updated_at: '2026-04-01T00:00:00Z',
75
- },
76
- {
77
- name: 'prefers-vitest',
78
- kind: 'preference',
79
- scope: 'work/claude-web-chat',
80
- tags: ['testing', 'vitest', 'framework'],
81
- importance: 'normal',
82
- frequency: 3,
83
- content: 'User prefers vitest over jest for testing. Uses vitest for all new projects.',
84
- created_at: '2026-03-15T00:00:00Z',
85
- updated_at: '2026-04-01T00:00:00Z',
86
- },
87
- {
88
- name: 'error-handling-pattern',
89
- kind: 'lesson',
90
- scope: 'global',
91
- tags: ['error-handling', 'typescript', 'patterns'],
92
- importance: 'high',
93
- frequency: 4,
94
- content: 'Always use Result<T, E> pattern instead of throwing exceptions. Wrap external API calls in try-catch and return Result.',
95
- created_at: '2026-02-01T00:00:00Z',
96
- updated_at: '2026-04-01T00:00:00Z',
97
- },
98
- {
99
- name: 'project-structure',
100
- kind: 'context',
101
- scope: 'work/claude-web-chat',
102
- tags: ['architecture', 'project', 'monorepo'],
103
- importance: 'normal',
104
- frequency: 2,
105
- content: 'Project uses monorepo with agent/, server/, web/ directories. Agent code is in agent/unify/.',
106
- created_at: '2026-01-01T00:00:00Z',
107
- updated_at: '2026-03-01T00:00:00Z',
108
- },
109
- {
110
- name: 'functional-programming',
111
- kind: 'preference',
112
- scope: 'global',
113
- tags: ['functional', 'programming', 'style'],
114
- importance: 'normal',
115
- frequency: 6,
116
- content: 'User prefers functional programming: pure functions, immutable data, map/filter/reduce over loops.',
117
- created_at: '2026-01-15T00:00:00Z',
118
- updated_at: '2026-04-05T00:00:00Z',
119
- },
120
- {
121
- name: 'api-design-rest',
122
- kind: 'skill',
123
- scope: 'global',
124
- tags: ['api', 'rest', 'design'],
125
- importance: 'normal',
126
- frequency: 1,
127
- content: 'REST API conventions: use plural nouns, HTTP methods for CRUD, 2xx success, 4xx client error, 5xx server error.',
128
- created_at: '2026-02-15T00:00:00Z',
129
- updated_at: '2026-02-15T00:00:00Z',
130
- },
131
- ];
8
+ import { noError, custom } from '../runner.js';
132
9
 
133
10
  // ─── Eval Cases ──────────────────────────────────────────────
134
11
 
@@ -139,12 +16,8 @@ export const memoryCases = [
139
16
  {
140
17
  id: 'memory-profile-injection',
141
18
  suite: 'memory',
142
- description: 'System prompt should include user profile from memory',
19
+ description: 'System prompt should include memory section after preflow',
143
20
  prompt: 'Help me with a coding task',
144
- setupEngine: (engine) => {
145
- // We can't directly inject memoryStore here since Engine uses private fields
146
- // Instead, this eval verifies via the adapter call log that system prompt contains memory
147
- },
148
21
  criteria: [
149
22
  noError,
150
23
  custom('has-response', 'Model produces a response', 5, (result) => ({
@@ -154,29 +27,23 @@ export const memoryCases = [
154
27
  ],
155
28
  },
156
29
 
157
- // ─── Keyword Extraction (unit-level eval) ─────────────
30
+ // ─── Recall Event ──────────────────────────────────────
158
31
 
159
32
  {
160
33
  id: 'memory-keyword-extraction',
161
34
  suite: 'memory',
162
- description: 'Keyword extraction produces relevant keywords',
35
+ description: 'Recall event emitted when preflow has hits',
163
36
  prompt: 'How should I handle TypeScript errors in my Express API?',
164
37
  criteria: [
165
38
  noError,
166
- // This is tested at unit level but verifiable here via recall event
167
- custom('recall-event', 'Recall event emitted (if memory store provided)', 3, (result) => {
168
- // Without a real memory store this won't emit recall, so we check gracefully
39
+ custom('recall-event', 'Recall event emitted (if FTS index seeded)', 3, (result) => {
169
40
  const recallEvent = result.events.find(e => e.type === 'recall');
170
41
  return {
171
- pass: true, // Always passes — it's informational
42
+ pass: true,
172
43
  score: recallEvent ? 1 : 0.5,
173
- reason: recallEvent ? `Recalled ${recallEvent.entryCount} entries` : 'No memory store configured',
44
+ reason: recallEvent ? `Recalled ${recallEvent.entryCount} segments` : 'No FTS hits',
174
45
  };
175
46
  }),
176
47
  ],
177
48
  },
178
49
  ];
179
-
180
- // ─── Exported for direct import in unit tests ────────────────
181
-
182
- export { createMockMemoryStore, sampleMemoryEntries };
@@ -1,30 +1,18 @@
1
1
  /**
2
- * summary.js — task-334n: Feature multi-VP collaboration summary protocol.
2
+ * summary.js — Feature multi-VP collaboration summary protocol.
3
3
  *
4
4
  * Owns:
5
5
  * - postSummary() — write a `type=summary` message to the group jsonl
6
- * and run the extractor (B + C)
7
- * - extractFeatureMemory() — turn a summary body into 2-5 feature-memory entries
8
- * via 334f feature-memory shard lib (C)
9
6
  * - buildSummaryReminder() — compute the §Δ31.4 3-AND soft reminder shape
10
- * consumed by 334e's `featureCtx.summaryReminder` (D)
11
- * - buildFeatureCtxMemories() — assemble feature-memory top-5 (pinned + recent +
12
- * tag relevance) for feature_ctx (E)
7
+ * - buildFeatureCtxMemories() feature-memory top-5 (post-rip stub: returns [])
13
8
  *
14
9
  * Hard boundaries:
15
- * - does NOT touch 334o jsonl rotation internals (calls group.appendMessage)
16
- * - does NOT touch 334f shard-store impl (calls openMemoryShardStore API)
17
- * - does NOT touch 334e prompts main frame (returns plain shapes that feed
18
- * the existing renderFeatureCtx contract)
19
- * - does NOT self-loop-write VP-memory (extractor writes feature-memory only;
20
- * VP-level synthesis is deferred to 334g dream)
21
- * - softCap overflow does NOT create new shards (334f already routes into
22
- * dream queue via projectDeriveHint; we just surface `needsRecompression`)
10
+ * - does NOT touch jsonl rotation internals (calls group.appendMessage)
11
+ * - does NOT write feature-memory shards — the H2-AMS rip retired the shard
12
+ * store; feature memory now flows through Dream V2's scope merge instead.
23
13
  */
24
14
 
25
15
  import { join } from 'path';
26
- import { openMemoryShardStore } from '../memory/shard-store.js';
27
- import { AUTHORED_BY } from '../memory/schema.js';
28
16
 
29
17
  // ─── §Δ31.4 soft-reminder thresholds ─────────────────────────────
30
18
  /** Must be initiator AND members>1 AND (age≥20min OR turns≥10). */
@@ -119,36 +107,11 @@ export function postSummary(opts) {
119
107
  },
120
108
  });
121
109
 
122
- // 2) Run the extractor write feature-memory entries (C).
110
+ // 2) Feature-memory extraction is now owned by Dream V2 (per-group diff
111
+ // triage → merge by target scope → atomic segments). The legacy in-line
112
+ // shard-store extractor was retired in the H2-AMS rip; we leave
113
+ // `memoryIds` empty so callers don't depend on inline-extracted IDs.
123
114
  const memoryIds = [];
124
- try {
125
- const store = openMemoryShardStore(memoryDir, 'feature');
126
- const raw = extractor(body) || [];
127
- const bounded = clampExtracted(raw);
128
- for (const [i, item] of bounded.entries()) {
129
- const kind = EXTRACT_KINDS.includes(item.kind) ? item.kind : 'progress';
130
- const shard = KIND_TO_SHARD[kind] || 'progress';
131
- const id = `mem-${stored.id}-${i + 1}`;
132
- store.put({
133
- id,
134
- shard,
135
- kind,
136
- featureId,
137
- body: typeof item.body === 'string' ? item.body.trim() : '',
138
- tags: Array.isArray(item.tags) ? item.tags.slice(0, 5) : [],
139
- authoredBy: AUTHORED_BY.SUMMARY,
140
- sourceRef: { featureId, msgIds: [stored.id] },
141
- createdAt: new Date(now()).toISOString(),
142
- });
143
- memoryIds.push(id);
144
- }
145
- } catch (err) {
146
- // Extractor failures must not fail the summary post; the message is
147
- // already persisted (audit property). We return the empty memoryIds so
148
- // callers can surface a warning if they want.
149
- // eslint-disable-next-line no-console
150
- console.warn('[task-334n] summary-extractor failed:', err?.message || err);
151
- }
152
115
 
153
116
  return {
154
117
  message: stored,
@@ -241,62 +204,16 @@ export function buildSummaryReminder(input) {
241
204
 
242
205
  /**
243
206
  * Assemble feature-memory top-5 for 334e's `featureCtx.memories` field.
244
- * Ordering (§Δ16.5): pinned first recent tag-relevant. Supersedes are
245
- * hidden (entries with supersededBy != null are filtered out).
207
+ * Post-rip stub: the underlying shard store was retired in the H2-AMS rip;
208
+ * Dream V2 owns feature-scope memory now. Returning `[]` keeps the prompt
209
+ * shape valid until a follow-up wires the new scope-summary read path.
246
210
  *
247
- * @param {string} memoryDir groups/<g>/features/<f>/memory/
211
+ * @param {string} memoryDir unused (kept for callsite compatibility)
248
212
  * @param {{ tags?: string[], top?: number }} [opts]
249
213
  * @returns {Array<{body:string, shard:string, authoredBy?:string}>}
250
214
  */
251
- export function buildFeatureCtxMemories(memoryDir, opts = {}) {
252
- const top = Number.isFinite(opts.top) ? Number(opts.top) : 5;
253
- const tagHints = Array.isArray(opts.tags) ? opts.tags : [];
254
- const nowMs = typeof opts.now === 'number' ? opts.now : Date.now();
255
- let results = [];
256
- try {
257
- const store = openMemoryShardStore(memoryDir, 'feature');
258
- const q = store.query({});
259
- const hits = (q.results || [])
260
- .filter((r) => !r.supersededBy)
261
- .map((r) => {
262
- const full = store.get(r.id);
263
- return {
264
- id: r.id,
265
- shard: r.shard || 'general',
266
- body: full?.body || '',
267
- tags: Array.isArray(r.tags) ? r.tags : [],
268
- pinned: !!r.pinned,
269
- createdAt: full?.createdAt || null,
270
- authoredBy: full?.authoredBy || null,
271
- };
272
- })
273
- .filter((r) => r.body && r.body.trim());
274
-
275
- const score = (r) => {
276
- let s = 0;
277
- if (r.pinned) s += 1000;
278
- if (r.createdAt) {
279
- const ageMs = nowMs - new Date(r.createdAt).getTime();
280
- const halfLifeMs = 24 * 60 * 60 * 1000;
281
- s += Math.max(0, 100 * Math.pow(0.5, Math.max(0, ageMs) / halfLifeMs));
282
- }
283
- for (const t of tagHints) if (r.tags.includes(t)) s += 5;
284
- return s;
285
- };
286
- hits.sort((a, b) => {
287
- const ds = score(b) - score(a);
288
- if (ds !== 0) return ds;
289
- return String(b.createdAt || '').localeCompare(String(a.createdAt || ''));
290
- });
291
- results = hits.slice(0, top).map((r) => ({
292
- body: r.body,
293
- shard: r.shard,
294
- ...(r.authoredBy ? { authoredBy: r.authoredBy } : {}),
295
- }));
296
- } catch {
297
- results = [];
298
- }
299
- return results;
215
+ export function buildFeatureCtxMemories(_memoryDir, _opts = {}) {
216
+ return [];
300
217
  }
301
218
 
302
219
  // ─── (F) related-feature ACL fail-closed gate ─────────────────────
package/unify/index.js CHANGED
@@ -22,10 +22,8 @@ export { buildSystemPrompt, SUPPORTED_LANGUAGES } from './prompts.js';
22
22
  export { Engine } from './engine.js';
23
23
  export { ConversationStore, parseMessage, estimateTokens } from './conversation/persist.js';
24
24
  export { searchMessages } from './conversation/search.js';
25
- export { MemoryStore, parseEntry, serializeEntry, MEMORY_KINDS } from './memory/store.js';
26
25
 
27
26
  // Phase 5: Advanced features
28
- export { KINDS, KIND_PRIORITY, KIND_DESCRIPTIONS, IMPORTANCE_LEVELS, validateEntry, parseScopePath, getAncestorScopes, areScopesRelated } from './memory/types.js';
29
27
  export { runStopHooks } from './stop-hooks.js';
30
28
  export { MCPManager, createMCPManager } from './mcp.js';
31
29
  export { SkillManager, createSkillManager, parseSkill, serializeSkill } from './skills.js';
@@ -11,7 +11,7 @@
11
11
  * truth is `<scope>/memory.md` + `<scope>/summary.md`. AMS itself
12
12
  * doesn't write to disk — that's Dream's job.
13
13
  *
14
- * Privacy (DESIGN-v2 §2.2): `vp/<other>` scopes are ALWAYS filtered out
14
+ * Privacy: `vp/<other>` scopes are ALWAYS filtered out
15
15
  * for any worker that isn't `<other>`. The owning code passes its own
16
16
  * vpId at construction.
17
17
  */
@@ -1,22 +1,19 @@
1
1
  /**
2
- * consolidate.js — Consolidate = compact + extract (one LLM call)
2
+ * consolidate.js — Hot-window budget partitioning utilities.
3
3
  *
4
- * Triggered when hot_tokens > MESSAGE_TOKEN_BUDGET.
5
- * One LLM call does two things simultaneously:
6
- * 1. Generate compact summary append to compact.md ("short-term memory")
7
- * 2. Extract memory entries → write to entries/ ("long-term memory")
4
+ * Reduced surface (PR-B rip): the legacy LLM-driven consolidate() pipeline
5
+ * (compact summary + entries-store extraction) has been retired. The only
6
+ * survivors are the pure functions used by the compact orchestrator:
8
7
  *
9
- * After consolidation:
10
- * - Processed messages moved from messages/ to cold/
11
- * - index.md + scopes.md updated
8
+ * - shouldConsolidate(store, budget) — decide when to compact
9
+ * - partitionMessages(messages, budget) split hot messages into
10
+ * toArchive / toKeep based on token budget
12
11
  *
13
- * Reference: yeaft-unify-core-systems.md §3.1, §4.2
14
- * yeaft-unify-design.md §6.1
12
+ * Memory extraction is now owned by Dream V2 (per-group diff -> triage ->
13
+ * merge by target scope -> apply via segment-store + summary-store).
14
+ * Conversation summarisation lives in compact/orchestrator.js's hooks.
15
15
  */
16
16
 
17
- import { extractMemories } from './extract.js';
18
- import { pickEffort } from '../effort.js';
19
-
20
17
  // ─── Constants ──────────────────────────────────────────────────
21
18
 
22
19
  /** Default MESSAGE_TOKEN_BUDGET (context * 4%, default ~8192). */
@@ -28,8 +25,6 @@ export const COMPACT_KEEP_RATIO = 0.4;
28
25
  /** Minimum messages to keep hot (newest). */
29
26
  const MIN_KEEP_MESSAGES = 3;
30
27
 
31
- // ─── Consolidate ────────────────────────────────────────────────
32
-
33
28
  /**
34
29
  * Check if consolidation should be triggered.
35
30
  *
@@ -81,113 +76,3 @@ export function partitionMessages(messages, budget = DEFAULT_MESSAGE_TOKEN_BUDGE
81
76
  toKeep: messages.slice(keepStart),
82
77
  };
83
78
  }
84
-
85
- /**
86
- * Generate a compact summary of messages.
87
- *
88
- * @param {object[]} messages — messages to summarize
89
- * @param {object} adapter — LLM adapter with .call()
90
- * @param {object} config — { model }
91
- * @returns {Promise<string>} — compact summary text
92
- */
93
- async function generateSummary(messages, adapter, config) {
94
- const conversation = messages.map(m => {
95
- const prefix = m.role === 'user' ? 'User' : m.role === 'assistant' ? 'Assistant' : m.role;
96
- return `[${prefix}]: ${(m.content || '').slice(0, 500)}`;
97
- }).join('\n\n');
98
-
99
- const system = 'You are a conversation summarizer. Summarize the conversation concisely in 2-3 paragraphs, preserving key decisions, facts, and context. Write in the same language as the conversation.';
100
-
101
- try {
102
- const result = await adapter.call({
103
- model: config.model,
104
- system,
105
- messages: [{ role: 'user', content: `Summarize this conversation:\n\n${conversation}` }],
106
- maxTokens: 1024,
107
- // task-327c: consolidate is a high-complexity side-query; flag as
108
- // 'max' effort so supported models use extended thinking / reasoning.
109
- // Router/adapter silently drops the param for models that don't
110
- // support thinking, or when UNIFY_THINKING_V1 is off.
111
- effort: pickEffort({ scenario: 'consolidate' }),
112
- });
113
- return result.text.trim();
114
- } catch {
115
- // Fallback: simple concatenation of first/last messages
116
- const first = messages[0]?.content?.slice(0, 200) || '';
117
- const last = messages[messages.length - 1]?.content?.slice(0, 200) || '';
118
- return `[Auto-summary failed] Started with: ${first}... Ended with: ${last}`;
119
- }
120
- }
121
-
122
- /**
123
- * Run the full Consolidate pipeline.
124
- *
125
- * 1. Partition messages (what to archive vs keep)
126
- * 2. Generate compact summary (LLM call)
127
- * 3. Extract memory entries (LLM call)
128
- * 4. Move archived messages to cold/
129
- * 5. Update compact.md, index.md, scopes.md
130
- *
131
- * @param {{
132
- * conversationStore: import('../conversation/persist.js').ConversationStore,
133
- * memoryStore: import('./store.js').MemoryStore,
134
- * adapter: object,
135
- * config: object,
136
- * budget?: number
137
- * }} params
138
- * @returns {Promise<{ compactSummary: string, extractedEntries: string[], archivedCount: number }>}
139
- */
140
- export async function consolidate({ conversationStore, memoryStore, adapter, config, budget = DEFAULT_MESSAGE_TOKEN_BUDGET }) {
141
- // Load all hot messages
142
- const messages = conversationStore.loadAll();
143
-
144
- if (messages.length <= MIN_KEEP_MESSAGES) {
145
- return { compactSummary: '', extractedEntries: [], archivedCount: 0 };
146
- }
147
-
148
- // Step 1: Partition
149
- const { toArchive, toKeep } = partitionMessages(messages, budget);
150
-
151
- if (toArchive.length === 0) {
152
- return { compactSummary: '', extractedEntries: [], archivedCount: 0 };
153
- }
154
-
155
- // Step 2: Generate compact summary
156
- const compactSummary = await generateSummary(toArchive, adapter, config);
157
-
158
- // Step 3: Extract memory entries
159
- const extracted = await extractMemories({ messages: toArchive, adapter, config });
160
-
161
- // Step 4: Move archived messages to cold
162
- const archiveIds = toArchive.map(m => m.id).filter(Boolean);
163
- conversationStore.moveToColdBatch(archiveIds);
164
-
165
- // Step 5a: Update compact.md
166
- if (compactSummary) {
167
- conversationStore.updateCompactSummary(compactSummary);
168
- }
169
-
170
- // Step 5b: Write extracted memory entries
171
- const entryNames = [];
172
- for (const entry of extracted) {
173
- const slug = memoryStore.writeEntry(entry);
174
- entryNames.push(slug);
175
- }
176
-
177
- // Step 5c: Update index.md
178
- const lastMsg = toKeep[toKeep.length - 1];
179
- conversationStore.updateIndex({
180
- lastMessageId: lastMsg?.id || null,
181
- });
182
-
183
- // Step 5d: Rebuild scopes.md
184
- if (entryNames.length > 0) {
185
- memoryStore.rebuildScopes();
186
- }
187
-
188
- return {
189
- compactSummary,
190
- extractedEntries: entryNames,
191
- archivedCount: archiveIds.length,
192
- };
193
- }
@@ -5,7 +5,7 @@
5
5
  * segment blocks) and the SQLite segment index. This layer handles
6
6
  * scope <-> file path mapping; the index layer is scope-agnostic.
7
7
  *
8
- * Path conventions (DESIGN-v2 §5):
8
+ * Path conventions:
9
9
  * ~/.yeaft/memory/user/memory.md
10
10
  * ~/.yeaft/memory/vp/<id>/memory.md
11
11
  * ~/.yeaft/memory/group/<id>/memory.md
@@ -1,5 +1,5 @@
1
1
  /**
2
- * memory/store-v2.js — DESIGN-v2.md Part I: per-scope memory.md + summary.md.
2
+ * memory/store-v2.js — per-scope memory.md + summary.md (Layer-A storage).
3
3
  *
4
4
  * One pair of files per scope. No shards, no entries/, no index.md, no
5
5
  * index.json. The five scope kinds — user, vp, group, feature, topic — share
@@ -29,18 +29,15 @@
29
29
  * ACL:
30
30
  * - This module enforces ONE ACL: `vp/<other>` paths are blocked when
31
31
  * `currentVpId` is given and differs from `<other>`. Every other scope
32
- * boundary is ACL-free in v2 (DESIGN-v2 §3.2).
32
+ * boundary is ACL-free.
33
33
  *
34
34
  * What this module deliberately does NOT do:
35
35
  * - No frontmatter parsing. memory.md and summary.md are pure markdown;
36
36
  * the dream-state metadata block lives at the file's tail and is read
37
37
  * by `dream-v2/state.js`, not here.
38
38
  * - No LLM calls, no extraction, no summarisation. Pure I/O.
39
- * - No legacy R6 fallback. The old MemoryStore (memory/store.js) and
40
- * ScopeTree (memory/scope-tree.js) remain in service until PR-E swaps
41
- * callers; this module is additive.
42
39
  *
43
- * Reference: agent/unify/memory/DESIGN-v2.md §2, §5, §9.
40
+ * Reference: agent/unify/memory/DESIGN-H2-AMS.md.
44
41
  */
45
42
 
46
43
  import {
@@ -228,8 +225,8 @@ export async function writeMemory(scope, content, opts = {}) {
228
225
  }
229
226
 
230
227
  /**
231
- * Append to a scope's memory.md. Used by the rare "direct write" path
232
- * (DESIGN-v2 §7.1); main flow is dream-driven rewrites.
228
+ * Append to a scope's memory.md. Used by the rare "direct write" path;
229
+ * main flow is dream-driven rewrites.
233
230
  *
234
231
  * Append is non-atomic with concurrent readers in the strict sense, but a
235
232
  * single appendFile of a small buffer is atomic at the kernel level on POSIX
@@ -316,7 +313,7 @@ export async function ensureScope(scope, opts = {}) {
316
313
 
317
314
  /**
318
315
  * Enumerate all scopes present on disk. Returns Scope shapes that round-trip
319
- * back through `scopeDir`. Used by Triage (DESIGN-v2 §14) to list candidate
316
+ * back through `scopeDir`. Used by Triage to list candidate
320
317
  * scopes for a group's diff.
321
318
  *
322
319
  * Walks shallowly:
package/unify/prompts.js CHANGED
@@ -8,39 +8,13 @@
8
8
  * and used to enrich the system prompt beyond the hardcoded fallbacks.
9
9
  *
10
10
  * Phase 2 additions:
11
- * - Memory section (user profile + recalled entries)
11
+ * - Memory section (recalled segments via H2-AMS pre-flow)
12
12
  * - Compact summary section (conversation history summary)
13
13
  *
14
- * task-287 refactor (tool-on-demand memory):
15
- * - New `memoryInjection` param carries prebuilt "Memory Index + user
16
- * preferences + project header" text (~1.5k tokens). Engine builds this
17
- * via memory/layout.buildMemoryInjection() and passes it every turn.
18
- * - Legacy `memory={profile,entries}` param still supported for callers
19
- * (tests, CLI) that have not migrated.
20
- *
21
- * task-334e additions (R6 §Δ24.5 / §Δ27.3 / §Δ31.4 / §Δ29.3):
22
- * - `taskCtx` param → renders a `## task_ctx` block with:
23
- * * task-memory top-5 bodies with semantic shard prefix `[shard]`
24
- * (no sourceRef — memory_trace opens the trail on demand)
25
- * * `### related tasks` sub-section — `relatedTaskIds` top-3 (sorted
26
- * by updatedAt desc) + per-task top-2 memory; ACL-gated by
27
- * `target.members` ∋ currentVpId (§Δ31.4)
28
- * * `### summary reminder` soft nudge — condition (§Δ27.3):
29
- * non-summary msgs ≥ 3 AND since-lastSummary > 15min AND
30
- * currentVpId == task.initiatorVpId → emits a DYNAMIC hint
31
- * - `userProfile` param → renders a `## user_profile` block. Stub
32
- * implementation: when not passed explicitly, we fall back to reading
33
- * `~/.yeaft/user/profile.json` ({ "content": "…string…" }) if present
34
- * (§Δ29.3 placeholder until 334l wires real user-memory recall).
35
- * - `coreMemory` param → renders a `## core_memory` block with recall
36
- * top-7 memory bodies (no sourceRef) and a trailing meta line pointing
37
- * at `memory_trace` as the way to open the original message.
38
- *
39
- * Hard constraints (task-334e contract):
40
- * - Does NOT modify engine.js turn loop.
41
- * - Does NOT implement memory_trace / open_source_message (task-334f).
42
- * - Does NOT implement task_summary_post (task-334n).
43
- * - Changes limited to prompts.js + templates/.
14
+ * Memory injection (H2-AMS):
15
+ * - `memoryInjection` carries prebuilt FTS-recall text from
16
+ * `memory/preflow.js` over the relevant scopes (user/group/vp/feature/
17
+ * global). Engine passes it every turn after preflow runs.
44
18
  *
45
19
  * Reference: yeaft-unify-system-prompt-budget.md — Static + Dynamic + Context layers
46
20
  */
@@ -368,27 +342,11 @@ export function buildSystemPrompt({
368
342
  }
369
343
 
370
344
  // ─── 6. Memory Section ─────────────────────────────────
345
+ // FTS5 pre-flow recall + AMS snapshot are concatenated upstream by the
346
+ // engine into a single `memoryInjection` block. The legacy entries-based
347
+ // memory.profile / memory.entries shape was retired in the H2-AMS rip.
371
348
  if (memoryInjection && memoryInjection.trim()) {
372
- // New path (task-287): prebuilt injection from memory/layout.buildMemoryInjection()
373
- // Contains index.md + user-preferences.md + optional project header excerpt.
374
349
  parts.push(memoryInjection.trim());
375
- } else if (memory && (memory.profile || (memory.entries && memory.entries.length > 0))) {
376
- // Legacy path — kept for callers (tests, CLI) that have not migrated yet.
377
- const memoryParts = [lang.memoryHeader];
378
-
379
- if (memory.profile) {
380
- memoryParts.push(`${lang.profileHeader}\n${memory.profile}`);
381
- }
382
-
383
- if (memory.entries && memory.entries.length > 0) {
384
- const entryLines = memory.entries.map(e => {
385
- const tags = (e.tags && e.tags.length > 0) ? ` [${e.tags.join(', ')}]` : '';
386
- return `- **${e.name}** (${e.kind}): ${e.content}${tags}`;
387
- });
388
- memoryParts.push(`${lang.recalledHeader}\n${entryLines.join('\n')}`);
389
- }
390
-
391
- parts.push(memoryParts.join('\n\n'));
392
350
  }
393
351
 
394
352
  // ─── 7. Compact Summary Section ────────────────────────