openhermes 4.9.2 → 4.11.2

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 (69) hide show
  1. package/CONTEXT.md +1 -1
  2. package/README.md +32 -31
  3. package/bootstrap.ts +262 -45
  4. package/harness/agents/oh-planner.md +1 -1
  5. package/harness/agents/openhermes.md +27 -126
  6. package/harness/codex/AUTOPILOT.md +99 -3
  7. package/harness/codex/CHARTER.md +3 -4
  8. package/harness/lib/background/background.test.ts +197 -0
  9. package/harness/lib/background/index.ts +7 -0
  10. package/harness/lib/background/interfaces.ts +31 -0
  11. package/harness/lib/background/manager.ts +320 -0
  12. package/harness/lib/composer/compose.test.ts +168 -0
  13. package/harness/lib/composer/compose.ts +65 -0
  14. package/harness/lib/composer/fragments/01-identity.md +1 -0
  15. package/harness/lib/composer/fragments/02-delegation.md +6 -0
  16. package/harness/lib/composer/fragments/03-permissions.md +13 -0
  17. package/harness/lib/composer/fragments/04-task-flow.md +15 -0
  18. package/harness/lib/composer/fragments/05-confidence.md +5 -0
  19. package/harness/lib/composer/fragments/06-parallelization.md +17 -0
  20. package/harness/lib/composer/fragments/07-shell.md +41 -0
  21. package/harness/lib/composer/fragments/08-routing.md +8 -0
  22. package/harness/lib/composer/fragments/09-guardrails.md +12 -0
  23. package/harness/lib/composer/index.ts +1 -0
  24. package/harness/lib/hooks/builtins/confidence-gate-hook.ts +70 -0
  25. package/harness/lib/hooks/builtins/delegation-depth-hook.ts +59 -0
  26. package/harness/lib/hooks/builtins/error-recovery-hook.ts +107 -0
  27. package/harness/lib/hooks/builtins/memory-sync-hook.ts +73 -0
  28. package/harness/lib/hooks/builtins/plan-check-hook.ts +43 -0
  29. package/harness/lib/hooks/builtins/route-tracking-hook.ts +147 -0
  30. package/harness/lib/hooks/builtins/sanity-check-hook.ts +52 -0
  31. package/harness/lib/hooks/builtins/shell-detect-hook.ts +96 -0
  32. package/harness/lib/hooks/hooks.test.ts +1016 -0
  33. package/harness/lib/hooks/index.ts +30 -0
  34. package/harness/lib/hooks/registry.ts +416 -0
  35. package/harness/lib/hooks/types.ts +71 -0
  36. package/harness/lib/memory/index.ts +18 -0
  37. package/harness/lib/memory/interfaces.ts +53 -0
  38. package/harness/lib/memory/memory-manager.ts +205 -0
  39. package/harness/lib/memory/memory.test.ts +491 -0
  40. package/harness/lib/memory/plan-store.ts +366 -0
  41. package/harness/lib/recovery/handler.ts +243 -0
  42. package/harness/lib/recovery/index.ts +14 -0
  43. package/harness/lib/recovery/interfaces.ts +48 -0
  44. package/harness/lib/recovery/patterns.ts +149 -0
  45. package/harness/lib/recovery/recovery.test.ts +312 -0
  46. package/harness/lib/sanity/anomaly-tracker.ts +127 -0
  47. package/harness/lib/sanity/checker.ts +178 -0
  48. package/harness/lib/sanity/index.ts +13 -0
  49. package/harness/lib/sanity/interfaces.ts +24 -0
  50. package/harness/lib/sanity/sanity.test.ts +472 -0
  51. package/harness/lib/sync/file-watcher.ts +174 -0
  52. package/harness/lib/sync/index.ts +11 -0
  53. package/harness/lib/sync/interfaces.ts +27 -0
  54. package/harness/lib/sync/plan-sync.ts +536 -0
  55. package/harness/lib/sync/sync.test.ts +832 -0
  56. package/harness/skills/oh-init/DEEP.md +2 -2
  57. package/harness/skills/oh-manifest/SKILL.md +1 -1
  58. package/harness/skills/oh-plan-review/DEEP.md +1 -1
  59. package/harness/skills/oh-planner/DEEP.md +3 -3
  60. package/harness/skills/oh-ship/SKILL.md +1 -1
  61. package/harness/skills/oh-skill-craft/SKILL.md +1 -4
  62. package/package.json +5 -5
  63. package/tsconfig.json +1 -1
  64. package/harness/commands/oh-doctor.md +0 -205
  65. package/harness/commands/oh-log.md +0 -18
  66. package/harness/skills/oh-learn/DEEP.md +0 -44
  67. package/harness/skills/oh-learn/SKILL.md +0 -30
  68. package/scripts/count-tokens.mjs +0 -158
  69. package/scripts/oh-doctor.ps1 +0 -342
@@ -0,0 +1,30 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Hook System — barrel export
3
+ // ---------------------------------------------------------------------------
4
+
5
+ export {
6
+ HookPhase,
7
+ HookResult,
8
+ } from "./types.ts";
9
+ export type {
10
+ HookContext,
11
+ HookMetadata,
12
+ PreToolUseHook,
13
+ PostToolUseHook,
14
+ RouteHook,
15
+ SessionHook,
16
+ AnyHook,
17
+ } from "./types.ts";
18
+
19
+ export { HookRegistry } from "./registry.ts";
20
+
21
+ // Built-in hooks
22
+ export { planCheckHook } from "./builtins/plan-check-hook.ts";
23
+ export { shellDetectHook } from "./builtins/shell-detect-hook.ts";
24
+ export { confidenceGateHook } from "./builtins/confidence-gate-hook.ts";
25
+ export { delegationDepthHook, resetDepthTracker } from "./builtins/delegation-depth-hook.ts";
26
+ export { errorRecoveryHook } from "./builtins/error-recovery-hook.ts";
27
+ export { memorySyncHook } from "./builtins/memory-sync-hook.ts";
28
+ export { sanityCheckHook } from "./builtins/sanity-check-hook.ts";
29
+ export { routeTrackingHook, resetRouteTracker, getHopHistory } from "./builtins/route-tracking-hook.ts";
30
+ export type { HopRecord, RouteTrackingConfig } from "./builtins/route-tracking-hook.ts";
@@ -0,0 +1,416 @@
1
+ // ---------------------------------------------------------------------------
2
+ // HookRegistry — singleton pluggable hook system with topological sort
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import type {
6
+ HookContext,
7
+ HookMetadata,
8
+ PreToolUseHook,
9
+ PostToolUseHook,
10
+ RouteHook,
11
+ SessionHook,
12
+ AnyHook,
13
+ } from "./types.ts";
14
+ import { HookPhase, HookResult } from "./types.ts";
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Registry
18
+ // ---------------------------------------------------------------------------
19
+
20
+ export class HookRegistry {
21
+ private static instance: HookRegistry;
22
+
23
+ private preToolHooks: PreToolUseHook[] = [];
24
+ private postToolHooks: PostToolUseHook[] = [];
25
+ private routeHooks: RouteHook[] = [];
26
+ private sessionHooks: SessionHook[] = [];
27
+
28
+ private constructor() {}
29
+
30
+ /** Get the singleton instance. */
31
+ static getInstance(): HookRegistry {
32
+ if (!HookRegistry.instance) {
33
+ HookRegistry.instance = new HookRegistry();
34
+ }
35
+ return HookRegistry.instance;
36
+ }
37
+
38
+ /** Reset singleton — used in tests for isolation. */
39
+ static resetInstance(): void {
40
+ HookRegistry.instance = null as unknown as HookRegistry;
41
+ }
42
+
43
+ // -----------------------------------------------------------------------
44
+ // Registration
45
+ // -----------------------------------------------------------------------
46
+
47
+ registerPreTool(hook: PreToolUseHook): boolean {
48
+ if (!this.assertNoNameConflict(hook.metadata.name, this.preToolHooks)) {
49
+ return false; // already registered, skip silently
50
+ }
51
+ this.preToolHooks.push(hook);
52
+ return true;
53
+ }
54
+
55
+ registerPostTool(hook: PostToolUseHook): boolean {
56
+ if (!this.assertNoNameConflict(hook.metadata.name, this.postToolHooks)) {
57
+ return false;
58
+ }
59
+ this.postToolHooks.push(hook);
60
+ return true;
61
+ }
62
+
63
+ registerRoute(hook: RouteHook): boolean {
64
+ if (!this.assertNoNameConflict(hook.metadata.name, this.routeHooks)) {
65
+ return false;
66
+ }
67
+ this.routeHooks.push(hook);
68
+ return true;
69
+ }
70
+
71
+ registerSession(hook: SessionHook): boolean {
72
+ if (!this.assertNoNameConflict(hook.metadata.name, this.sessionHooks)) {
73
+ return false;
74
+ }
75
+ this.sessionHooks.push(hook);
76
+ return true;
77
+ }
78
+
79
+ /**
80
+ * Unregister a hook by name from ALL categories.
81
+ * Returns true if found and removed, false if not found.
82
+ */
83
+ unregister(name: string): boolean {
84
+ let removed = false;
85
+
86
+ const preIdx = this.preToolHooks.findIndex((h) => h.metadata.name === name);
87
+ if (preIdx >= 0) {
88
+ this.preToolHooks.splice(preIdx, 1);
89
+ removed = true;
90
+ }
91
+
92
+ const postIdx = this.postToolHooks.findIndex(
93
+ (h) => h.metadata.name === name,
94
+ );
95
+ if (postIdx >= 0) {
96
+ this.postToolHooks.splice(postIdx, 1);
97
+ removed = true;
98
+ }
99
+
100
+ const routeIdx = this.routeHooks.findIndex(
101
+ (h) => h.metadata.name === name,
102
+ );
103
+ if (routeIdx >= 0) {
104
+ this.routeHooks.splice(routeIdx, 1);
105
+ removed = true;
106
+ }
107
+
108
+ const sessIdx = this.sessionHooks.findIndex(
109
+ (h) => h.metadata.name === name,
110
+ );
111
+ if (sessIdx >= 0) {
112
+ this.sessionHooks.splice(sessIdx, 1);
113
+ removed = true;
114
+ }
115
+
116
+ return removed;
117
+ }
118
+
119
+ /**
120
+ * Check if a hook with the given name is already registered in the target list.
121
+ * Returns true if the name is available (no conflict), false if already registered.
122
+ * Does NOT throw — silently skips duplicates to support multiple bootstrap calls.
123
+ */
124
+ private assertNoNameConflict(
125
+ name: string,
126
+ list: { metadata: HookMetadata }[],
127
+ ): boolean {
128
+ return !list.some((h) => h.metadata.name === name);
129
+ }
130
+
131
+ // -----------------------------------------------------------------------
132
+ // Execution
133
+ // -----------------------------------------------------------------------
134
+
135
+ /**
136
+ * Execute all PreToolUse hooks in sorted order.
137
+ * Stops execution only on STOP. INJECT signals context modification
138
+ * but does not short-circuit subsequent hooks.
139
+ */
140
+ async executePreTool(
141
+ context: HookContext,
142
+ ): Promise<{ result: HookResult; modifiedContext?: Partial<HookContext> }> {
143
+ const sorted = this.topologicalSort(this.preToolHooks);
144
+ let currentContext = context;
145
+ let hasInjection = false;
146
+
147
+ for (const hook of sorted) {
148
+ const result = await hook.execute(currentContext);
149
+ if (result.modifiedContext) {
150
+ currentContext = { ...currentContext, ...result.modifiedContext };
151
+ }
152
+ if (result.result === HookResult.STOP) {
153
+ return { result: HookResult.STOP, modifiedContext: currentContext };
154
+ }
155
+ if (result.result === HookResult.INJECT) {
156
+ hasInjection = true;
157
+ }
158
+ }
159
+
160
+ return {
161
+ result: hasInjection ? HookResult.INJECT : HookResult.CONTINUE,
162
+ modifiedContext: currentContext,
163
+ };
164
+ }
165
+
166
+ /**
167
+ * Execute all PostToolUse hooks in sorted order.
168
+ * Accumulates output modifications across hooks.
169
+ * INJECT does not short-circuit — all hooks run and the result is aggregated.
170
+ */
171
+ async executePostTool(
172
+ context: HookContext,
173
+ output: string,
174
+ ): Promise<{
175
+ result: HookResult;
176
+ modifiedOutput?: string;
177
+ recovery?: string;
178
+ }> {
179
+ const sorted = this.topologicalSort(this.postToolHooks);
180
+ let currentOutput = output;
181
+ let recovery: string | undefined;
182
+ let hasInjection = false;
183
+
184
+ for (const hook of sorted) {
185
+ const result = await hook.execute(context, currentOutput);
186
+ if (result.modifiedOutput !== undefined) {
187
+ currentOutput = result.modifiedOutput;
188
+ }
189
+ if (result.injectRecovery !== undefined) {
190
+ recovery = result.injectRecovery;
191
+ }
192
+ if (result.result === HookResult.STOP) {
193
+ return {
194
+ result: HookResult.STOP,
195
+ modifiedOutput: currentOutput,
196
+ recovery,
197
+ };
198
+ }
199
+ if (result.result === HookResult.INJECT) {
200
+ hasInjection = true;
201
+ }
202
+ }
203
+
204
+ return {
205
+ result: hasInjection ? HookResult.INJECT : HookResult.CONTINUE,
206
+ modifiedOutput: currentOutput,
207
+ recovery,
208
+ };
209
+ }
210
+
211
+ /**
212
+ * Execute all Route hooks in sorted order.
213
+ * Each hook can modify the route destination.
214
+ * INJECT does not short-circuit — all hooks run and the result is aggregated.
215
+ */
216
+ async executeRoute(
217
+ context: HookContext,
218
+ route: string,
219
+ ): Promise<{ result: HookResult; modifiedRoute?: string }> {
220
+ const sorted = this.topologicalSort(this.routeHooks);
221
+ let currentRoute = route;
222
+ let hasInjection = false;
223
+
224
+ for (const hook of sorted) {
225
+ const result = await hook.execute(context, currentRoute);
226
+ if (result.modifiedRoute !== undefined) {
227
+ currentRoute = result.modifiedRoute;
228
+ }
229
+ if (result.result === HookResult.STOP) {
230
+ return { result: HookResult.STOP, modifiedRoute: currentRoute };
231
+ }
232
+ if (result.result === HookResult.INJECT) {
233
+ hasInjection = true;
234
+ }
235
+ }
236
+
237
+ return {
238
+ result: hasInjection ? HookResult.INJECT : HookResult.CONTINUE,
239
+ modifiedRoute: currentRoute,
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Execute onSessionStart for all Session hooks in sorted order.
245
+ */
246
+ async executeSessionStart(context: HookContext): Promise<void> {
247
+ const sorted = this.topologicalSort(this.sessionHooks);
248
+ for (const hook of sorted) {
249
+ await hook.onSessionStart(context);
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Execute onSessionEnd for all Session hooks in sorted order.
255
+ */
256
+ async executeSessionEnd(context: HookContext): Promise<void> {
257
+ const sorted = this.topologicalSort(this.sessionHooks);
258
+ for (const hook of sorted) {
259
+ await hook.onSessionEnd(context);
260
+ }
261
+ }
262
+
263
+ // -----------------------------------------------------------------------
264
+ // Accessors (for testing)
265
+ // -----------------------------------------------------------------------
266
+
267
+ getPreToolHooks(): PreToolUseHook[] {
268
+ return [...this.preToolHooks];
269
+ }
270
+
271
+ getPostToolHooks(): PostToolUseHook[] {
272
+ return [...this.postToolHooks];
273
+ }
274
+
275
+ getRouteHooks(): RouteHook[] {
276
+ return [...this.routeHooks];
277
+ }
278
+
279
+ getSessionHooks(): SessionHook[] {
280
+ return [...this.sessionHooks];
281
+ }
282
+
283
+ getHookByName(name: string): AnyHook | undefined {
284
+ return (
285
+ this.preToolHooks.find((h) => h.metadata.name === name) ??
286
+ this.postToolHooks.find((h) => h.metadata.name === name) ??
287
+ this.routeHooks.find((h) => h.metadata.name === name) ??
288
+ this.sessionHooks.find((h) => h.metadata.name === name)
289
+ );
290
+ }
291
+
292
+ // -----------------------------------------------------------------------
293
+ // Topological Sort
294
+ // -----------------------------------------------------------------------
295
+
296
+ /**
297
+ * Topological sort using Kahn's algorithm.
298
+ *
299
+ * Ordering rules:
300
+ * 1. Group by phase: EARLY → NORMAL → LATE
301
+ * 2. Within same phase, sort by priority DESC
302
+ * 3. Within same priority, topological dependency order
303
+ *
304
+ * Cycle detection: throws Error("Circular dependency detected: ...")
305
+ * Handles diamond dependencies correctly.
306
+ */
307
+ topologicalSort<T extends { metadata: HookMetadata }>(hooks: T[]): T[] {
308
+ if (hooks.length === 0) return [];
309
+
310
+ // Build name-to-hook map for lookup
311
+ const nameToHook = new Map<string, T>();
312
+ for (const hook of hooks) {
313
+ nameToHook.set(hook.metadata.name, hook);
314
+ }
315
+
316
+ // Separate hooks by phase, keeping only those in the input set
317
+ const phaseGroups = new Map<HookPhase, T[]>();
318
+ for (const hook of hooks) {
319
+ const phase = hook.metadata.phase;
320
+ if (!phaseGroups.has(phase)) phaseGroups.set(phase, []);
321
+ phaseGroups.get(phase)!.push(hook);
322
+ }
323
+
324
+ const result: T[] = [];
325
+
326
+ // Process phases in order: EARLY, NORMAL, LATE
327
+ for (const phase of [HookPhase.EARLY, HookPhase.NORMAL, HookPhase.LATE]) {
328
+ const phaseHooks = phaseGroups.get(phase);
329
+ if (!phaseHooks || phaseHooks.length === 0) continue;
330
+
331
+ // Topologically sort within this phase
332
+ const sorted = this.kahnSort(phaseHooks, nameToHook);
333
+ result.push(...sorted);
334
+ }
335
+
336
+ return result;
337
+ }
338
+
339
+ /**
340
+ * Kahn's algorithm for topological sorting within a phase group.
341
+ * Dependencies are only considered within the hooks passed in.
342
+ */
343
+ private kahnSort<T extends { metadata: HookMetadata }>(
344
+ hooks: T[],
345
+ _allHooks: Map<string, T>,
346
+ ): T[] {
347
+ const nameToHook = new Map<string, T>();
348
+ for (const hook of hooks) {
349
+ nameToHook.set(hook.metadata.name, hook);
350
+ }
351
+
352
+ // Build adjacency list and in-degree map
353
+ const inDegree = new Map<string, number>();
354
+ const adj = new Map<string, string[]>();
355
+
356
+ for (const hook of hooks) {
357
+ const name = hook.metadata.name;
358
+ if (!inDegree.has(name)) inDegree.set(name, 0);
359
+ if (!adj.has(name)) adj.set(name, []);
360
+
361
+ for (const dep of hook.metadata.dependencies) {
362
+ // Only consider dependencies within this phase group
363
+ if (!nameToHook.has(dep)) continue;
364
+
365
+ if (!adj.has(dep)) adj.set(dep, []);
366
+ adj.get(dep)!.push(name);
367
+ inDegree.set(name, (inDegree.get(name) ?? 0) + 1);
368
+ }
369
+ }
370
+
371
+ // Seed queue with zero in-degree nodes, sorted by priority DESC for determinism
372
+ const queue: string[] = [];
373
+ for (const [name, degree] of inDegree) {
374
+ if (degree === 0) queue.push(name);
375
+ }
376
+
377
+ const sorted: T[] = [];
378
+
379
+ while (queue.length > 0) {
380
+ // Sort by priority DESC for deterministic output
381
+ queue.sort((a, b) => {
382
+ const pa = nameToHook.get(a)?.metadata.priority ?? 0;
383
+ const pb = nameToHook.get(b)?.metadata.priority ?? 0;
384
+ if (pb !== pa) return pb - pa;
385
+ // Tie-break by name for stability
386
+ return a.localeCompare(b);
387
+ });
388
+
389
+ const name = queue.shift()!;
390
+ const hook = nameToHook.get(name)!;
391
+ sorted.push(hook);
392
+
393
+ for (const neighbor of adj.get(name) ?? []) {
394
+ const currentDegree = inDegree.get(neighbor) ?? 1;
395
+ const newDegree = currentDegree - 1;
396
+ inDegree.set(neighbor, newDegree);
397
+ if (newDegree === 0) {
398
+ queue.push(neighbor);
399
+ }
400
+ }
401
+ }
402
+
403
+ // Cycle detection
404
+ if (sorted.length !== hooks.length) {
405
+ const sortedNames = new Set(sorted.map((h) => h.metadata.name));
406
+ const unsorted = hooks
407
+ .filter((h) => !sortedNames.has(h.metadata.name))
408
+ .map((h) => h.metadata.name);
409
+ throw new Error(
410
+ `Circular dependency detected: ${unsorted.join(" → ")}`,
411
+ );
412
+ }
413
+
414
+ return sorted;
415
+ }
416
+ }
@@ -0,0 +1,71 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Hook System — type definitions
3
+ // ---------------------------------------------------------------------------
4
+
5
+ export enum HookPhase {
6
+ EARLY = 0,
7
+ NORMAL = 1,
8
+ LATE = 2,
9
+ }
10
+
11
+ export interface HookContext {
12
+ sessionId: string;
13
+ agent: string;
14
+ directory: string;
15
+ sessions: Map<string, unknown>;
16
+ [key: string]: unknown;
17
+ }
18
+
19
+ export interface HookMetadata {
20
+ name: string;
21
+ priority: number; // 0-100, higher = earlier within phase
22
+ phase: HookPhase;
23
+ dependencies: string[]; // hook names this depends on
24
+ errorHandling: "propagate" | "isolate" | "retry";
25
+ }
26
+
27
+ export enum HookResult {
28
+ CONTINUE = "continue",
29
+ STOP = "stop",
30
+ INJECT = "inject",
31
+ }
32
+
33
+ export interface PreToolUseHook {
34
+ metadata: HookMetadata;
35
+ execute(
36
+ context: HookContext,
37
+ ): Promise<{ result: HookResult; modifiedContext?: Partial<HookContext> }>;
38
+ }
39
+
40
+ export interface PostToolUseHook {
41
+ metadata: HookMetadata;
42
+ execute(
43
+ context: HookContext,
44
+ output: string,
45
+ ): Promise<{
46
+ result: HookResult;
47
+ modifiedOutput?: string;
48
+ injectRecovery?: string;
49
+ }>;
50
+ }
51
+
52
+ export interface RouteHook {
53
+ metadata: HookMetadata;
54
+ execute(
55
+ context: HookContext,
56
+ route: string,
57
+ ): Promise<{ result: HookResult; modifiedRoute?: string }>;
58
+ }
59
+
60
+ export interface SessionHook {
61
+ metadata: HookMetadata;
62
+ onSessionStart(context: HookContext): Promise<void>;
63
+ onSessionEnd(context: HookContext): Promise<void>;
64
+ }
65
+
66
+ // Union type for any hook in the registry
67
+ export type AnyHook =
68
+ | PreToolUseHook
69
+ | PostToolUseHook
70
+ | RouteHook
71
+ | SessionHook;
@@ -0,0 +1,18 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Memory module — barrel export
3
+ // ---------------------------------------------------------------------------
4
+
5
+ export {
6
+ MemoryLevel,
7
+ DEFAULT_BUDGETS,
8
+ } from "./interfaces.ts";
9
+ export type {
10
+ MemoryEntry,
11
+ MemorySnapshot,
12
+ MemoryConfig,
13
+ Finding,
14
+ Decision,
15
+ } from "./interfaces.ts";
16
+
17
+ export { MemoryManager } from "./memory-manager.ts";
18
+ export { PlanStore } from "./plan-store.ts";
@@ -0,0 +1,53 @@
1
+ // ---------------------------------------------------------------------------
2
+ // 4-Tier Memory System — interfaces & types
3
+ // ---------------------------------------------------------------------------
4
+
5
+ export enum MemoryLevel {
6
+ SYSTEM = "system", // immutable OpenHermes identity, never pruned
7
+ PROJECT = "project", // project-level config, conventions, decisions
8
+ MISSION = "mission", // current session goal, active plan reference
9
+ TASK = "task", // per-step findings, cleared after each iteration
10
+ }
11
+
12
+ export interface MemoryEntry {
13
+ id: string;
14
+ level: MemoryLevel;
15
+ content: string;
16
+ timestamp: number;
17
+ importance: number; // 0.0 to 1.0
18
+ metadata?: Record<string, string>;
19
+ }
20
+
21
+ export interface MemorySnapshot {
22
+ system: MemoryEntry[];
23
+ project: MemoryEntry[];
24
+ mission: MemoryEntry[];
25
+ task: MemoryEntry[];
26
+ }
27
+
28
+ export interface MemoryConfig {
29
+ budgets: Partial<Record<MemoryLevel, number>>; // max entries per level, defaults filled for missing
30
+ }
31
+
32
+ export interface Finding {
33
+ id: string;
34
+ sessionId: string;
35
+ description: string;
36
+ severity: "info" | "warning" | "blocker";
37
+ timestamp: number;
38
+ }
39
+
40
+ export interface Decision {
41
+ id: string;
42
+ sessionId: string;
43
+ description: string;
44
+ rationale: string;
45
+ timestamp: number;
46
+ }
47
+
48
+ export const DEFAULT_BUDGETS: Record<MemoryLevel, number> = {
49
+ [MemoryLevel.SYSTEM]: 50,
50
+ [MemoryLevel.PROJECT]: 100,
51
+ [MemoryLevel.MISSION]: 30,
52
+ [MemoryLevel.TASK]: 20,
53
+ };