@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.
- package/README.md +2 -0
- package/dist/config/schema/magic-context.d.ts +17 -0
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/runner.d.ts +9 -1
- package/dist/features/magic-context/dreamer/runner.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/key-files/identify-key-files.d.ts +39 -0
- package/dist/features/magic-context/key-files/identify-key-files.d.ts.map +1 -0
- package/dist/features/magic-context/key-files/read-stats.d.ts +20 -0
- package/dist/features/magic-context/key-files/read-stats.d.ts.map +1 -0
- package/dist/features/magic-context/key-files/storage-key-files.d.ts +25 -0
- package/dist/features/magic-context/key-files/storage-key-files.d.ts.map +1 -0
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/command-handler.d.ts +5 -0
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +5 -0
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/system-prompt-hash.d.ts +4 -0
- package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +517 -43
- package/dist/plugin/dream-timer.d.ts +5 -0
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/tools/ctx-note/tools.d.ts.map +1 -1
- package/package.json +1 -1
- 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":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,eAAO,MAAM,6BAA6B,QAAS,CAAC;AACpD,eAAO,MAAM,oCAAoC,KAAK,CAAC;AACvD,eAAO,MAAM,gCAAgC,QAAS,CAAC;AACvD,eAAO,MAAM,4BAA4B,SAAU,CAAC;AACpD,eAAO,MAAM,iCAAiC,OAAO,CAAC;AACtD,eAAO,MAAM,6BAA6B,4BAA4B,CAAC;AAEvE,eAAO,MAAM,aAAa,iFAMhB,CAAC;AAEX,eAAO,MAAM,kBAAkB;;;;;;EAAwB,CAAC;AACxD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,qBAAqB,EAAE,YAAY,EAK/C,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAe/B,CAAC;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAIpB,CAAC;AACd,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC;AA2B/E,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;GAmBhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB;;oFAEgF;IAChF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;IACtD,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACpE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,4BAA4B,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvF,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,yBAAyB,EAAE,MAAM,CAAC;IAClC,wBAAwB,EAAE,MAAM,CAAC;IACjC,yBAAyB,EAAE,MAAM,CAAC;IAClC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE;QACpB,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,YAAY,EAAE;QACV,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE;YACX,OAAO,EAAE,OAAO,CAAC;YACjB,mBAAmB,EAAE,MAAM,CAAC;SAC/B,CAAC;KACL,CAAC;IACF,SAAS,EAAE,eAAe,CAAC;IAC3B,MAAM,EAAE;QACJ,OAAO,EAAE,OAAO,CAAC;QACjB,uBAAuB,EAAE,MAAM,CAAC;QAChC,YAAY,EAAE,OAAO,CAAC;QACtB,mCAAmC,EAAE,MAAM,CAAC;KAC/C,CAAC;IACF,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,eAAO,MAAM,wBAAwB
|
|
1
|
+
{"version":3,"file":"magic-context.d.ts","sourceRoot":"","sources":["../../../src/config/schema/magic-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,eAAO,MAAM,6BAA6B,QAAS,CAAC;AACpD,eAAO,MAAM,oCAAoC,KAAK,CAAC;AACvD,eAAO,MAAM,gCAAgC,QAAS,CAAC;AACvD,eAAO,MAAM,4BAA4B,SAAU,CAAC;AACpD,eAAO,MAAM,iCAAiC,OAAO,CAAC;AACtD,eAAO,MAAM,6BAA6B,4BAA4B,CAAC;AAEvE,eAAO,MAAM,aAAa,iFAMhB,CAAC;AAEX,eAAO,MAAM,kBAAkB;;;;;;EAAwB,CAAC;AACxD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,qBAAqB,EAAE,YAAY,EAK/C,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAe/B,CAAC;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAIpB,CAAC;AACd,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC;AA2B/E,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;GAmBhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB;;oFAEgF;IAChF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;IACtD,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACpE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,4BAA4B,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvF,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,yBAAyB,EAAE,MAAM,CAAC;IAClC,wBAAwB,EAAE,MAAM,CAAC;IACjC,yBAAyB,EAAE,MAAM,CAAC;IAClC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE;QACpB,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,YAAY,EAAE;QACV,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE;YACX,OAAO,EAAE,OAAO,CAAC;YACjB,mBAAmB,EAAE,MAAM,CAAC;SAC/B,CAAC;QACF,aAAa,EAAE;YACX,OAAO,EAAE,OAAO,CAAC;YACjB,mEAAmE;YACnE,YAAY,EAAE,MAAM,CAAC;YACrB,mFAAmF;YACnF,SAAS,EAAE,MAAM,CAAC;SACrB,CAAC;KACL,CAAC;IACF,SAAS,EAAE,eAAe,CAAC;IAC3B,MAAM,EAAE;QACJ,OAAO,EAAE,OAAO,CAAC;QACjB,uBAAuB,EAAE,MAAM,CAAC;QAChC,YAAY,EAAE,OAAO,CAAC;QACtB,mCAAmC,EAAE,MAAM,CAAC;KAC/C,CAAC;IACF,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyH/B,CAAC"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import
|
|
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,
|
|
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;
|
|
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,
|
|
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;
|
|
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"}
|
|
@@ -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;
|
|
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":"
|
|
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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
|
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(
|
|
24483
|
-
structure: existsSync4(
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
25820
|
+
const dbPath = getOpenCodeDbPath2();
|
|
25438
25821
|
if (cachedReadOnlyDb?.path === dbPath) {
|
|
25439
25822
|
return cachedReadOnlyDb.db;
|
|
25440
25823
|
}
|
|
25441
25824
|
closeCachedReadOnlyDb();
|
|
25442
|
-
const db = new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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}
|
|
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}
|
|
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;
|
|
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;;;;;;
|
|
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;
|
|
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
|
@@ -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 /
|
|
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 /
|
|
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 /
|
|
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
|
-
// ~
|
|
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 /
|
|
520
|
+
.length / 3.5,
|
|
521
521
|
);
|
|
522
522
|
}
|
|
523
523
|
for (const f of facts) {
|
|
524
|
-
histTokens += Math.ceil(`* ${f.content}\n`.length /
|
|
524
|
+
histTokens += Math.ceil(`* ${f.content}\n`.length / 3.5);
|
|
525
525
|
}
|
|
526
526
|
detail.historyBlockTokens = histTokens;
|
|
527
527
|
|