@memory-river/core 0.2.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 (86) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +222 -0
  3. package/README.zh-TW.md +186 -0
  4. package/dist/api.d.ts +100 -0
  5. package/dist/api.js +156 -0
  6. package/dist/cognition/causal-attribution.d.ts +36 -0
  7. package/dist/cognition/causal-attribution.js +239 -0
  8. package/dist/cognition/causal-engine.d.ts +105 -0
  9. package/dist/cognition/causal-engine.js +150 -0
  10. package/dist/cognition/conflict-detector.d.ts +39 -0
  11. package/dist/cognition/conflict-detector.js +193 -0
  12. package/dist/cognition/global-working-memory.d.ts +53 -0
  13. package/dist/cognition/global-working-memory.js +211 -0
  14. package/dist/cognition/hooks-engine.d.ts +99 -0
  15. package/dist/cognition/hooks-engine.js +672 -0
  16. package/dist/cognition/ralph-core.d.ts +28 -0
  17. package/dist/cognition/ralph-core.js +104 -0
  18. package/dist/distill/concentrator-adapter.d.ts +167 -0
  19. package/dist/distill/concentrator-adapter.js +1876 -0
  20. package/dist/engine.d.ts +402 -0
  21. package/dist/engine.js +2254 -0
  22. package/dist/index.d.ts +6 -0
  23. package/dist/index.js +3 -0
  24. package/dist/lifecycle/cleanup-engine.d.ts +80 -0
  25. package/dist/lifecycle/cleanup-engine.js +162 -0
  26. package/dist/lifecycle/cleanup-state.d.ts +34 -0
  27. package/dist/lifecycle/cleanup-state.js +50 -0
  28. package/dist/lifecycle/night-consolidation.d.ts +102 -0
  29. package/dist/lifecycle/night-consolidation.js +640 -0
  30. package/dist/lifecycle/night-recovery.d.ts +40 -0
  31. package/dist/lifecycle/night-recovery.js +107 -0
  32. package/dist/paths.d.ts +17 -0
  33. package/dist/paths.js +16 -0
  34. package/dist/pipeline/capsule-bridge.d.ts +35 -0
  35. package/dist/pipeline/capsule-bridge.js +86 -0
  36. package/dist/pipeline/compact-request.d.ts +30 -0
  37. package/dist/pipeline/compact-request.js +66 -0
  38. package/dist/pipeline/inbox-watcher.d.ts +112 -0
  39. package/dist/pipeline/inbox-watcher.js +1039 -0
  40. package/dist/ports.d.ts +29 -0
  41. package/dist/ports.js +1 -0
  42. package/dist/providers/embedder-v5.d.ts +46 -0
  43. package/dist/providers/embedder-v5.js +155 -0
  44. package/dist/providers/ollama-embedding.d.ts +25 -0
  45. package/dist/providers/ollama-embedding.js +166 -0
  46. package/dist/retrieval/abstractness-judge.d.ts +14 -0
  47. package/dist/retrieval/abstractness-judge.js +87 -0
  48. package/dist/retrieval/coverage-selection.d.ts +3 -0
  49. package/dist/retrieval/coverage-selection.js +53 -0
  50. package/dist/retrieval/cross-encoder-gate.d.ts +40 -0
  51. package/dist/retrieval/cross-encoder-gate.js +239 -0
  52. package/dist/retrieval/retriever-v4.d.ts +78 -0
  53. package/dist/retrieval/retriever-v4.js +1200 -0
  54. package/dist/skills/validate.d.ts +6 -0
  55. package/dist/skills/validate.js +69 -0
  56. package/dist/storage.d.ts +19 -0
  57. package/dist/storage.js +54 -0
  58. package/dist/store/aux-table-maintenance.d.ts +5 -0
  59. package/dist/store/aux-table-maintenance.js +64 -0
  60. package/dist/store/graph-enumerator.d.ts +21 -0
  61. package/dist/store/graph-enumerator.js +185 -0
  62. package/dist/store/graph-store.d.ts +107 -0
  63. package/dist/store/graph-store.js +478 -0
  64. package/dist/store/status-manager.d.ts +44 -0
  65. package/dist/store/status-manager.js +235 -0
  66. package/dist/store/store-v4.d.ts +339 -0
  67. package/dist/store/store-v4.js +2871 -0
  68. package/dist/transcript/keyword-search.d.ts +9 -0
  69. package/dist/transcript/keyword-search.js +67 -0
  70. package/dist/transcript/rehydrate-keyword.d.ts +6 -0
  71. package/dist/transcript/rehydrate-keyword.js +29 -0
  72. package/dist/transcript/rehydrate.d.ts +33 -0
  73. package/dist/transcript/rehydrate.js +285 -0
  74. package/dist/transcript/transcript-archive.d.ts +46 -0
  75. package/dist/transcript/transcript-archive.js +516 -0
  76. package/dist/types.d.ts +409 -0
  77. package/dist/types.js +104 -0
  78. package/dist/util/bounded-map.d.ts +1 -0
  79. package/dist/util/bounded-map.js +8 -0
  80. package/dist/util/rate-limiter.d.ts +12 -0
  81. package/dist/util/rate-limiter.js +54 -0
  82. package/dist/util/session-identity.d.ts +65 -0
  83. package/dist/util/session-identity.js +227 -0
  84. package/dist/util/util-hash.d.ts +1 -0
  85. package/dist/util/util-hash.js +4 -0
  86. package/package.json +59 -0
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Unified Types - memory-river
3
+ * Merged from v4/types.ts + context-river/concentrator.ts
4
+ */
5
+ export type MemoryCategory = "preference" | "fact" | "decision" | "entity" | "constraint" | "identity" | "business" | "knowledge" | "skill" | "other";
6
+ export declare const MEMORY_CATEGORIES: readonly ["preference", "fact", "decision", "entity", "constraint", "identity", "business", "knowledge", "skill", "other"];
7
+ export declare const PROTECTED_CATEGORIES: MemoryCategory[];
8
+ export type HookWeight = "high" | "medium" | "low";
9
+ export interface MemoryHook {
10
+ keyword: string;
11
+ weight: HookWeight;
12
+ weightScore: number;
13
+ }
14
+ export interface HealthConfig {
15
+ initialScore: number;
16
+ decayPerRun: number;
17
+ decayIntervalMs: number;
18
+ deleteThreshold: number;
19
+ coreCategories: string[];
20
+ coreImportanceThreshold: number;
21
+ skillDecayFactor: number;
22
+ }
23
+ export interface MemoryHealth {
24
+ healthScore: number;
25
+ lastAccessedAt: number;
26
+ decayCount: number;
27
+ accessCount: number;
28
+ lastDecayedAt?: number;
29
+ /** 記憶生命週期狀態 */
30
+ status?: 'active' | 'superseded' | 'deprecated' | 'trashed';
31
+ /** 標記為 deprecated 的時間戳(ms) */
32
+ deprecatedAt?: number;
33
+ }
34
+ export interface MemoryEntry {
35
+ id: string;
36
+ text: string;
37
+ textTokens?: string;
38
+ vector: number[];
39
+ importance: number;
40
+ category: MemoryCategory;
41
+ parentId: string | null;
42
+ metadata?: string;
43
+ createdAt: number;
44
+ updatedAt: number;
45
+ /** Slot key,格式:category:param,例如 "memory:drift_threshold" */
46
+ slotKey?: string;
47
+ /** Slot 值(僅 simple value:number | string | boolean) */
48
+ slotValue?: number | string | boolean;
49
+ /** 此記錄覆寫了哪些 id(建立更新鏈) */
50
+ supersedes?: string[];
51
+ /** LLM結構化抽取可靠性指標(0.0-1.0),未結構化時可省略 */
52
+ confidence?: number;
53
+ /** 抽取時的 domain 分類 */
54
+ extractionDomain?: "technical" | "identity" | "preference" | "free_text";
55
+ /** Unix ms timestamp of when this memory was last concentrated */
56
+ lastConcentratedAt?: number;
57
+ /** Unix ms timestamp of when this memory was last included in retrieval results */
58
+ lastRecalledAt?: number;
59
+ /** Number of times this memory has been included in retrieval results */
60
+ recallCount?: number;
61
+ /** Which session this was distilled from */
62
+ sessionId?: string;
63
+ }
64
+ export interface MemorySearchResult {
65
+ entry: MemoryEntry;
66
+ /** @deprecated Use rankScore for ranking or rawDistance for distance thresholds. */
67
+ vectorScore: number;
68
+ /** Rank-based score used by hybrid retrieval. */
69
+ rankScore: number;
70
+ /** Original vector distance. Infinity when no vector result is available. */
71
+ rawDistance: number;
72
+ bm25Score: number;
73
+ fusedScore: number;
74
+ /** CRAG / reranker 評估後的最終分數 */
75
+ finalScore?: number;
76
+ }
77
+ export interface EnumerationPlan {
78
+ anchors: string[];
79
+ setMode: 'union' | 'intersection' | 'compare';
80
+ relationText?: string;
81
+ direction?: 'out' | 'in' | 'both';
82
+ }
83
+ export interface EnumerationAnswer {
84
+ entity: string;
85
+ evidenceTriples: import('./store/graph-store.js').GraphTriple[];
86
+ sourceMemoryIds: string[];
87
+ }
88
+ export interface EnumerationResult {
89
+ answers: EnumerationAnswer[];
90
+ truncated: boolean;
91
+ fallbackUsed: boolean;
92
+ }
93
+ export type SubsystemEffectivenessSubsystem = "causal_chain" | "hooks" | "gwm" | "plugin" | "conflict" | "skill_capsule" | (string & {});
94
+ export interface SubsystemEffectivenessEvent {
95
+ id?: string;
96
+ ts?: string;
97
+ subsystem?: SubsystemEffectivenessSubsystem;
98
+ event?: string;
99
+ entityId?: string;
100
+ relatedId?: string;
101
+ sessionKey?: string;
102
+ sessionId?: string;
103
+ queryHash?: string;
104
+ outcome?: string;
105
+ count?: number;
106
+ score?: number;
107
+ durationMs?: number;
108
+ metadata?: string | Record<string, unknown> | null;
109
+ }
110
+ export interface SubsystemEffectivenessRow {
111
+ id: string;
112
+ ts: string;
113
+ subsystem: string;
114
+ event: string;
115
+ entityId: string;
116
+ relatedId: string;
117
+ sessionKey: string;
118
+ sessionId: string;
119
+ queryHash: string;
120
+ outcome: string;
121
+ count: number;
122
+ score: number;
123
+ durationMs: number;
124
+ metadata: string;
125
+ }
126
+ export interface SubsystemEffectivenessQueryFilter {
127
+ subsystem?: string;
128
+ event?: string;
129
+ outcome?: string;
130
+ since?: string;
131
+ limit?: number;
132
+ }
133
+ /** 記憶生命週期狀態 */
134
+ export type MemoryStatus = 'active' | 'superseded' | 'deprecated' | 'archived' | 'trashed';
135
+ /** 狀態變更原因(audit log 用) */
136
+ export type StatusChangeReason = 'created' | 'slot_supersedes' | 'causal_update' | 'conflict_detected' | 'night_consolidation' | 'cleanup_decay' | 'saveSkill_supersede_rollback' | 'manual';
137
+ /** 狀態變更請求 */
138
+ export interface StatusChangeRequest {
139
+ memoryId: string;
140
+ toStatus: MemoryStatus;
141
+ reason: StatusChangeReason;
142
+ source: string;
143
+ supersededBy?: string;
144
+ meta?: Record<string, unknown>;
145
+ }
146
+ /** 狀態變更結果 */
147
+ export interface StatusChangeResult {
148
+ ok: boolean;
149
+ memoryId: string;
150
+ fromStatus: MemoryStatus | null;
151
+ toStatus: MemoryStatus;
152
+ auditRowId: string;
153
+ error?: string;
154
+ }
155
+ /** 狀態 audit log row(寫入 status_audit_log table) */
156
+ export interface StatusAuditRow {
157
+ id: string;
158
+ timestamp: number;
159
+ memoryId: string;
160
+ fromStatus: string | null;
161
+ toStatus: string;
162
+ reason: string;
163
+ source: string;
164
+ supersededBy: string | null;
165
+ meta: string | null;
166
+ canonicalKey: string | null;
167
+ partial: boolean;
168
+ }
169
+ export interface TranscriptWatermarkRow {
170
+ canonicalKey: string;
171
+ lineCount: number;
172
+ updatedAt: number;
173
+ }
174
+ export type ConcentratorProvider = "gemini" | "deepseek" | "all_failed";
175
+ export type ConcentratorStatOutcome = "success" | "partial" | "failure";
176
+ export type ConcentratorFailureReason = "broken_json" | "timeout" | "quota" | "other";
177
+ export interface ConcentratorStat {
178
+ id: string;
179
+ canonicalKey: string;
180
+ sessionId: string | null;
181
+ provider: ConcentratorProvider;
182
+ outcome: ConcentratorStatOutcome;
183
+ attemptedProviders: string;
184
+ inputTokens: number;
185
+ outputTokens: number | null;
186
+ durationMs: number;
187
+ failureReason: ConcentratorFailureReason | null;
188
+ createdAt: number;
189
+ }
190
+ export interface ContextMessage {
191
+ role: "user" | "assistant" | "system";
192
+ content: string | Array<{
193
+ type?: string;
194
+ text?: string;
195
+ [key: string]: unknown;
196
+ }>;
197
+ timestamp?: number;
198
+ metadata?: Record<string, unknown>;
199
+ }
200
+ export interface RememberOptions {
201
+ source?: "user" | "system" | "tool" | "capsule";
202
+ urgency?: "low" | "normal" | "high";
203
+ category?: string;
204
+ importance?: number;
205
+ tags?: string[];
206
+ }
207
+ export interface RecallOptions {
208
+ limit?: number;
209
+ categories?: string[];
210
+ }
211
+ export interface MemoryResult {
212
+ id: string;
213
+ text: string;
214
+ source: "short" | "long";
215
+ relevance: number;
216
+ category: string;
217
+ timestamp: number;
218
+ health: number;
219
+ }
220
+ export interface RouteDecision {
221
+ targets: ("short" | "long")[];
222
+ category: string;
223
+ importance: number;
224
+ reason: string;
225
+ }
226
+ export interface RoutingContext {
227
+ text: string;
228
+ source: "user" | "system" | "tool" | "capsule";
229
+ urgency: "low" | "normal" | "high";
230
+ manualCategory?: string;
231
+ }
232
+ export type CapsuleType = "working_memory" | "skill_capsule";
233
+ export interface CapsuleSourceEntryRange {
234
+ firstEntryId: number;
235
+ lastEntryId: number;
236
+ count: number;
237
+ }
238
+ export interface CapsuleMetadata {
239
+ sourceEntryIds?: number[];
240
+ sourceEntryRange?: CapsuleSourceEntryRange;
241
+ lastRecalledAt?: number;
242
+ recallCount?: number;
243
+ [key: string]: unknown;
244
+ }
245
+ export interface CapsulePayload {
246
+ summary: string;
247
+ mode: "engineering" | "relationship";
248
+ turnCount: number;
249
+ truncatedAt: number;
250
+ }
251
+ export interface SkillCapsuleEntry extends MemoryEntry {
252
+ capsuleType: CapsuleType;
253
+ skillName: string;
254
+ triggerConditions: string[];
255
+ executionSteps: string[];
256
+ confidence: number;
257
+ sourcePatterns: number;
258
+ }
259
+ export interface InboxWriteOptions {
260
+ category: string;
261
+ importance: number;
262
+ tags?: string[];
263
+ metadata?: CapsuleMetadata;
264
+ }
265
+ /**
266
+ * =============================================================================
267
+ * 📋 Provider / Model 職責對照表(2026-04 現況)
268
+ * =============================================================================
269
+ *
270
+ * 各模組實際使用的 Embedding Provider / Model 不一致,統一記錄於此,
271
+ * 避免日後重構時踩到不相容的維度組合(Gemini 3072d vs Qwen 1024d)。
272
+ *
273
+ * ┌─────────────────────────┬──────────────┬──────────────────────────────────────────────┬─────────────┐
274
+ * │ 模組 │ Provider │ Model │ 向量維度 │
275
+ * ├─────────────────────────┼──────────────┼──────────────────────────────────────────────┼─────────────┤
276
+ * │ embedder-v4.legacy.ts │ Gemini (硬綁)│ gemini-embedding-001 │ 3072d │
277
+ * │ embedder-v5.ts (目前主力)│ Ollama (本地)│ hf.co/Qwen/Qwen3-Embedding-0.6B-GGUF │ 1024d ⚠️ │
278
+ * │ causal-engine.ts │ (引用 embedder-v5) 同上 │ 1024d ⚠️ │
279
+ * │ concentrator-adapter.ts │ Gemini → DeepSeek(濃縮用,非 embedding) │ N/A │
280
+ * └─────────────────────────┴──────────────┴──────────────────────────────────────────────┴─────────────┘
281
+ *
282
+ * ⚠️ 向量維度不相容:v4 (Gemini 3072d) 和 v5 (Qwen 1024d) 使用不同的 LanceDB 实例。
283
+ * types.ts DEFAULT_CONFIG.embedding.provider="minimax" 僅為相容性宣告,
284
+ * 實際 embedder-v5.ts 使用 Ollama,不受此欄位影響。
285
+ *
286
+ * Concentration(濃縮)Provider 順序:
287
+ * - 預設:Gemini → DeepSeek
288
+ * - Gemini 若連續 3 次 503:冷卻 90 秒,期間直接跳過 Gemini
289
+ * - concentrate() 全失敗時仍由 ConcentratorAdapter 走 deterministic fallback capsule
290
+ *
291
+ * =============================================================================
292
+ */
293
+ export interface PluginConfig {
294
+ embedding: {
295
+ provider: "gemini" | "openai" | "ollama" | "minimax";
296
+ apiKey?: string;
297
+ minimaxApiKey?: string;
298
+ model: string;
299
+ dimensions?: number;
300
+ };
301
+ dbPath: string;
302
+ ramDbPath?: string;
303
+ storageMode?: 'auto' | 'ram' | 'ssd';
304
+ inboxPath?: string;
305
+ retrieval?: {
306
+ vectorWeight?: number;
307
+ bm25Weight?: number;
308
+ candidatePoolMultiplier?: number;
309
+ };
310
+ cleanup?: {
311
+ enabled?: boolean;
312
+ decayDays?: number;
313
+ deleteBelow?: number;
314
+ trashRetentionDays?: number;
315
+ };
316
+ cleanupEngine?: {
317
+ enabled?: boolean;
318
+ decayDays?: number;
319
+ deleteBelow?: number;
320
+ trashRetentionDays?: number;
321
+ coreCategories?: string[];
322
+ coreImportanceThreshold?: number;
323
+ skillCapsuleProtection?: boolean;
324
+ useTrash?: boolean;
325
+ dryRun?: boolean;
326
+ maxStartupDelete?: number;
327
+ maxStartupDecay?: number;
328
+ };
329
+ autoRecall?: boolean;
330
+ driftThreshold?: number;
331
+ health?: {
332
+ initialScore?: number;
333
+ decayPerRun?: number;
334
+ decayIntervalMs?: number;
335
+ deleteThreshold?: number;
336
+ coreCategories?: string[];
337
+ coreImportanceThreshold?: number;
338
+ skillDecayFactor?: number;
339
+ };
340
+ hooks?: {
341
+ enabled?: boolean;
342
+ maxHooksPerMemory?: number;
343
+ maxTriggerDepth?: number;
344
+ minTriggerScore?: number;
345
+ cooldownMs?: number;
346
+ };
347
+ causalEngine?: {
348
+ updateThreshold?: number;
349
+ causalThreshold?: number;
350
+ embeddingModel?: string;
351
+ };
352
+ concentration?: {
353
+ model?: string;
354
+ provider?: 'gemini' | 'deepseek';
355
+ geminiApiKey?: string;
356
+ maxTokens?: number;
357
+ deepseekApiKey?: string;
358
+ deepseekModel?: string;
359
+ asyncCompactAfterAssemble?: boolean;
360
+ asyncCompactConcurrency?: number;
361
+ asyncCompactRaceGuard?: boolean;
362
+ };
363
+ }
364
+ export declare const DEFAULT_CONFIG: Omit<Required<PluginConfig>, 'dbPath' | 'ramDbPath' | 'inboxPath'>;
365
+ export declare const CATEGORY_DESCRIPTIONS: Record<MemoryCategory, string>;
366
+ export interface SkillDef {
367
+ name: string;
368
+ summary: string;
369
+ triggers: string[];
370
+ steps: string[];
371
+ }
372
+ export interface SkillIndexEntry {
373
+ name: string;
374
+ triggerConditions: string[];
375
+ summary: string;
376
+ }
377
+ export interface SkillCapsuleV2 extends SkillIndexEntry {
378
+ id: string;
379
+ executionSteps: string[];
380
+ category: "skill";
381
+ importance: 0.7;
382
+ capsuleVersion: 2;
383
+ usageCount: number;
384
+ lastUsedAt: number | null;
385
+ status: "active" | "superseded";
386
+ createdAt: number;
387
+ updatedAt: number;
388
+ }
389
+ export interface SkillCapsule {
390
+ id: string;
391
+ skillName: string;
392
+ triggerConditions: string[];
393
+ executionSteps: string[];
394
+ summary: string;
395
+ confidence: number;
396
+ category: string;
397
+ createdAt: number;
398
+ updatedAt: number;
399
+ usageCount: number;
400
+ lastUsedAt: number | null;
401
+ status: "active" | "deprecated" | "trashed";
402
+ }
403
+ export interface HookStats {
404
+ keyword: string;
405
+ totalTriggers: number;
406
+ successfulTriggers: number;
407
+ failedTriggers: number;
408
+ lastTriggeredAt: number | null;
409
+ }
package/dist/types.js ADDED
@@ -0,0 +1,104 @@
1
+ export const MEMORY_CATEGORIES = [
2
+ "preference",
3
+ "fact",
4
+ "decision",
5
+ "entity",
6
+ "constraint",
7
+ "identity",
8
+ "business",
9
+ "knowledge",
10
+ "skill",
11
+ "other",
12
+ ];
13
+ export const PROTECTED_CATEGORIES = [
14
+ "constraint",
15
+ "identity",
16
+ "business",
17
+ ];
18
+ export const DEFAULT_CONFIG = {
19
+ embedding: {
20
+ provider: "minimax",
21
+ model: "hf.co/Qwen/Qwen3-Embedding-0.6B-GGUF",
22
+ dimensions: 1024,
23
+ apiKey: "",
24
+ minimaxApiKey: "",
25
+ },
26
+ retrieval: {
27
+ vectorWeight: 0.7,
28
+ bm25Weight: 0.3,
29
+ candidatePoolMultiplier: 2,
30
+ },
31
+ cleanup: {
32
+ enabled: true,
33
+ decayDays: 30,
34
+ deleteBelow: 0.1,
35
+ trashRetentionDays: 7,
36
+ },
37
+ cleanupEngine: {
38
+ enabled: true,
39
+ decayDays: 30,
40
+ deleteBelow: 0.1,
41
+ trashRetentionDays: 7,
42
+ coreCategories: ["identity", "preference", "constraint", "business", "decision", "core_rule"],
43
+ coreImportanceThreshold: 0.75,
44
+ skillCapsuleProtection: true,
45
+ useTrash: true,
46
+ dryRun: false,
47
+ maxStartupDelete: 20,
48
+ maxStartupDecay: 50,
49
+ },
50
+ autoRecall: true,
51
+ storageMode: 'auto',
52
+ driftThreshold: 0.78,
53
+ health: {
54
+ initialScore: 100,
55
+ decayPerRun: 5,
56
+ decayIntervalMs: 24 * 60 * 60 * 1000,
57
+ deleteThreshold: 0,
58
+ coreCategories: ["identity", "preference", "constraint", "business", "core_rule"],
59
+ coreImportanceThreshold: 0.8,
60
+ skillDecayFactor: 0.25,
61
+ },
62
+ hooks: {
63
+ enabled: true,
64
+ maxHooksPerMemory: 3,
65
+ maxTriggerDepth: 1,
66
+ minTriggerScore: 0.5,
67
+ cooldownMs: 3600000,
68
+ },
69
+ causalEngine: {
70
+ updateThreshold: 0.28,
71
+ causalThreshold: 0.32,
72
+ embeddingModel: "hf.co/Qwen/Qwen3-Embedding-0.6B-GGUF",
73
+ },
74
+ concentration: {
75
+ model: 'gemini-2.5-flash-lite',
76
+ /**
77
+ * @deprecated 此欄位已失效。實際 fallback 順序由 concentrator-adapter.ts 內定
78
+ * (Gemini → DeepSeek),不受 config 影響。
79
+ * 保留欄位以避免破壞既有宿主設定,請勿依賴其值。
80
+ */
81
+ provider: 'gemini',
82
+ geminiApiKey: "",
83
+ maxTokens: 8192,
84
+ deepseekApiKey: "",
85
+ deepseekModel: 'deepseek-v4-flash',
86
+ asyncCompactAfterAssemble: false,
87
+ asyncCompactConcurrency: 1,
88
+ asyncCompactRaceGuard: true,
89
+ },
90
+ };
91
+ export const CATEGORY_DESCRIPTIONS = {
92
+ preference: "老闆的偏好、習慣、溝通風格",
93
+ fact: "客觀事實、技術規格、數據",
94
+ decision: "做過的決定、選擇",
95
+ entity: "人物、組織、產品等實體",
96
+ constraint: "鐵律、限制、紅線",
97
+ identity: "身份認同、個人特徵",
98
+ business: "商務營運、原創IP",
99
+ knowledge: "其他知識",
100
+ skill: "顯式保存的技能流程",
101
+ other: "其他",
102
+ };
103
+ // ─── Global Working Memory (GWM) ───────────────────────────────────────────
104
+ // GwmState is exported from global-working-memory.ts
@@ -0,0 +1 @@
1
+ export declare function setBoundedMapEntry<K, V>(map: Map<K, V>, key: K, value: V, maxSize: number): void;
@@ -0,0 +1,8 @@
1
+ export function setBoundedMapEntry(map, key, value, maxSize) {
2
+ if (map.size >= maxSize && !map.has(key)) {
3
+ const oldest = map.keys().next().value;
4
+ if (oldest !== undefined)
5
+ map.delete(oldest);
6
+ }
7
+ map.set(key, value);
8
+ }
@@ -0,0 +1,12 @@
1
+ export declare class LLMRateLimiter {
2
+ private readonly capacity;
3
+ private readonly refillWindowMs;
4
+ private tokens;
5
+ private lastRefillAt;
6
+ private queue;
7
+ constructor(capacity: number, refillWindowMs: number);
8
+ private refill;
9
+ private msUntilNextToken;
10
+ acquire(provider: string): Promise<void>;
11
+ }
12
+ export declare const sharedLLMRateLimiter: LLMRateLimiter;
@@ -0,0 +1,54 @@
1
+ function sleep(ms) {
2
+ return new Promise((resolve) => setTimeout(resolve, ms));
3
+ }
4
+ export class LLMRateLimiter {
5
+ capacity;
6
+ refillWindowMs;
7
+ tokens;
8
+ lastRefillAt;
9
+ queue = Promise.resolve();
10
+ constructor(capacity, refillWindowMs) {
11
+ this.capacity = capacity;
12
+ this.refillWindowMs = refillWindowMs;
13
+ this.tokens = capacity;
14
+ this.lastRefillAt = Date.now();
15
+ }
16
+ refill() {
17
+ const now = Date.now();
18
+ const elapsed = now - this.lastRefillAt;
19
+ if (elapsed <= 0)
20
+ return;
21
+ const refillRate = this.capacity / this.refillWindowMs;
22
+ this.tokens = Math.min(this.capacity, this.tokens + elapsed * refillRate);
23
+ this.lastRefillAt = now;
24
+ }
25
+ msUntilNextToken() {
26
+ if (this.tokens >= 1)
27
+ return 0;
28
+ const refillRate = this.capacity / this.refillWindowMs;
29
+ const missing = 1 - this.tokens;
30
+ return Math.ceil(missing / refillRate);
31
+ }
32
+ async acquire(provider) {
33
+ let release;
34
+ const previous = this.queue;
35
+ this.queue = new Promise((resolve) => { release = resolve; });
36
+ await previous;
37
+ try {
38
+ while (true) {
39
+ this.refill();
40
+ if (this.tokens >= 1) {
41
+ this.tokens -= 1;
42
+ return;
43
+ }
44
+ const waitMs = this.msUntilNextToken();
45
+ console.log(`[throttle] delaying ${provider} call for ${Math.ceil(waitMs / 1000)}s`);
46
+ await sleep(waitMs);
47
+ }
48
+ }
49
+ finally {
50
+ release();
51
+ }
52
+ }
53
+ }
54
+ export const sharedLLMRateLimiter = new LLMRateLimiter(6, 60_000);
@@ -0,0 +1,65 @@
1
+ /**
2
+ * session-identity.ts
3
+ *
4
+ * 統一的 session 身分解析模組(P0-2)。
5
+ *
6
+ * 替代既有 resolveTrackingSessionKey / extractSessionIdentity 雙鏈,把
7
+ * 「從 payload 抽 session 身分」與「決定對外 canonicalKey」收斂到同一條路徑。
8
+ *
9
+ * - 對外只暴露 canonicalKey;所有 in-memory state map / Set 都應該以它為 key。
10
+ * - canonicalKey 優先序:sessionKey -> session.key -> session.id -> sessionId
11
+ * (與磁碟序列化最重的 transcript archive 既有 sessionKey 命名一致)。
12
+ * - 原始 sessionKey / sessionId 仍保留在 record 裡,給磁碟讀寫拼檔名用。
13
+ *
14
+ * 純函式(除 console / fs 觀測點),不可在這裡碰任何 state map。
15
+ */
16
+ export type SessionIdentitySource = 'sessionKey' | 'session.key' | 'session.id' | 'sessionId' | 'guess' | 'fallback' | 'global';
17
+ export interface SessionIdentity {
18
+ /** 對外統一識別子,所有 state map key 都用這個 */
19
+ canonicalKey: string;
20
+ /** 原始 sessionKey(如果 payload 有) */
21
+ sessionKey: string | null;
22
+ /** 原始 sessionId(如果 payload 有) */
23
+ sessionId: string | null;
24
+ /** canonicalKey 是從哪個欄位拿到的 */
25
+ source: SessionIdentitySource;
26
+ /** 是否走到 'global' fallback(payload 完全沒有 session 身分) */
27
+ isFallback: boolean;
28
+ }
29
+ export declare const GLOBAL_FALLBACK_KEY = "global";
30
+ export type FallbackObserver = (info: {
31
+ signature: string;
32
+ ctx: string;
33
+ }) => void;
34
+ /** 註冊全域 fallback observer;傳 null 取消。 */
35
+ export declare function setFallbackObserver(fn: FallbackObserver | null): void;
36
+ /**
37
+ * 從單一 payload 解析 sessionIdentity。
38
+ * 同時拿 sessionKey 與 sessionId 兩個原始欄位(如果存在),以便磁碟讀寫拼檔名。
39
+ */
40
+ export declare function resolveSessionIdentity(payload: unknown): SessionIdentity;
41
+ /**
42
+ * 從 variadic args 解析(assemble、register hook 場景)。
43
+ * 走訪每個 arg,取第一個能解出非 fallback identity 的;若全部 fallback,回傳 global。
44
+ *
45
+ * 與 resolveTrackingSessionKey 的 variadic 行為等價,但同步把 sessionKey/sessionId 一起帶出。
46
+ */
47
+ export declare function resolveSessionIdentityFromArgs(...args: unknown[]): SessionIdentity;
48
+ /**
49
+ * 已知 canonicalKey 反查 SessionIdentity。
50
+ * 給 cleanup / scheduler / inbox-watcher 等沒有原始 payload 的場景用。
51
+ *
52
+ * source 標 'fallback' 是為了與 'global' 區分:'global' 表示 payload 不帶任何 session
53
+ * 身分,'fallback' 表示我們已經有 canonicalKey 但沒走過 payload resolve 流程。
54
+ */
55
+ export declare function makeSessionIdentity(canonicalKey: string, hints?: {
56
+ sessionKey?: string;
57
+ sessionId?: string;
58
+ }): SessionIdentity;
59
+ /**
60
+ * 從磁碟猜當前 session(mtime 最新的 transcript .jsonl)。
61
+ * 找不到回 null。每次呼叫 log 一條 info,方便觀測有多少流量走這條 fallback。
62
+ */
63
+ export declare function resolveSessionIdentityByGuess(transcriptsDir: string, ctx?: string): SessionIdentity | null;
64
+ /** 重置 fallback dedupe 狀態,僅供測試使用。 */
65
+ export declare function __resetFallbackDedupeForTests(): void;