@cortexkit/opencode-magic-context 0.6.2 → 0.7.1

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 (27) hide show
  1. package/README.md +2 -0
  2. package/dist/config/schema/magic-context.d.ts +17 -0
  3. package/dist/config/schema/magic-context.d.ts.map +1 -1
  4. package/dist/features/magic-context/dreamer/runner.d.ts +9 -1
  5. package/dist/features/magic-context/dreamer/runner.d.ts.map +1 -1
  6. package/dist/features/magic-context/dreamer/scheduler.d.ts.map +1 -1
  7. package/dist/features/magic-context/key-files/identify-key-files.d.ts +39 -0
  8. package/dist/features/magic-context/key-files/identify-key-files.d.ts.map +1 -0
  9. package/dist/features/magic-context/key-files/read-stats.d.ts +20 -0
  10. package/dist/features/magic-context/key-files/read-stats.d.ts.map +1 -0
  11. package/dist/features/magic-context/key-files/storage-key-files.d.ts +25 -0
  12. package/dist/features/magic-context/key-files/storage-key-files.d.ts.map +1 -0
  13. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  14. package/dist/hooks/magic-context/command-handler.d.ts +5 -0
  15. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  16. package/dist/hooks/magic-context/hook.d.ts +5 -0
  17. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  18. package/dist/hooks/magic-context/system-prompt-hash.d.ts +4 -0
  19. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +517 -43
  22. package/dist/plugin/dream-timer.d.ts +5 -0
  23. package/dist/plugin/dream-timer.d.ts.map +1 -1
  24. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  25. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/tui/data/context-db.ts +6 -6
package/README.md CHANGED
@@ -45,6 +45,8 @@ Your agent should never stop working to manage its own context. Magic Context is
45
45
 
46
46
  **User Memories** — historian extracts behavioral observations about you alongside its normal compartment output (communication style, expertise level, review focus, working patterns). Recurring observations are promoted by the dreamer to stable user memories that appear in all sessions via `<user-profile>`. Enable with `experimental.user_memories.enabled: true` (requires dreamer).
47
47
 
48
+ **Key File Pinning** — dreamer analyzes which files your agent reads most frequently across the session. Core orientation files (architecture, config, types) that get re-read after every context drop are pinned into the system prompt as `<key-files>`, so the agent always has them without needing to re-read from disk. Files are read fresh on each cache-busting pass. Enable with `experimental.pin_key_files.enabled: true` (requires dreamer).
49
+
48
50
  ---
49
51
 
50
52
  ## Get Started
@@ -195,6 +195,13 @@ export interface MagicContextConfig {
195
195
  enabled: boolean;
196
196
  promotion_threshold: number;
197
197
  };
198
+ pin_key_files: {
199
+ enabled: boolean;
200
+ /** Total token budget for all pinned key files (default: 10000) */
201
+ token_budget: number;
202
+ /** Minimum full-read count before a file is considered for pinning (default: 4) */
203
+ min_reads: number;
204
+ };
198
205
  };
199
206
  embedding: EmbeddingConfig;
200
207
  memory: {
@@ -370,6 +377,11 @@ export declare const MagicContextConfigSchema: z.ZodPipe<z.ZodObject<{
370
377
  enabled: z.ZodDefault<z.ZodBoolean>;
371
378
  promotion_threshold: z.ZodDefault<z.ZodNumber>;
372
379
  }, z.core.$strip>>;
380
+ pin_key_files: z.ZodDefault<z.ZodObject<{
381
+ enabled: z.ZodDefault<z.ZodBoolean>;
382
+ token_budget: z.ZodDefault<z.ZodNumber>;
383
+ min_reads: z.ZodDefault<z.ZodNumber>;
384
+ }, z.core.$strip>>;
373
385
  }, z.core.$strip>>;
374
386
  memory: z.ZodDefault<z.ZodObject<{
375
387
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -470,6 +482,11 @@ export declare const MagicContextConfigSchema: z.ZodPipe<z.ZodObject<{
470
482
  enabled: boolean;
471
483
  promotion_threshold: number;
472
484
  };
485
+ pin_key_files: {
486
+ enabled: boolean;
487
+ token_budget: number;
488
+ min_reads: number;
489
+ };
473
490
  };
474
491
  memory: {
475
492
  enabled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"magic-context.d.ts","sourceRoot":"","sources":["../../../src/config/schema/magic-context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"magic-context.d.ts","sourceRoot":"","sources":["../../../src/config/schema/magic-context.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,11 @@
1
- import type { Database } from "bun:sqlite";
1
+ import { Database } from "bun:sqlite";
2
2
  import type { DreamingTask } from "../../../config/schema/magic-context";
3
3
  import type { PluginContext } from "../../../plugin/types";
4
+ interface ExperimentalPinKeyFilesConfig {
5
+ enabled: boolean;
6
+ token_budget: number;
7
+ min_reads: number;
8
+ }
4
9
  export declare function registerDreamProjectDirectory(projectIdentity: string, directory: string): void;
5
10
  export interface DreamRunResult {
6
11
  startedAt: number;
@@ -29,6 +34,7 @@ export declare function runDream(args: {
29
34
  enabled: boolean;
30
35
  promotionThreshold: number;
31
36
  };
37
+ experimentalPinKeyFiles?: ExperimentalPinKeyFilesConfig;
32
38
  }): Promise<DreamRunResult>;
33
39
  export declare function processDreamQueue(args: {
34
40
  db: Database;
@@ -40,5 +46,7 @@ export declare function processDreamQueue(args: {
40
46
  enabled: boolean;
41
47
  promotionThreshold: number;
42
48
  };
49
+ experimentalPinKeyFiles?: ExperimentalPinKeyFilesConfig;
43
50
  }): Promise<DreamRunResult | null>;
51
+ export {};
44
52
  //# sourceMappingURL=runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/dreamer/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAyB3D,wBAAgB,6BAA6B,CAAC,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAE9F;AAMD,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE;QACH,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAClB,EAAE,CAAC;CACP;AAaD,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACjC,EAAE,EAAE,QAAQ,CAAC;IACb,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,6FAA6F;IAC7F,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wBAAwB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E,GAAG,OAAO,CAAC,cAAc,CAAC,CA8R1B;AA0LD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC1C,EAAE,EAAE,QAAQ,CAAC;IACb,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,wBAAwB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAwDjC"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/dreamer/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AA0C3D,UAAU,6BAA6B;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,6BAA6B,CAAC,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAE9F;AAMD,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE;QACH,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAClB,EAAE,CAAC;CACP;AA6RD,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACjC,EAAE,EAAE,QAAQ,CAAC;IACb,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,6FAA6F;IAC7F,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wBAAwB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5E,uBAAuB,CAAC,EAAE,6BAA6B,CAAC;CAC3D,GAAG,OAAO,CAAC,cAAc,CAAC,CA8S1B;AA0LD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC1C,EAAE,EAAE,QAAQ,CAAC;IACb,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,wBAAwB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5E,uBAAuB,CAAC,EAAE,6BAA6B,CAAC;CAC3D,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAyDjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/dreamer/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,MAAM,WAAW,mBAAmB;IAChC,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,iEAAiE;AACjE,wBAAgB,mBAAmB,CAC/B,QAAQ,EAAE,MAAM,GACjB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAkBrD;AAED,6GAA6G;AAC7G,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,OAAO,CAYpF;AAED,6GAA6G;AAC7G,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,EAAE,CA0C/D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAwB9E"}
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/dreamer/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,MAAM,WAAW,mBAAmB;IAChC,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,iEAAiE;AACjE,wBAAgB,mBAAmB,CAC/B,QAAQ,EAAE,MAAM,GACjB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAkBrD;AAaD,6GAA6G;AAC7G,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,OAAO,CAYpF;AAED,6GAA6G;AAC7G,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,EAAE,CAkD/D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAwB9E"}
@@ -0,0 +1,39 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import { type FileReadStat } from "./read-stats";
3
+ export declare const KEY_FILES_SYSTEM_PROMPT = "You are a file importance evaluator. Given read statistics about files in a coding session, identify which are core orientation files worth pinning in context. Return a JSON array.";
4
+ /**
5
+ * Build the LLM prompt for key file identification.
6
+ * Called from the dreamer runner which handles session creation.
7
+ */
8
+ export declare function buildKeyFilesPrompt(candidates: FileReadStat[], tokenBudget: number, minReads: number): string;
9
+ /**
10
+ * Parse the LLM's response into a ranked file list.
11
+ */
12
+ export declare function parseKeyFilesOutput(text: string): Array<{
13
+ filePath: string;
14
+ tokens: number;
15
+ }>;
16
+ /**
17
+ * Get candidate files for key-file analysis from OpenCode's DB.
18
+ * Returns files with full reads >= minReads and size under half the budget.
19
+ */
20
+ export declare function getKeyFileCandidates(openCodeDb: Database, sessionId: string, minReads: number, tokenBudget: number): FileReadStat[];
21
+ /**
22
+ * Apply LLM-ranked results through the knapsack solver and persist.
23
+ */
24
+ export declare function applyKeyFileResults(db: Database, sessionId: string, llmRanked: Array<{
25
+ filePath: string;
26
+ tokens: number;
27
+ }>, tokenBudget: number, candidatePaths?: Set<string>): {
28
+ filesIdentified: number;
29
+ totalTokens: number;
30
+ };
31
+ /**
32
+ * Pure heuristic fallback when LLM is unavailable.
33
+ * Ranks by: high read count, low edit count, reasonable size.
34
+ */
35
+ export declare function heuristicKeyFileSelection(db: Database, sessionId: string, candidates: FileReadStat[], tokenBudget: number): {
36
+ filesIdentified: number;
37
+ totalTokens: number;
38
+ };
39
+ //# sourceMappingURL=identify-key-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identify-key-files.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/key-files/identify-key-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,KAAK,YAAY,EAAuB,MAAM,cAAc,CAAC;AAGtE,eAAO,MAAM,uBAAuB,yLACsJ,CAAC;AAE3L;;;GAGG;AACH,wBAAgB,mBAAmB,CAC/B,UAAU,EAAE,YAAY,EAAE,EAC1B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACjB,MAAM,CAqCR;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB7F;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAChC,UAAU,EAAE,QAAQ,EACpB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GACpB,YAAY,EAAE,CAIhB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAC/B,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EACtD,WAAW,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAC7B;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAelD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACrC,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,YAAY,EAAE,EAC1B,WAAW,EAAE,MAAM,GACpB;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAmBlD"}
@@ -0,0 +1,20 @@
1
+ import type { Database } from "bun:sqlite";
2
+ export interface FileReadStat {
3
+ filePath: string;
4
+ fullReadCount: number;
5
+ /** Number of distinct compartment ranges the reads span across */
6
+ spreadAcrossCompartments: number;
7
+ /** Number of times the file was edited (write/edit tool) in this session */
8
+ editCount: number;
9
+ /** Byte size of the most recent full read output */
10
+ latestReadBytes: number;
11
+ /** Approximate token count of the most recent full read (~3.5 chars per token) */
12
+ latestReadTokens: number;
13
+ }
14
+ /**
15
+ * Query file read patterns from OpenCode's DB for a specific session.
16
+ * Returns files that were fully read (no line range) at least `minReads` times,
17
+ * sorted by read frequency descending.
18
+ */
19
+ export declare function getSessionReadStats(openCodeDb: Database, sessionId: string, minReads: number): FileReadStat[];
20
+ //# sourceMappingURL=read-stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-stats.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/key-files/read-stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,WAAW,YAAY;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,wBAAwB,EAAE,MAAM,CAAC;IACjC,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,eAAe,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAC/B,UAAU,EAAE,QAAQ,EACpB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACjB,YAAY,EAAE,CAgFhB"}
@@ -0,0 +1,25 @@
1
+ import type { Database } from "bun:sqlite";
2
+ export interface KeyFileEntry {
3
+ filePath: string;
4
+ /** Approximate token count when last pinned */
5
+ tokens: number;
6
+ }
7
+ /**
8
+ * Get the pinned key files for a session from session_meta.
9
+ * Returns empty array if not set or parse fails.
10
+ */
11
+ export declare function getKeyFiles(db: Database, sessionId: string): KeyFileEntry[];
12
+ /**
13
+ * Set the pinned key files for a session.
14
+ */
15
+ export declare function setKeyFiles(db: Database, sessionId: string, files: KeyFileEntry[]): void;
16
+ /**
17
+ * Greedy-fit files into a token budget.
18
+ * Takes files sorted by priority (dreamer's ranking) and greedily adds
19
+ * them until the budget is exhausted. Returns the selected files.
20
+ */
21
+ export declare function greedyFitFiles(rankedFiles: Array<{
22
+ filePath: string;
23
+ tokens: number;
24
+ }>, tokenBudget: number): KeyFileEntry[];
25
+ //# sourceMappingURL=storage-key-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-key-files.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/key-files/storage-key-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,MAAM,WAAW,YAAY;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,EAAE,CAqB3E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,IAAI,CAYxF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC1B,WAAW,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EACxD,WAAW,EAAE,MAAM,GACpB,YAAY,EAAE,CAehB"}
@@ -1 +1 @@
1
- {"version":3,"file":"storage-db.d.ts","sourceRoot":"","sources":["../../../src/features/magic-context/storage-db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAkBtC,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAiRrD;AAiCD,wBAAgB,YAAY,IAAI,QAAQ,CAsCvC;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAEzD;AAED,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAEvE;AAED,wBAAgB,aAAa,IAAI,IAAI,CAUpC;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC"}
1
+ {"version":3,"file":"storage-db.d.ts","sourceRoot":"","sources":["../../../src/features/magic-context/storage-db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAkBtC,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAkRrD;AAiCD,wBAAgB,YAAY,IAAI,QAAQ,CAsCvC;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAEzD;AAED,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAEvE;AAED,wBAAgB,aAAa,IAAI,IAAI,CAUpC;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC"}
@@ -47,6 +47,11 @@ export declare function createMagicContextCommandHandler(deps: {
47
47
  enabled: boolean;
48
48
  promotionThreshold: number;
49
49
  };
50
+ experimentalPinKeyFiles?: {
51
+ enabled: boolean;
52
+ token_budget: number;
53
+ min_reads: number;
54
+ };
50
55
  };
51
56
  }): {
52
57
  "command.execute.before": (input: CommandExecuteInput, _output: CommandExecuteOutput, _params: NotificationParams) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/command-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,EACH,KAAK,cAAc,EAGtB,MAAM,sCAAsC,CAAC;AAG9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAOtE,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjD;AA8JD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE;IACnD,EAAE,EAAE,QAAQ,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0BAA0B,CAAC,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACtF,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,oBAAoB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC5D,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,gBAAgB,EAAE,CACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,kBAAkB,KACzB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,QAAQ,CAAC,EAAE;QACP,MAAM,EAAE,cAAc,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;KACnC,CAAC;IACF,OAAO,CAAC,EAAE;QACN,MAAM,EAAE,aAAa,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;QACrE,wBAAwB,CAAC,EAAE;YAAE,OAAO,EAAE,OAAO,CAAC;YAAC,kBAAkB,EAAE,MAAM,CAAA;SAAE,CAAC;KAC/E,CAAC;CACL;sCASkB,mBAAmB,WACjB,oBAAoB,WACpB,kBAAkB,KAC5B,OAAO,CAAC,IAAI,CAAC;EAoGvB"}
1
+ {"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/command-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,EACH,KAAK,cAAc,EAGtB,MAAM,sCAAsC,CAAC;AAG9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAOtE,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjD;AAoKD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE;IACnD,EAAE,EAAE,QAAQ,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0BAA0B,CAAC,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACtF,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,oBAAoB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC5D,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,gBAAgB,EAAE,CACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,kBAAkB,KACzB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,QAAQ,CAAC,EAAE;QACP,MAAM,EAAE,cAAc,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;KACnC,CAAC;IACF,OAAO,CAAC,EAAE;QACN,MAAM,EAAE,aAAa,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;QACrE,wBAAwB,CAAC,EAAE;YAAE,OAAO,EAAE,OAAO,CAAC;YAAC,kBAAkB,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5E,uBAAuB,CAAC,EAAE;YACtB,OAAO,EAAE,OAAO,CAAC;YACjB,YAAY,EAAE,MAAM,CAAC;YACrB,SAAS,EAAE,MAAM,CAAC;SACrB,CAAC;KACL,CAAC;CACL;sCASkB,mBAAmB,WACjB,oBAAoB,WACpB,kBAAkB,KAC5B,OAAO,CAAC,IAAI,CAAC;EAoGvB"}
@@ -43,6 +43,11 @@ export interface MagicContextDeps {
43
43
  enabled: boolean;
44
44
  promotion_threshold: number;
45
45
  };
46
+ pin_key_files?: {
47
+ enabled: boolean;
48
+ token_budget: number;
49
+ min_reads: number;
50
+ };
46
51
  };
47
52
  };
48
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,KAAK,aAAa,EAClB,KAAK,cAAc,EACtB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAOvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AAMxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAWxD,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAiBnF,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,yBAAyB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,iBAAiB,EAAE,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;IAC9D,MAAM,EAAE;QACJ,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,4BAA4B,CAAC,EAAE,MAAM,GAAG;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACxF,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,uBAAuB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAE9C,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,MAAM,CAAC,EAAE;YACL,OAAO,EAAE,OAAO,CAAC;YACjB,uBAAuB,EAAE,MAAM,CAAC;SACnC,CAAC;QACF,QAAQ,CAAC,EAAE,cAAc,CAAC;QAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;QACxB,sBAAsB,CAAC,EAAE;YAAE,OAAO,EAAE,OAAO,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC;QACpE,YAAY,CAAC,EAAE;YACX,kBAAkB,CAAC,EAAE,OAAO,CAAC;YAC7B,aAAa,CAAC,EAAE;gBAAE,OAAO,EAAE,OAAO,CAAC;gBAAC,mBAAmB,EAAE,MAAM,CAAA;aAAE,CAAC;SACrE,CAAC;KACL,CAAC;CACL;AAqCD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB;;;;;iBAtDtC,CAAC;;;;;;;;;;;;iBAbpB,CAAA;eACA,CAAF;;mBA2S2B;QAAE,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE;;;SAa7E"}
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,KAAK,aAAa,EAClB,KAAK,cAAc,EACtB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAOvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AAMxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAWxD,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAiBnF,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,yBAAyB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,iBAAiB,EAAE,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;IAC9D,MAAM,EAAE;QACJ,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,4BAA4B,CAAC,EAAE,MAAM,GAAG;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACxF,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,uBAAuB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAE9C,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,MAAM,CAAC,EAAE;YACL,OAAO,EAAE,OAAO,CAAC;YACjB,uBAAuB,EAAE,MAAM,CAAC;SACnC,CAAC;QACF,QAAQ,CAAC,EAAE,cAAc,CAAC;QAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;QACxB,sBAAsB,CAAC,EAAE;YAAE,OAAO,EAAE,OAAO,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC;QACpE,YAAY,CAAC,EAAE;YACX,kBAAkB,CAAC,EAAE,OAAO,CAAC;YAC7B,aAAa,CAAC,EAAE;gBAAE,OAAO,EAAE,OAAO,CAAC;gBAAC,mBAAmB,EAAE,MAAM,CAAA;aAAE,CAAC;YAClE,aAAa,CAAC,EAAE;gBAAE,OAAO,EAAE,OAAO,CAAC;gBAAC,YAAY,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;SACjF,CAAC;KACL,CAAC;CACL;AAqCD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB;;;;;iBAxCF,CAAC;;;;;;;;;;;;iBA5BxD,CAAA;eACA,CAAF;;mBA4T2B;QAAE,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE;;;SAa7E"}
@@ -25,6 +25,10 @@ export declare function createSystemPromptHashHandler(deps: {
25
25
  lastHeuristicsTurnId: Map<string, string>;
26
26
  /** When true, inject stable user memories as <user-profile> into system prompt */
27
27
  experimentalUserMemories?: boolean;
28
+ /** When true, inject pinned key files as <key-files> into system prompt */
29
+ experimentalPinKeyFiles?: boolean;
30
+ /** Token budget for key files injection (default 10000) */
31
+ experimentalPinKeyFilesTokenBudget?: number;
28
32
  }): (input: {
29
33
  sessionID?: string;
30
34
  }, output: {
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt-hash.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/system-prompt-hash.ts"],"names":[],"mappings":"AAMA,OAAO,EACH,KAAK,eAAe,EAGvB,MAAM,sCAAsC,CAAC;AAqC9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAChD,EAAE,EAAE,eAAe,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,6FAA6F;IAC7F,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,kFAAkF;IAClF,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACtC,GAAG,CAAC,KAAK,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAgKjF"}
1
+ {"version":3,"file":"system-prompt-hash.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/system-prompt-hash.ts"],"names":[],"mappings":"AAQA,OAAO,EACH,KAAK,eAAe,EAGvB,MAAM,sCAAsC,CAAC;AAwC9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAChD,EAAE,EAAE,eAAe,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,6FAA6F;IAC7F,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,kFAAkF;IAClF,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,2EAA2E;IAC3E,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,2DAA2D;IAC3D,kCAAkC,CAAC,EAAE,MAAM,CAAC;CAC/C,GAAG,CAAC,KAAK,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CA2PjF"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuBlD,QAAA,MAAM,MAAM,EAAE,MA2Lb,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuBlD,QAAA,MAAM,MAAM,EAAE,MAkMb,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js CHANGED
@@ -22202,10 +22202,16 @@ var MagicContextConfigSchema = exports_external.object({
22202
22202
  user_memories: exports_external.object({
22203
22203
  enabled: exports_external.boolean().default(false),
22204
22204
  promotion_threshold: exports_external.number().min(2).max(20).default(3)
22205
- }).default({ enabled: false, promotion_threshold: 3 })
22205
+ }).default({ enabled: false, promotion_threshold: 3 }),
22206
+ pin_key_files: exports_external.object({
22207
+ enabled: exports_external.boolean().default(false),
22208
+ token_budget: exports_external.number().min(2000).max(30000).default(1e4),
22209
+ min_reads: exports_external.number().min(2).max(20).default(4)
22210
+ }).default({ enabled: false, token_budget: 1e4, min_reads: 4 })
22206
22211
  }).default({
22207
22212
  compaction_markers: false,
22208
- user_memories: { enabled: false, promotion_threshold: 3 }
22213
+ user_memories: { enabled: false, promotion_threshold: 3 },
22214
+ pin_key_files: { enabled: false, token_budget: 1e4, min_reads: 4 }
22209
22215
  }),
22210
22216
  memory: exports_external.object({
22211
22217
  enabled: exports_external.boolean().default(true),
@@ -23502,8 +23508,19 @@ function clearStaleEntries(db, maxAgeMs) {
23502
23508
  return result.changes;
23503
23509
  }
23504
23510
  // src/features/magic-context/dreamer/runner.ts
23511
+ import { Database } from "bun:sqlite";
23505
23512
  import { existsSync as existsSync4 } from "fs";
23506
- import { join as join6 } from "path";
23513
+ import { join as join7 } from "path";
23514
+
23515
+ // src/shared/data-path.ts
23516
+ import * as os2 from "os";
23517
+ import * as path2 from "path";
23518
+ function getDataDir() {
23519
+ return process.env.XDG_DATA_HOME ?? path2.join(os2.homedir(), ".local", "share");
23520
+ }
23521
+ function getOpenCodeStorageDir() {
23522
+ return path2.join(getDataDir(), "opencode", "storage");
23523
+ }
23507
23524
 
23508
23525
  // src/shared/error-message.ts
23509
23526
  function getErrorMessage(error48) {
@@ -23513,6 +23530,189 @@ function getErrorMessage(error48) {
23513
23530
  // src/features/magic-context/dreamer/runner.ts
23514
23531
  init_logger();
23515
23532
 
23533
+ // src/features/magic-context/key-files/identify-key-files.ts
23534
+ init_logger();
23535
+
23536
+ // src/features/magic-context/key-files/read-stats.ts
23537
+ function getSessionReadStats(openCodeDb, sessionId, minReads) {
23538
+ const fullReads = openCodeDb.prepare(`
23539
+ WITH full_reads AS (
23540
+ SELECT
23541
+ json_extract(json_extract(data, '$.state'), '$.input.filePath') as file_path,
23542
+ LENGTH(json_extract(json_extract(data, '$.state'), '$.output')) as output_bytes,
23543
+ p.time_created,
23544
+ ROW_NUMBER() OVER (
23545
+ PARTITION BY json_extract(json_extract(data, '$.state'), '$.input.filePath')
23546
+ ORDER BY p.time_created DESC
23547
+ ) as rn
23548
+ FROM part p
23549
+ WHERE p.session_id = ?
23550
+ AND json_extract(data, '$.type') = 'tool'
23551
+ AND json_extract(data, '$.tool') = 'read'
23552
+ AND json_extract(json_extract(data, '$.state'), '$.input.filePath') IS NOT NULL
23553
+ AND json_extract(json_extract(data, '$.state'), '$.input.startLine') IS NULL
23554
+ AND json_extract(json_extract(data, '$.state'), '$.input.start_line') IS NULL
23555
+ AND json_extract(json_extract(data, '$.state'), '$.input.endLine') IS NULL
23556
+ AND json_extract(json_extract(data, '$.state'), '$.input.end_line') IS NULL
23557
+ AND json_extract(json_extract(data, '$.state'), '$.input.offset') IS NULL
23558
+ AND json_extract(json_extract(data, '$.state'), '$.input.limit') IS NULL
23559
+ ),
23560
+ file_counts AS (
23561
+ SELECT file_path, COUNT(*) as full_read_count
23562
+ FROM full_reads
23563
+ GROUP BY file_path
23564
+ HAVING full_read_count >= ?
23565
+ )
23566
+ SELECT
23567
+ r.file_path,
23568
+ fc.full_read_count,
23569
+ r.output_bytes as latest_read_bytes
23570
+ FROM full_reads r
23571
+ JOIN file_counts fc ON r.file_path = fc.file_path
23572
+ WHERE r.rn = 1
23573
+ ORDER BY fc.full_read_count DESC
23574
+ `).all(sessionId, minReads);
23575
+ if (fullReads.length === 0)
23576
+ return [];
23577
+ const editCounts = new Map;
23578
+ const editRows = openCodeDb.prepare(`
23579
+ SELECT
23580
+ json_extract(json_extract(data, '$.state'), '$.input.filePath') as file_path,
23581
+ COUNT(*) as edit_count
23582
+ FROM part p
23583
+ WHERE p.session_id = ?
23584
+ AND json_extract(data, '$.type') = 'tool'
23585
+ AND json_extract(data, '$.tool') IN ('edit', 'write', 'mcp_edit', 'mcp_write')
23586
+ AND json_extract(json_extract(data, '$.state'), '$.input.filePath') IS NOT NULL
23587
+ GROUP BY file_path
23588
+ `).all(sessionId);
23589
+ for (const row of editRows) {
23590
+ editCounts.set(row.file_path, row.edit_count);
23591
+ }
23592
+ return fullReads.map((row) => ({
23593
+ filePath: row.file_path,
23594
+ fullReadCount: row.full_read_count,
23595
+ spreadAcrossCompartments: 0,
23596
+ editCount: editCounts.get(row.file_path) ?? 0,
23597
+ latestReadBytes: row.latest_read_bytes ?? 0,
23598
+ latestReadTokens: Math.ceil((row.latest_read_bytes ?? 0) / 3.5)
23599
+ }));
23600
+ }
23601
+
23602
+ // src/features/magic-context/key-files/storage-key-files.ts
23603
+ init_logger();
23604
+ function getKeyFiles(db, sessionId) {
23605
+ try {
23606
+ const row = db.prepare("SELECT key_files FROM session_meta WHERE session_id = ?").get(sessionId);
23607
+ if (!row?.key_files)
23608
+ return [];
23609
+ const parsed = JSON.parse(row.key_files);
23610
+ if (!Array.isArray(parsed))
23611
+ return [];
23612
+ return parsed.filter((e) => typeof e === "object" && e !== null && typeof e.filePath === "string" && typeof e.tokens === "number");
23613
+ } catch {
23614
+ return [];
23615
+ }
23616
+ }
23617
+ function setKeyFiles(db, sessionId, files) {
23618
+ try {
23619
+ db.prepare("INSERT OR IGNORE INTO session_meta (session_id) VALUES (?)").run(sessionId);
23620
+ db.prepare("UPDATE session_meta SET key_files = ? WHERE session_id = ?").run(JSON.stringify(files), sessionId);
23621
+ } catch (error48) {
23622
+ sessionLog(sessionId, "failed to persist key files:", error48);
23623
+ }
23624
+ }
23625
+ function greedyFitFiles(rankedFiles, tokenBudget) {
23626
+ const selected = [];
23627
+ let remainingBudget = tokenBudget;
23628
+ for (const file2 of rankedFiles) {
23629
+ if (file2.tokens <= 0)
23630
+ continue;
23631
+ if (file2.tokens > remainingBudget)
23632
+ continue;
23633
+ selected.push({ filePath: file2.filePath, tokens: file2.tokens });
23634
+ remainingBudget -= file2.tokens;
23635
+ if (remainingBudget <= 0)
23636
+ break;
23637
+ }
23638
+ return selected;
23639
+ }
23640
+
23641
+ // src/features/magic-context/key-files/identify-key-files.ts
23642
+ var KEY_FILES_SYSTEM_PROMPT = "You are a file importance evaluator. Given read statistics about files in a coding session, identify which are core orientation files worth pinning in context. Return a JSON array.";
23643
+ function buildKeyFilesPrompt(candidates, tokenBudget, minReads) {
23644
+ const statsText = candidates.map((s) => `- **${s.filePath}** \u2014 ${s.fullReadCount} full reads, ${s.editCount} edits, ~${s.latestReadTokens} tokens`).join(`
23645
+ `);
23646
+ return `## Identify Key Files for Pinning
23647
+
23648
+ The following files were fully read ${minReads}+ times during a coding session.
23649
+ Identify which ones are **core orientation files** worth keeping permanently in context.
23650
+
23651
+ ### Signals of a core orientation file:
23652
+ - Read many times across different phases of work (not clustered in one task)
23653
+ - Read without editing \u2014 consulted for understanding, not modification
23654
+ - Contains architecture, configuration, types, or key abstractions
23655
+
23656
+ ### Signals of a NON-core file (exclude):
23657
+ - Read many times but always edited \u2014 actively working on it
23658
+ - Very large (>5000 tokens) \u2014 too expensive to pin
23659
+ - Test files, scripts, or generated files
23660
+
23661
+ ### Token budget: ${tokenBudget} tokens total
23662
+
23663
+ ### Files:
23664
+ ${statsText}
23665
+
23666
+ ### Output Format
23667
+ Return a JSON array ranked by importance (most important first):
23668
+ \`\`\`json
23669
+ [
23670
+ {"filePath": "src/path/to/file.ts", "tokens": 2500, "reason": "brief reason"}
23671
+ ]
23672
+ \`\`\`
23673
+
23674
+ Only include files you're confident are true orientation files. Return empty array if none qualify.`;
23675
+ }
23676
+ function parseKeyFilesOutput(text) {
23677
+ const jsonMatch = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/) ?? text.match(/\[[\s\S]*\]/);
23678
+ if (!jsonMatch)
23679
+ return [];
23680
+ try {
23681
+ const raw = jsonMatch[1] ?? jsonMatch[0];
23682
+ const parsed = JSON.parse(raw);
23683
+ if (!Array.isArray(parsed))
23684
+ return [];
23685
+ return parsed.filter((item) => typeof item === "object" && item !== null && typeof item.filePath === "string" && typeof item.tokens === "number").map((item) => ({ filePath: item.filePath, tokens: item.tokens }));
23686
+ } catch {
23687
+ return [];
23688
+ }
23689
+ }
23690
+ function getKeyFileCandidates(openCodeDb, sessionId, minReads, tokenBudget) {
23691
+ const stats = getSessionReadStats(openCodeDb, sessionId, minReads);
23692
+ const maxPerFileTokens = Math.min(tokenBudget / 2, 5000);
23693
+ return stats.filter((s) => s.latestReadTokens > 0 && s.latestReadTokens <= maxPerFileTokens);
23694
+ }
23695
+ function applyKeyFileResults(db, sessionId, llmRanked, tokenBudget, candidatePaths) {
23696
+ const filtered = candidatePaths ? llmRanked.filter((f) => candidatePaths.has(f.filePath)) : llmRanked;
23697
+ const selected = greedyFitFiles(filtered, tokenBudget);
23698
+ setKeyFiles(db, sessionId, selected);
23699
+ const totalTokens = selected.reduce((sum, f) => sum + f.tokens, 0);
23700
+ log(`[key-files][${sessionId}] pinned ${selected.length} files (${totalTokens} tokens): ${selected.map((f) => f.filePath).join(", ")}`);
23701
+ return { filesIdentified: selected.length, totalTokens };
23702
+ }
23703
+ function heuristicKeyFileSelection(db, sessionId, candidates, tokenBudget) {
23704
+ const scored = candidates.map((c) => ({
23705
+ filePath: c.filePath,
23706
+ tokens: c.latestReadTokens,
23707
+ score: c.fullReadCount * 2 - c.editCount * 3
23708
+ })).filter((c) => c.score > 0).sort((a, b) => b.score - a.score);
23709
+ const selected = greedyFitFiles(scored, tokenBudget);
23710
+ setKeyFiles(db, sessionId, selected);
23711
+ const totalTokens = selected.reduce((sum, f) => sum + f.tokens, 0);
23712
+ log(`[key-files][${sessionId}] heuristic pinned ${selected.length} files (${totalTokens} tokens)`);
23713
+ return { filesIdentified: selected.length, totalTokens };
23714
+ }
23715
+
23516
23716
  // src/features/magic-context/memory/storage-memory-embeddings.ts
23517
23717
  var saveEmbeddingStatements = new WeakMap;
23518
23718
  var loadAllEmbeddingsStatements = new WeakMap;
@@ -24408,6 +24608,176 @@ function countNewIds(beforeIds, afterIds) {
24408
24608
  }
24409
24609
  return count;
24410
24610
  }
24611
+ function getOpenCodeDbPath() {
24612
+ return join7(getDataDir(), "opencode", "opencode.db");
24613
+ }
24614
+ function openOpenCodeDb() {
24615
+ const dbPath = getOpenCodeDbPath();
24616
+ if (!existsSync4(dbPath)) {
24617
+ log(`[key-files] OpenCode DB not found at ${dbPath} \u2014 skipping`);
24618
+ return null;
24619
+ }
24620
+ try {
24621
+ const db = new Database(dbPath, { readonly: true });
24622
+ db.exec("PRAGMA busy_timeout = 5000");
24623
+ return db;
24624
+ } catch (error48) {
24625
+ log(`[key-files] failed to open OpenCode DB at ${dbPath}: ${getErrorMessage(error48)}`);
24626
+ return null;
24627
+ }
24628
+ }
24629
+ function isSessionIdRow(row) {
24630
+ if (row === null || typeof row !== "object") {
24631
+ return false;
24632
+ }
24633
+ return typeof row.sessionId === "string";
24634
+ }
24635
+ function hasExplicitEmptyKeyFilesOutput(text) {
24636
+ return /```(?:json)?\s*\[\s*\]\s*```/s.test(text) || /^\s*\[\s*\]\s*$/s.test(text);
24637
+ }
24638
+ async function getActiveProjectSessionIds(args) {
24639
+ const listResponse = await args.client.session.list({
24640
+ query: { directory: args.sessionDirectory ?? args.projectIdentity }
24641
+ });
24642
+ const sessions = normalizeSDKResponse(listResponse, [], {
24643
+ preferResponseOnMissingData: true
24644
+ });
24645
+ const projectSessionIds = new Set(sessions.map((session) => typeof session?.id === "string" ? session.id : null).filter((sessionId) => Boolean(sessionId)));
24646
+ if (projectSessionIds.size === 0) {
24647
+ return [];
24648
+ }
24649
+ return args.db.prepare("SELECT session_id AS sessionId FROM session_meta WHERE is_subagent = 0 ORDER BY session_id ASC").all().filter(isSessionIdRow).map((row) => row.sessionId).filter((sessionId) => projectSessionIds.has(sessionId));
24650
+ }
24651
+ async function identifyKeyFilesForSession(args) {
24652
+ let openCodeDb = null;
24653
+ try {
24654
+ openCodeDb = openOpenCodeDb();
24655
+ if (!openCodeDb) {
24656
+ return;
24657
+ }
24658
+ const candidates = getKeyFileCandidates(openCodeDb, args.sessionId, args.config.min_reads, args.config.token_budget);
24659
+ if (candidates.length === 0) {
24660
+ log(`[key-files][${args.sessionId}] no candidates found \u2014 skipping`);
24661
+ return;
24662
+ }
24663
+ const prompt = buildKeyFilesPrompt(candidates, args.config.token_budget, args.config.min_reads);
24664
+ const applyHeuristicFallback = () => {
24665
+ heuristicKeyFileSelection(args.db, args.sessionId, candidates, args.config.token_budget);
24666
+ };
24667
+ let agentSessionId = null;
24668
+ const abortController = new AbortController;
24669
+ const leaseInterval = setInterval(() => {
24670
+ try {
24671
+ if (!renewLease(args.db, args.holderId)) {
24672
+ log(`[key-files][${args.sessionId}] lease renewal failed \u2014 aborting`);
24673
+ abortController.abort();
24674
+ }
24675
+ } catch {
24676
+ abortController.abort();
24677
+ }
24678
+ }, 60000);
24679
+ try {
24680
+ const createResponse = await args.client.session.create({
24681
+ body: {
24682
+ ...args.parentSessionId ? { parentID: args.parentSessionId } : {},
24683
+ title: `magic-context-dream-key-files-${args.sessionId.slice(0, 12)}`
24684
+ },
24685
+ query: { directory: args.sessionDirectory }
24686
+ });
24687
+ const created = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
24688
+ agentSessionId = typeof created?.id === "string" ? created.id : null;
24689
+ if (!agentSessionId) {
24690
+ throw new Error("Could not create key-file identification session.");
24691
+ }
24692
+ log(`[key-files][${args.sessionId}] child session created ${agentSessionId}`);
24693
+ const remainingMs = Math.max(0, args.deadline - Date.now());
24694
+ await promptSyncWithModelSuggestionRetry(args.client, {
24695
+ path: { id: agentSessionId },
24696
+ query: { directory: args.sessionDirectory },
24697
+ body: {
24698
+ agent: DREAMER_AGENT,
24699
+ system: KEY_FILES_SYSTEM_PROMPT,
24700
+ parts: [{ type: "text", text: prompt }]
24701
+ }
24702
+ }, { timeoutMs: Math.min(remainingMs, 5 * 60 * 1000), signal: abortController.signal });
24703
+ const messagesResponse = await args.client.session.messages({
24704
+ path: { id: agentSessionId },
24705
+ query: { directory: args.sessionDirectory }
24706
+ });
24707
+ const messages = normalizeSDKResponse(messagesResponse, [], {
24708
+ preferResponseOnMissingData: true
24709
+ });
24710
+ const responseText = extractLatestAssistantText(messages);
24711
+ if (!responseText) {
24712
+ log(`[key-files][${args.sessionId}] no response from agent \u2014 using heuristic fallback`);
24713
+ applyHeuristicFallback();
24714
+ return;
24715
+ }
24716
+ const parsed = parseKeyFilesOutput(responseText);
24717
+ if (parsed.length > 0 || hasExplicitEmptyKeyFilesOutput(responseText)) {
24718
+ const candidatePaths = new Set(candidates.map((c) => c.filePath));
24719
+ applyKeyFileResults(args.db, args.sessionId, parsed, args.config.token_budget, candidatePaths);
24720
+ return;
24721
+ }
24722
+ log(`[key-files][${args.sessionId}] could not parse agent output \u2014 using heuristic fallback`);
24723
+ applyHeuristicFallback();
24724
+ } catch (error48) {
24725
+ log(`[key-files][${args.sessionId}] identification failed: ${getErrorMessage(error48)} \u2014 using heuristic fallback`);
24726
+ try {
24727
+ applyHeuristicFallback();
24728
+ } catch (fallbackError) {
24729
+ log(`[key-files][${args.sessionId}] heuristic fallback failed: ${getErrorMessage(fallbackError)}`);
24730
+ }
24731
+ } finally {
24732
+ clearInterval(leaseInterval);
24733
+ if (agentSessionId) {
24734
+ await args.client.session.delete({
24735
+ path: { id: agentSessionId },
24736
+ query: { directory: args.sessionDirectory }
24737
+ }).catch((error48) => {
24738
+ log(`[key-files][${args.sessionId}] session cleanup failed: ${getErrorMessage(error48)}`);
24739
+ });
24740
+ }
24741
+ }
24742
+ } finally {
24743
+ if (openCodeDb) {
24744
+ try {
24745
+ openCodeDb.close(false);
24746
+ } catch (error48) {
24747
+ log(`[key-files][${args.sessionId}] failed to close OpenCode DB: ${getErrorMessage(error48)}`);
24748
+ }
24749
+ }
24750
+ }
24751
+ }
24752
+ async function identifyKeyFiles(args) {
24753
+ const sessionIds = await getActiveProjectSessionIds({
24754
+ db: args.db,
24755
+ client: args.client,
24756
+ projectIdentity: args.projectIdentity,
24757
+ sessionDirectory: args.sessionDirectory
24758
+ });
24759
+ if (sessionIds.length === 0) {
24760
+ log(`[key-files] no active sessions found for ${args.projectIdentity}`);
24761
+ return;
24762
+ }
24763
+ log(`[key-files] evaluating ${sessionIds.length} active session(s) for ${args.projectIdentity}`);
24764
+ for (const sessionId of sessionIds) {
24765
+ if (Date.now() > args.deadline) {
24766
+ log("[key-files] deadline reached \u2014 stopping key-file identification");
24767
+ break;
24768
+ }
24769
+ await identifyKeyFilesForSession({
24770
+ db: args.db,
24771
+ client: args.client,
24772
+ parentSessionId: args.parentSessionId,
24773
+ sessionDirectory: args.sessionDirectory,
24774
+ holderId: args.holderId,
24775
+ deadline: args.deadline,
24776
+ sessionId,
24777
+ config: args.config
24778
+ });
24779
+ }
24780
+ }
24411
24781
  async function runDream(args) {
24412
24782
  const holderId = crypto.randomUUID();
24413
24783
  const startedAt = Date.now();
@@ -24479,8 +24849,8 @@ async function runDream(args) {
24479
24849
  try {
24480
24850
  const docsDir = args.sessionDirectory ?? args.projectIdentity;
24481
24851
  const existingDocs = taskName === "maintain-docs" ? {
24482
- architecture: existsSync4(join6(docsDir, "ARCHITECTURE.md")),
24483
- structure: existsSync4(join6(docsDir, "STRUCTURE.md"))
24852
+ architecture: existsSync4(join7(docsDir, "ARCHITECTURE.md")),
24853
+ structure: existsSync4(join7(docsDir, "STRUCTURE.md"))
24484
24854
  } : undefined;
24485
24855
  const taskPrompt = buildDreamTaskPrompt(taskName, {
24486
24856
  projectPath: args.projectIdentity,
@@ -24586,6 +24956,22 @@ async function runDream(args) {
24586
24956
  log(`[dreamer] smart note evaluation failed: ${getErrorMessage(error48)}`);
24587
24957
  }
24588
24958
  }
24959
+ if (args.experimentalPinKeyFiles?.enabled && Date.now() <= deadline) {
24960
+ try {
24961
+ await identifyKeyFiles({
24962
+ db: args.db,
24963
+ client: args.client,
24964
+ projectIdentity: args.projectIdentity,
24965
+ parentSessionId,
24966
+ sessionDirectory: args.sessionDirectory ?? args.projectIdentity,
24967
+ holderId,
24968
+ deadline,
24969
+ config: args.experimentalPinKeyFiles
24970
+ });
24971
+ } catch (error48) {
24972
+ log(`[key-files] identification phase failed: ${getErrorMessage(error48)}`);
24973
+ }
24974
+ }
24589
24975
  } finally {
24590
24976
  releaseLease(args.db, holderId);
24591
24977
  log(`[dreamer] lease released: ${holderId}`);
@@ -24794,7 +25180,8 @@ async function processDreamQueue(args) {
24794
25180
  taskTimeoutMinutes: args.taskTimeoutMinutes,
24795
25181
  maxRuntimeMinutes: args.maxRuntimeMinutes,
24796
25182
  sessionDirectory: projectDirectory,
24797
- experimentalUserMemories: args.experimentalUserMemories
25183
+ experimentalUserMemories: args.experimentalUserMemories,
25184
+ experimentalPinKeyFiles: args.experimentalPinKeyFiles
24798
25185
  });
24799
25186
  } catch (error48) {
24800
25187
  log(`[dreamer] runDream threw for ${entry.projectIdentity}: ${getErrorMessage(error48)}`);
@@ -24833,6 +25220,10 @@ function parseScheduleWindow(schedule) {
24833
25220
  const endMinutes = endHour * 60 + endMin;
24834
25221
  return { startMinutes, endMinutes };
24835
25222
  }
25223
+ function isDreamFromCurrentWindow(lastDreamAtMs, now) {
25224
+ const twelveHoursMs = 12 * 60 * 60 * 1000;
25225
+ return now.getTime() - lastDreamAtMs < twelveHoursMs;
25226
+ }
24836
25227
  function isInScheduleWindow(schedule, now = new Date) {
24837
25228
  const window = parseScheduleWindow(schedule);
24838
25229
  if (!window)
@@ -24850,10 +25241,14 @@ function findProjectsNeedingDream(db) {
24850
25241
  WHERE type = 'smart' AND status = 'pending' AND project_path IS NOT NULL
24851
25242
  ORDER BY project_path`).all();
24852
25243
  const projects = [];
25244
+ const now = new Date;
24853
25245
  for (const row of projectRows) {
24854
25246
  const lastDreamAtStr = getDreamState(db, `last_dream_at:${row.project_path}`);
24855
25247
  const fallbackStr = !lastDreamAtStr ? getDreamState(db, "last_dream_at") : null;
24856
25248
  const lastDreamAt = Number(lastDreamAtStr ?? fallbackStr ?? "0") || 0;
25249
+ if (lastDreamAt > 0 && isDreamFromCurrentWindow(lastDreamAt, now)) {
25250
+ continue;
25251
+ }
24857
25252
  const updatedMemories = db.query(`SELECT COUNT(*) as cnt FROM memories
24858
25253
  WHERE project_path = ? AND status = 'active' AND updated_at > ?`).get(row.project_path, lastDreamAt);
24859
25254
  const pendingSmartNotes = db.query(`SELECT COUNT(*) as cnt FROM notes
@@ -25291,7 +25686,7 @@ function getEmbeddingModelId() {
25291
25686
 
25292
25687
  // src/features/magic-context/memory/project-identity.ts
25293
25688
  import { execSync } from "child_process";
25294
- import path2 from "path";
25689
+ import path3 from "path";
25295
25690
  var GIT_TIMEOUT_MS = 5000;
25296
25691
  var resolvedCache = new Map;
25297
25692
  function getRootCommitHash(directory) {
@@ -25310,12 +25705,12 @@ function getRootCommitHash(directory) {
25310
25705
  }
25311
25706
  }
25312
25707
  function directoryFallback(directory) {
25313
- const canonical = path2.resolve(directory);
25708
+ const canonical = path3.resolve(directory);
25314
25709
  const hash2 = Bun.hash(canonical).toString(16).slice(0, 12);
25315
25710
  return `dir:${hash2}`;
25316
25711
  }
25317
25712
  function resolveProjectIdentity(directory) {
25318
- const resolved = path2.resolve(directory);
25713
+ const resolved = path3.resolve(directory);
25319
25714
  const cached2 = resolvedCache.get(resolved);
25320
25715
  if (cached2 !== undefined) {
25321
25716
  return cached2;
@@ -25402,22 +25797,10 @@ function removeSystemReminders(text) {
25402
25797
  }
25403
25798
 
25404
25799
  // src/hooks/magic-context/read-session-db.ts
25405
- import { Database } from "bun:sqlite";
25800
+ import { Database as Database2 } from "bun:sqlite";
25406
25801
  import { join as join8 } from "path";
25407
-
25408
- // src/shared/data-path.ts
25409
- import * as os2 from "os";
25410
- import * as path3 from "path";
25411
- function getDataDir() {
25412
- return process.env.XDG_DATA_HOME ?? path3.join(os2.homedir(), ".local", "share");
25413
- }
25414
- function getOpenCodeStorageDir() {
25415
- return path3.join(getDataDir(), "opencode", "storage");
25416
- }
25417
-
25418
- // src/hooks/magic-context/read-session-db.ts
25419
25802
  init_logger();
25420
- function getOpenCodeDbPath() {
25803
+ function getOpenCodeDbPath2() {
25421
25804
  return join8(getDataDir(), "opencode", "opencode.db");
25422
25805
  }
25423
25806
  var cachedReadOnlyDb = null;
@@ -25434,12 +25817,12 @@ function closeCachedReadOnlyDb() {
25434
25817
  }
25435
25818
  }
25436
25819
  function getReadOnlySessionDb() {
25437
- const dbPath = getOpenCodeDbPath();
25820
+ const dbPath = getOpenCodeDbPath2();
25438
25821
  if (cachedReadOnlyDb?.path === dbPath) {
25439
25822
  return cachedReadOnlyDb.db;
25440
25823
  }
25441
25824
  closeCachedReadOnlyDb();
25442
- const db = new Database(dbPath, { readonly: true });
25825
+ const db = new Database2(dbPath, { readonly: true });
25443
25826
  cachedReadOnlyDb = { path: dbPath, db };
25444
25827
  return db;
25445
25828
  }
@@ -25992,7 +26375,7 @@ function ensureMessagesIndexed(db, sessionId, readMessages) {
25992
26375
  })();
25993
26376
  }
25994
26377
  // src/features/magic-context/storage-db.ts
25995
- import { Database as Database2 } from "bun:sqlite";
26378
+ import { Database as Database3 } from "bun:sqlite";
25996
26379
  import { mkdirSync } from "fs";
25997
26380
  import { join as join9 } from "path";
25998
26381
  init_logger();
@@ -26428,6 +26811,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
26428
26811
  ensureColumn(db, "tags", "reasoning_byte_size", "INTEGER DEFAULT 0");
26429
26812
  ensureColumn(db, "session_meta", "system_prompt_tokens", "INTEGER DEFAULT 0");
26430
26813
  ensureColumn(db, "session_meta", "compaction_marker_state", "TEXT DEFAULT ''");
26814
+ ensureColumn(db, "session_meta", "key_files", "TEXT DEFAULT ''");
26431
26815
  }
26432
26816
  function ensureColumn(db, table, column, definition) {
26433
26817
  if (!/^[a-z_]+$/.test(table) || !/^[a-z_]+$/.test(column) || !/^[A-Z0-9_'(),\s]+$/i.test(definition)) {
@@ -26441,7 +26825,7 @@ function ensureColumn(db, table, column, definition) {
26441
26825
  }
26442
26826
  function createFallbackDatabase() {
26443
26827
  try {
26444
- const fallback = new Database2(":memory:");
26828
+ const fallback = new Database3(":memory:");
26445
26829
  initializeDatabase(fallback);
26446
26830
  runMigrations(fallback);
26447
26831
  return fallback;
@@ -26460,7 +26844,7 @@ function openDatabase() {
26460
26844
  return existing;
26461
26845
  }
26462
26846
  mkdirSync(dbDir, { recursive: true });
26463
- const db = new Database2(dbPath);
26847
+ const db = new Database3(dbPath);
26464
26848
  initializeDatabase(db);
26465
26849
  runMigrations(db);
26466
26850
  databases.set(dbPath, db);
@@ -27012,7 +27396,8 @@ function startDreamScheduleTimer(args) {
27012
27396
  dreamerConfig,
27013
27397
  embeddingConfig: embeddingConfig2,
27014
27398
  memoryEnabled,
27015
- experimentalUserMemories
27399
+ experimentalUserMemories,
27400
+ experimentalPinKeyFiles
27016
27401
  } = args;
27017
27402
  const dreamingEnabled = Boolean(dreamerConfig?.enabled && dreamerConfig.schedule?.trim());
27018
27403
  const embeddingSweepEnabled = memoryEnabled && embeddingConfig2.provider !== "off";
@@ -27042,7 +27427,8 @@ function startDreamScheduleTimer(args) {
27042
27427
  tasks: dreamerConfig.tasks,
27043
27428
  taskTimeoutMinutes: dreamerConfig.task_timeout_minutes,
27044
27429
  maxRuntimeMinutes: dreamerConfig.max_runtime_minutes,
27045
- experimentalUserMemories
27430
+ experimentalUserMemories,
27431
+ experimentalPinKeyFiles
27046
27432
  }).catch((error48) => {
27047
27433
  log("[dreamer] timer-triggered queue processing failed:", error48);
27048
27434
  });
@@ -27841,7 +28227,8 @@ Dreaming is not configured for this project.`, {});
27841
28227
  tasks: deps.dreamer.config.tasks,
27842
28228
  taskTimeoutMinutes: deps.dreamer.config.task_timeout_minutes,
27843
28229
  maxRuntimeMinutes: deps.dreamer.config.max_runtime_minutes,
27844
- experimentalUserMemories: deps.dreamer.experimentalUserMemories
28230
+ experimentalUserMemories: deps.dreamer.experimentalUserMemories,
28231
+ experimentalPinKeyFiles: deps.dreamer.experimentalPinKeyFiles
27845
28232
  });
27846
28233
  await deps.sendNotification(sessionId, result ? summarizeDreamResult(result) : "Dream queued, but another worker is already processing the queue.", {});
27847
28234
  throw new Error(`${SENTINEL_PREFIX}CTX-DREAM_HANDLED__`);
@@ -27930,7 +28317,7 @@ Historian recomp started. Rebuilding compartments and facts from raw session his
27930
28317
  init_logger();
27931
28318
 
27932
28319
  // src/features/magic-context/compaction-marker.ts
27933
- import { Database as Database3 } from "bun:sqlite";
28320
+ import { Database as Database4 } from "bun:sqlite";
27934
28321
  import { join as join10 } from "path";
27935
28322
  init_logger();
27936
28323
  var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@@ -27952,12 +28339,12 @@ function generateMessageId(timestampMs, counter = 0n) {
27952
28339
  function generatePartId(timestampMs, counter = 0n) {
27953
28340
  return generateId("prt", timestampMs, counter);
27954
28341
  }
27955
- function getOpenCodeDbPath2() {
28342
+ function getOpenCodeDbPath3() {
27956
28343
  return join10(getDataDir(), "opencode", "opencode.db");
27957
28344
  }
27958
28345
  var cachedWriteDb = null;
27959
28346
  function getWritableOpenCodeDb() {
27960
- const dbPath = getOpenCodeDbPath2();
28347
+ const dbPath = getOpenCodeDbPath3();
27961
28348
  if (cachedWriteDb?.path === dbPath) {
27962
28349
  return cachedWriteDb.db;
27963
28350
  }
@@ -27966,7 +28353,7 @@ function getWritableOpenCodeDb() {
27966
28353
  cachedWriteDb.db.close(false);
27967
28354
  } catch {}
27968
28355
  }
27969
- const db = new Database3(dbPath);
28356
+ const db = new Database4(dbPath);
27970
28357
  db.exec("PRAGMA journal_mode=WAL");
27971
28358
  db.exec("PRAGMA busy_timeout=5000");
27972
28359
  cachedWriteDb = { path: dbPath, db };
@@ -31965,8 +32352,8 @@ function createToolExecuteAfterHook(args) {
31965
32352
  }
31966
32353
 
31967
32354
  // src/hooks/magic-context/system-prompt-hash.ts
31968
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
31969
- import { join as join12 } from "path";
32355
+ import { existsSync as existsSync5, readFileSync as readFileSync4, realpathSync } from "fs";
32356
+ import { join as join12, resolve as resolve2, sep } from "path";
31970
32357
 
31971
32358
  // src/agents/magic-context-prompt.ts
31972
32359
  var BASE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with \xA7N\xA7 identifiers (e.g., \xA71\xA7, \xA742\xA7).
@@ -32168,7 +32555,9 @@ init_logger();
32168
32555
  var MAGIC_CONTEXT_MARKER = "## Magic Context";
32169
32556
  var PROJECT_DOCS_MARKER = "<project-docs>";
32170
32557
  var USER_PROFILE_MARKER = "<user-profile>";
32558
+ var KEY_FILES_MARKER = "<key-files>";
32171
32559
  var cachedUserProfileBySession = new Map;
32560
+ var cachedKeyFilesBySession = new Map;
32172
32561
  var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
32173
32562
  function readProjectDocs(directory) {
32174
32563
  const sections = [];
@@ -32250,6 +32639,72 @@ ${items}
32250
32639
  output.system.push(profileBlock);
32251
32640
  }
32252
32641
  }
32642
+ if (deps.experimentalPinKeyFiles) {
32643
+ const hasCachedKeyFiles = cachedKeyFilesBySession.has(sessionId);
32644
+ if (!hasCachedKeyFiles || isCacheBusting) {
32645
+ const keyFileEntries = getKeyFiles(deps.db, sessionId);
32646
+ if (keyFileEntries.length > 0) {
32647
+ const sections = [];
32648
+ const projectRoot = resolve2(deps.directory);
32649
+ let remainingBudgetTokens = deps.experimentalPinKeyFilesTokenBudget ?? 1e4;
32650
+ for (const entry of keyFileEntries) {
32651
+ try {
32652
+ const absPath = resolve2(deps.directory, entry.filePath);
32653
+ if (!absPath.startsWith(projectRoot + sep) && absPath !== projectRoot) {
32654
+ log(`[magic-context] key file path escapes project root, skipping: ${entry.filePath}`);
32655
+ continue;
32656
+ }
32657
+ if (!existsSync5(absPath))
32658
+ continue;
32659
+ let realPath;
32660
+ try {
32661
+ realPath = realpathSync(absPath);
32662
+ } catch {
32663
+ continue;
32664
+ }
32665
+ if (!realPath.startsWith(projectRoot + sep) && realPath !== projectRoot) {
32666
+ log(`[magic-context] key file symlink escapes project root, skipping: ${entry.filePath} \u2192 ${realPath}`);
32667
+ continue;
32668
+ }
32669
+ const content = readFileSync4(realPath, "utf-8").trim();
32670
+ if (content.length === 0)
32671
+ continue;
32672
+ const fileTokens = estimateTokens(content);
32673
+ if (fileTokens > remainingBudgetTokens) {
32674
+ log(`[magic-context] key file ${entry.filePath} exceeds remaining budget (${fileTokens} > ${remainingBudgetTokens}), skipping`);
32675
+ continue;
32676
+ }
32677
+ remainingBudgetTokens -= fileTokens;
32678
+ sections.push(`<file path="${escapeXmlAttr(entry.filePath)}">
32679
+ ${escapeXmlContent(content)}
32680
+ </file>`);
32681
+ } catch (error48) {
32682
+ log(`[magic-context] failed to read key file ${entry.filePath}:`, error48);
32683
+ }
32684
+ }
32685
+ if (sections.length > 0) {
32686
+ cachedKeyFilesBySession.set(sessionId, `${KEY_FILES_MARKER}
32687
+ ${sections.join(`
32688
+
32689
+ `)}
32690
+ </key-files>`);
32691
+ if (!hasCachedKeyFiles) {
32692
+ sessionLog(sessionId, `loaded ${sections.length} key file(s) into system prompt`);
32693
+ } else {
32694
+ sessionLog(sessionId, "refreshed key files (cache-busting pass)");
32695
+ }
32696
+ } else {
32697
+ cachedKeyFilesBySession.set(sessionId, null);
32698
+ }
32699
+ } else {
32700
+ cachedKeyFilesBySession.set(sessionId, null);
32701
+ }
32702
+ }
32703
+ const keyFilesBlock = cachedKeyFilesBySession.get(sessionId);
32704
+ if (keyFilesBlock && !fullPrompt.includes(KEY_FILES_MARKER)) {
32705
+ output.system.push(keyFilesBlock);
32706
+ }
32707
+ }
32253
32708
  const DATE_PATTERN = /Today's date: .+/;
32254
32709
  for (let i = 0;i < output.system.length; i++) {
32255
32710
  const match = output.system[i].match(DATE_PATTERN);
@@ -32290,7 +32745,7 @@ ${items}
32290
32745
  } else if (previousHash === "" || previousHash === "0") {
32291
32746
  sessionLog(sessionId, `system prompt hash initialized: ${currentHash} (len=${systemContent.length})`);
32292
32747
  }
32293
- const systemPromptTokens = Math.ceil(systemContent.length / 4);
32748
+ const systemPromptTokens = estimateTokens(systemContent);
32294
32749
  if (currentHash !== previousHash) {
32295
32750
  updateSessionMeta(deps.db, sessionId, {
32296
32751
  systemPromptHash: currentHash,
@@ -32424,6 +32879,11 @@ function createMagicContextHook(deps) {
32424
32879
  experimentalUserMemories: deps.config.experimental?.user_memories?.enabled ? {
32425
32880
  enabled: true,
32426
32881
  promotionThreshold: deps.config.experimental.user_memories?.promotion_threshold
32882
+ } : undefined,
32883
+ experimentalPinKeyFiles: deps.config.experimental?.pin_key_files?.enabled ? {
32884
+ enabled: true,
32885
+ token_budget: deps.config.experimental.pin_key_files?.token_budget,
32886
+ min_reads: deps.config.experimental.pin_key_files?.min_reads
32427
32887
  } : undefined
32428
32888
  }).catch((error48) => {
32429
32889
  log("[dreamer] scheduled queue processing failed:", error48);
@@ -32470,6 +32930,11 @@ function createMagicContextHook(deps) {
32470
32930
  experimentalUserMemories: deps.config.experimental?.user_memories?.enabled ? {
32471
32931
  enabled: true,
32472
32932
  promotionThreshold: deps.config.experimental.user_memories?.promotion_threshold
32933
+ } : undefined,
32934
+ experimentalPinKeyFiles: deps.config.experimental?.pin_key_files?.enabled ? {
32935
+ enabled: true,
32936
+ token_budget: deps.config.experimental.pin_key_files?.token_budget,
32937
+ min_reads: deps.config.experimental.pin_key_files?.min_reads
32473
32938
  } : undefined
32474
32939
  } : undefined
32475
32940
  });
@@ -32483,7 +32948,9 @@ function createMagicContextHook(deps) {
32483
32948
  directory: deps.directory,
32484
32949
  flushedSessions,
32485
32950
  lastHeuristicsTurnId,
32486
- experimentalUserMemories: deps.config.experimental?.user_memories?.enabled
32951
+ experimentalUserMemories: deps.config.experimental?.user_memories?.enabled,
32952
+ experimentalPinKeyFiles: deps.config.experimental?.pin_key_files?.enabled ?? false,
32953
+ experimentalPinKeyFilesTokenBudget: deps.config.experimental?.pin_key_files?.token_budget
32487
32954
  });
32488
32955
  const eventHook = createEventHook({
32489
32956
  eventHandler,
@@ -32962,15 +33429,17 @@ Historian reads these notes, deduplicates them, and rewrites the remaining usefu
32962
33429
  import { tool as tool3 } from "@opencode-ai/plugin";
32963
33430
  function formatNoteLine(note) {
32964
33431
  const statusSuffix = note.status === "active" ? "" : ` (${note.status})`;
32965
- const dismissHint = note.status === "dismissed" ? "" : ` _(dismiss with \`ctx_note(action="dismiss", note_id=${note.id})\`)_`;
32966
33432
  if (note.type === "session") {
32967
- return `- **#${note.id}**${statusSuffix}: ${note.content}${dismissHint}`;
33433
+ return `- **#${note.id}**${statusSuffix}: ${note.content}`;
32968
33434
  }
32969
33435
  const conditionText = note.status === "ready" ? note.readyReason ?? note.surfaceCondition ?? "Condition satisfied" : note.surfaceCondition ?? "No condition recorded";
32970
33436
  const conditionLabel = note.status === "ready" ? "Condition met" : "Condition";
32971
33437
  return `- **#${note.id}**${statusSuffix}: ${note.content}
32972
- ${conditionLabel}: ${conditionText}${dismissHint}`;
33438
+ ${conditionLabel}: ${conditionText}`;
32973
33439
  }
33440
+ var DISMISS_FOOTER = `
33441
+
33442
+ To dismiss a stale note: ctx_note(action="dismiss", note_id=N)`;
32974
33443
  function buildReadSections(args) {
32975
33444
  if (args.filter === undefined) {
32976
33445
  const sessionNotes2 = getSessionNotes(args.db, args.sessionId);
@@ -33109,7 +33578,7 @@ No session notes or smart notes.`;
33109
33578
  }
33110
33579
  return sections.join(`
33111
33580
 
33112
- `);
33581
+ `) + DISMISS_FOOTER;
33113
33582
  }
33114
33583
  });
33115
33584
  }
@@ -33880,6 +34349,11 @@ var plugin = async (ctx) => {
33880
34349
  experimentalUserMemories: pluginConfig.experimental?.user_memories?.enabled ? {
33881
34350
  enabled: true,
33882
34351
  promotionThreshold: pluginConfig.experimental.user_memories?.promotion_threshold
34352
+ } : undefined,
34353
+ experimentalPinKeyFiles: pluginConfig.experimental?.pin_key_files?.enabled ? {
34354
+ enabled: true,
34355
+ token_budget: pluginConfig.experimental.pin_key_files?.token_budget,
34356
+ min_reads: pluginConfig.experimental.pin_key_files?.min_reads
33883
34357
  } : undefined
33884
34358
  });
33885
34359
  startTuiActionConsumer({
@@ -17,5 +17,10 @@ export declare function startDreamScheduleTimer(args: {
17
17
  enabled: boolean;
18
18
  promotionThreshold: number;
19
19
  };
20
+ experimentalPinKeyFiles?: {
21
+ enabled: boolean;
22
+ token_budget: number;
23
+ min_reads: number;
24
+ };
20
25
  }): (() => void) | undefined;
21
26
  //# sourceMappingURL=dream-timer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dream-timer.d.ts","sourceRoot":"","sources":["../../src/plugin/dream-timer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAMrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,eAAe,EAAE,eAAe,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,wBAAwB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAqE3B"}
1
+ {"version":3,"file":"dream-timer.d.ts","sourceRoot":"","sources":["../../src/plugin/dream-timer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAMrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,eAAe,EAAE,eAAe,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,wBAAwB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5E,uBAAuB,CAAC,EAAE;QACtB,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACrB,CAAC;CACL,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAuE3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-session-hooks.d.ts","sourceRoot":"","sources":["../../../src/plugin/hooks/create-session-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAU7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,aAAa,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC;CAC1C;;;;;;qBA4C+R,CAAC;;;;;;;;;;;;qBATjR,CAAN;mBAAiB,CAAC;;;;;0BASwtW,CAAC;;;;;;EADpvW"}
1
+ {"version":3,"file":"create-session-hooks.d.ts","sourceRoot":"","sources":["../../../src/plugin/hooks/create-session-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAU7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,aAAa,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC;CAC1C;;;;;;qBA4Cq4B,CAAC;;;;;;;;;;;;qBATv3B,CAAN;mBAAiB,CAAC;;;;;0BASwvY,CAAC;;;;;;EADpxY"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/tools/ctx-note/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,KAAK,cAAc,EAAQ,MAAM,qBAAqB,CAAC;AAchE,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,QAAQ,CAAC;IACb,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAkND,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAIxF"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/tools/ctx-note/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,KAAK,cAAc,EAAQ,MAAM,qBAAqB,CAAC;AAchE,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,QAAQ,CAAC;IACb,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAgND,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAIxF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/opencode-magic-context",
3
- "version": "0.6.2",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Magic Context — cross-session memory and context management",
6
6
  "main": "dist/index.js",
@@ -272,7 +272,7 @@ export function loadSidebarSnapshot(sessionId: string, directory: string): Sideb
272
272
  for (const c of compRows) {
273
273
  compartmentTokens += Math.ceil(
274
274
  `<compartment start="${c.start_message}" end="${c.end_message}" title="${c.title}">\n${c.content}\n</compartment>\n`
275
- .length / 4,
275
+ .length / 3.5,
276
276
  );
277
277
  }
278
278
  } catch {
@@ -285,7 +285,7 @@ export function loadSidebarSnapshot(sessionId: string, directory: string): Sideb
285
285
  )
286
286
  .all(sessionId);
287
287
  for (const f of factRows) {
288
- factTokens += Math.ceil(`* ${f.content}\n`.length / 4);
288
+ factTokens += Math.ceil(`* ${f.content}\n`.length / 3.5);
289
289
  }
290
290
  } catch {
291
291
  /* session_facts table may not exist */
@@ -294,7 +294,7 @@ export function loadSidebarSnapshot(sessionId: string, directory: string): Sideb
294
294
  if (meta) {
295
295
  const cached = (meta as Record<string, unknown>).memory_block_cache;
296
296
  if (typeof cached === "string" && cached.length > 0) {
297
- memoryTokens = Math.ceil(cached.length / 4);
297
+ memoryTokens = Math.ceil(cached.length / 3.5);
298
298
  }
299
299
  }
300
300
 
@@ -514,14 +514,14 @@ export function loadStatusDetail(
514
514
 
515
515
  let histTokens = 0;
516
516
  for (const c of compartments) {
517
- // ~4 chars per token estimate (same as plugin's estimateTokens)
517
+ // ~3.5 chars per token estimate (matches plugin's estimateTokens)
518
518
  histTokens += Math.ceil(
519
519
  `<compartment start="${c.start_message}" end="${c.end_message}" title="${c.title}">\n${c.content}\n</compartment>\n`
520
- .length / 4,
520
+ .length / 3.5,
521
521
  );
522
522
  }
523
523
  for (const f of facts) {
524
- histTokens += Math.ceil(`* ${f.content}\n`.length / 4);
524
+ histTokens += Math.ceil(`* ${f.content}\n`.length / 3.5);
525
525
  }
526
526
  detail.historyBlockTokens = histTokens;
527
527