@cortexkit/opencode-magic-context 0.16.2 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/features/magic-context/message-index-async.d.ts +12 -0
- package/dist/features/magic-context/message-index-async.d.ts.map +1 -0
- package/dist/features/magic-context/message-index.d.ts +4 -0
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts +7 -0
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts +2 -2
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +3 -6
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts +163 -1
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +1 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/tagger.d.ts +52 -2
- package/dist/features/magic-context/tagger.d.ts.map +1 -1
- package/dist/features/magic-context/tool-definition-tokens.d.ts +26 -3
- package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
- package/dist/features/magic-context/tool-owner-backfill.d.ts +90 -0
- package/dist/features/magic-context/tool-owner-backfill.d.ts.map +1 -0
- package/dist/features/magic-context/types.d.ts +17 -0
- package/dist/features/magic-context/types.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/cache.d.ts +12 -1
- package/dist/hooks/auto-update-checker/cache.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts +23 -0
- package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-payloads.d.ts +8 -0
- package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts +6 -0
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +16 -0
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/live-session-state.d.ts +13 -0
- package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
- package/dist/hooks/magic-context/nudger.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts +24 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts +1 -0
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-raw.d.ts +1 -0
- package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +9 -6
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts +16 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +0 -11
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +7 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2073 -768
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts +3 -0
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/shared/models-dev-cache.d.ts +3 -10
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/native-binding.d.ts +87 -0
- package/dist/shared/native-binding.d.ts.map +1 -0
- package/dist/shared/sqlite.d.ts +0 -12
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/tag-transcript.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/shared/conflict-detector.ts +1 -1
- package/src/shared/models-dev-cache.test.ts +64 -57
- package/src/shared/models-dev-cache.ts +49 -68
- package/src/shared/native-binding.ts +311 -0
- package/src/shared/sqlite.ts +57 -14
- package/src/shared/tag-transcript.ts +137 -126
- package/src/tui/index.tsx +2 -2
- package/dist/hooks/magic-context/reasoning-capability.d.ts +0 -23
- package/dist/hooks/magic-context/reasoning-capability.d.ts.map +0 -1
|
@@ -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,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,aAAa,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC;IACvC,gBAAgB,EAAE,gBAAgB,CAAC;CACtC;;;;;;qBAqDyvJ,CAAC;;;;;;;;;;;;
|
|
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,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,aAAa,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC;IACvC,gBAAgB,EAAE,gBAAgB,CAAC;CACtC;;;;;;qBAqDyvJ,CAAC;;;;;;;;;;;;qBAA/1D,CAAC;mBAAyB,CAAC;iBAAuB,CAAC;iBAAuB,CAAC;0BAAc,CAAC;uBAAiB,CAAC;;;;;;0BAAulpB,CAAC;;;;;;EAD/lvB"}
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
* and returns typed responses for TUI consumption.
|
|
4
4
|
*/
|
|
5
5
|
import type { MagicContextConfig } from "../config/schema/magic-context";
|
|
6
|
+
import { type ContextDatabase as Database } from "../features/magic-context/storage";
|
|
6
7
|
import type { LiveSessionState } from "../hooks/magic-context/live-session-state";
|
|
7
8
|
import type { MagicContextRpcServer } from "../shared/rpc-server";
|
|
9
|
+
import type { SidebarSnapshot } from "../shared/rpc-types";
|
|
10
|
+
export declare function buildSidebarSnapshot(db: Database, sessionId: string, directory: string, liveSessionState?: LiveSessionState, injectionBudgetTokens?: number): SidebarSnapshot;
|
|
8
11
|
/**
|
|
9
12
|
* Register all RPC handlers on the server.
|
|
10
13
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-handlers.d.ts","sourceRoot":"","sources":["../../src/plugin/rpc-handlers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc-handlers.d.ts","sourceRoot":"","sources":["../../src/plugin/rpc-handlers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,KAAK,eAAe,IAAI,QAAQ,EAAgB,MAAM,mCAAmC,CAAC;AAQnG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AASlF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,qBAAqB,CAAC;AAmDzE,wBAAgB,oBAAoB,CAChC,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,qBAAqB,CAAC,EAAE,MAAM,GAC/B,eAAe,CAySjB;AAoMD;;GAEG;AACH,wBAAgB,mBAAmB,CAC/B,SAAS,EAAE,qBAAqB,EAChC,IAAI,EAAE;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,gBAAgB,CAAC;CACtC,GACF,IAAI,CAoIN"}
|
|
@@ -14,10 +14,9 @@
|
|
|
14
14
|
* Used during cold starts before the API cache warms up and in any
|
|
15
15
|
* code path that cannot reach the SDK client.
|
|
16
16
|
*
|
|
17
|
-
* The public
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* from `src/index.ts` at startup and on a timer.
|
|
17
|
+
* The public getter (`getModelsDevContextLimit()`) is synchronous: it checks
|
|
18
|
+
* the API cache first, then the file cache. The plugin warms and refreshes
|
|
19
|
+
* the API cache from `src/index.ts` at startup and on a timer.
|
|
21
20
|
*/
|
|
22
21
|
interface OpencodeClientLike {
|
|
23
22
|
config: {
|
|
@@ -52,12 +51,6 @@ export declare function refreshModelLimitsFromApi(client: OpencodeClientLike): P
|
|
|
52
51
|
* Returns `undefined` if neither layer knows the model.
|
|
53
52
|
*/
|
|
54
53
|
export declare function getModelsDevContextLimit(providerID: string, modelID: string): number | undefined;
|
|
55
|
-
/**
|
|
56
|
-
* Returns the provider-specific interleaved reasoning field when the model
|
|
57
|
-
* requires one (for example `reasoning_content` for Moonshot/Kimi style
|
|
58
|
-
* providers). Undefined means the cache has no such capability recorded.
|
|
59
|
-
*/
|
|
60
|
-
export declare function getModelsDevInterleavedField(providerID: string, modelID: string): string | undefined;
|
|
61
54
|
/** Clear in-memory caches (for testing). */
|
|
62
55
|
export declare function clearModelsDevCache(): void;
|
|
63
56
|
/** Inspection helpers (for logging / debugging). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models-dev-cache.d.ts","sourceRoot":"","sources":["../../src/shared/models-dev-cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"models-dev-cache.d.ts","sourceRoot":"","sources":["../../src/shared/models-dev-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AASH,UAAU,kBAAkB;IACxB,MAAM,EAAE;QACJ,SAAS,EAAE,MAAM,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE;gBAAE,SAAS,CAAC,EAAE,OAAO,CAAA;aAAE,CAAA;SAAE,CAAC,CAAC;KAChE,CAAC;CACL;AA4MD;;;;;;;;;;GAUG;AACH,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmEzF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAchG;AAED,4CAA4C;AAC5C,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C;AAED,oDAAoD;AACpD,wBAAgB,sBAAsB,IAAI;IACtC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACrB,CAOA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native-binding resolver for Electron-runtime mismatches.
|
|
3
|
+
*
|
|
4
|
+
* Why this module exists
|
|
5
|
+
* ----------------------
|
|
6
|
+
* `better-sqlite3` uses raw v8/nan bindings, so its compiled `.node` file is
|
|
7
|
+
* locked to a specific NODE_MODULE_VERSION (Node-API ABI revision). When
|
|
8
|
+
* OpenCode Desktop (Electron 41 → ABI 145) loads a plugin whose `node_modules`
|
|
9
|
+
* was populated by `bun install` / `npm install` under a different runtime,
|
|
10
|
+
* the fetched prebuild is for `node-vNNN` (e.g. ABI 137 for Node 22) and
|
|
11
|
+
* Electron refuses to load it with:
|
|
12
|
+
*
|
|
13
|
+
* The module '...better_sqlite3.node' was compiled against a different
|
|
14
|
+
* Node.js version using NODE_MODULE_VERSION 137. This version of Node.js
|
|
15
|
+
* requires NODE_MODULE_VERSION 145.
|
|
16
|
+
*
|
|
17
|
+
* `onnxruntime-node` (used by `@huggingface/transformers` for local
|
|
18
|
+
* embeddings) is N-API v3 and is ABI-stable across runtimes, so it does NOT
|
|
19
|
+
* have this problem. Only `better-sqlite3` is affected.
|
|
20
|
+
*
|
|
21
|
+
* What this does
|
|
22
|
+
* --------------
|
|
23
|
+
* On plugin load, before constructing any `Database`:
|
|
24
|
+
*
|
|
25
|
+
* 1. If we are NOT running under Electron (`process.versions.electron` is
|
|
26
|
+
* unset), return null. Bun uses `bun:sqlite` and never reaches this code;
|
|
27
|
+
* Pi/Node-CLI install matching Node-vNNN prebuilds via npm, which work
|
|
28
|
+
* natively against the on-disk binary.
|
|
29
|
+
*
|
|
30
|
+
* 2. If we ARE on Electron, locate the `.node` file path that
|
|
31
|
+
* `better-sqlite3`'s default `bindings()` lookup would try first. Probe
|
|
32
|
+
* its ABI by attempting a sandboxed `process.dlopen`. If it succeeds
|
|
33
|
+
* (Electron-compatible), return null — the default lookup will work.
|
|
34
|
+
*
|
|
35
|
+
* 3. Otherwise, look for a cached Electron prebuild at
|
|
36
|
+
* `<XDG_CACHE_HOME>/cortexkit/native-bindings/better-sqlite3/v<version>/electron-v<abi>-<platform>-<arch>/better_sqlite3.node`.
|
|
37
|
+
* Download it from the `WiseLibs/better-sqlite3` GitHub release if
|
|
38
|
+
* missing, extract with `nanotar` (pure-JS, ~45 KB, zero deps), validate
|
|
39
|
+
* the ABI, and return the absolute path.
|
|
40
|
+
*
|
|
41
|
+
* The caller (`sqlite.ts`) then passes this path through `better-sqlite3`'s
|
|
42
|
+
* documented `nativeBinding` constructor option:
|
|
43
|
+
*
|
|
44
|
+
* new Database(filename, { nativeBinding: <our cached path> })
|
|
45
|
+
*
|
|
46
|
+
* `better-sqlite3` calls `require()` directly on that path, bypassing the
|
|
47
|
+
* normal `bindings()` lookup chain. This is a first-class API the maintainer
|
|
48
|
+
* added for exactly this kind of cross-runtime extension scenario — see
|
|
49
|
+
* `node_modules/better-sqlite3/lib/database.js` `nativeBinding` handling.
|
|
50
|
+
*
|
|
51
|
+
* Why we don't replace the on-disk binary
|
|
52
|
+
* ---------------------------------------
|
|
53
|
+
* An earlier iteration copied the cached Electron binary over the in-tree
|
|
54
|
+
* `node_modules/.../better_sqlite3.node`. That worked but mutates a shared
|
|
55
|
+
* resource: in monorepo dev setups (or any case where multiple runtimes
|
|
56
|
+
* share one `node_modules`), a Pi process opening the plugin from the same
|
|
57
|
+
* workspace would then load the Electron-ABI binary and fail. Returning a
|
|
58
|
+
* separate cached path keeps the on-disk file untouched so each runtime
|
|
59
|
+
* sees the binary it needs.
|
|
60
|
+
*
|
|
61
|
+
* Failure modes
|
|
62
|
+
* -------------
|
|
63
|
+
* If GitHub is unreachable (corporate firewall, offline laptop, rate limit)
|
|
64
|
+
* AND no cached binary exists, this throws. The caller (sqlite.ts →
|
|
65
|
+
* openDatabase) surfaces a `storage unavailable` error and Magic Context
|
|
66
|
+
* disables itself for the run. The user can connect to the network and
|
|
67
|
+
* restart, or wait for a cached binary from a previous successful launch.
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* Resolve the absolute path to a `better-sqlite3` `.node` binary that the
|
|
71
|
+
* current runtime can load.
|
|
72
|
+
*
|
|
73
|
+
* - Returns `null` outside Electron (Bun uses `bun:sqlite`; Pi/Node CLI
|
|
74
|
+
* loads matching prebuilds from `node_modules` natively).
|
|
75
|
+
* - Returns `null` on Electron when the on-disk binary already matches
|
|
76
|
+
* the runtime's ABI (rare but possible — a future OpenCode build that
|
|
77
|
+
* post-install-rebuilds for Electron would hit this fast path).
|
|
78
|
+
* - Returns the cached/downloaded prebuild path on Electron when the
|
|
79
|
+
* on-disk binary's ABI doesn't match.
|
|
80
|
+
*
|
|
81
|
+
* The returned path is suitable as the `nativeBinding` option to
|
|
82
|
+
* `new Database(filename, { nativeBinding })`. better-sqlite3 calls `require()`
|
|
83
|
+
* directly on it, bypassing the default `bindings()` lookup chain — this is
|
|
84
|
+
* a documented public API in better-sqlite3, not an internal hack.
|
|
85
|
+
*/
|
|
86
|
+
export declare function resolveBetterSqliteNativeBinding(): Promise<string | null>;
|
|
87
|
+
//# sourceMappingURL=native-binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-binding.d.ts","sourceRoot":"","sources":["../../src/shared/native-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AA2IH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gCAAgC,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuF/E"}
|
package/dist/shared/sqlite.d.ts
CHANGED
|
@@ -25,18 +25,6 @@
|
|
|
25
25
|
* is either rewritten to common-subset patterns or hidden behind the helpers
|
|
26
26
|
* in `./sqlite-helpers.ts`.
|
|
27
27
|
*/
|
|
28
|
-
/**
|
|
29
|
-
* Database constructor compatible with both bun:sqlite and better-sqlite3.
|
|
30
|
-
*
|
|
31
|
-
* The TypeScript type intentionally references @types/better-sqlite3 because
|
|
32
|
-
* its definitions are richer than @types/bun's bun:sqlite types and bun:sqlite
|
|
33
|
-
* is a structural superset for the API surface we use. Calls written against
|
|
34
|
-
* this type work correctly under both runtimes at runtime.
|
|
35
|
-
*
|
|
36
|
-
* @types/better-sqlite3 uses `export = Database` (CommonJS interop), which
|
|
37
|
-
* surfaces in TypeScript as `import Database = require("better-sqlite3")`.
|
|
38
|
-
* We capture the DatabaseConstructor type from the namespace re-export.
|
|
39
|
-
*/
|
|
40
28
|
import type BetterSqlite3 from "better-sqlite3";
|
|
41
29
|
export declare const Database: typeof BetterSqlite3;
|
|
42
30
|
/** Instance type alias used by helpers and storage modules. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/shared/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/shared/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAC;AAsFhD,eAAO,MAAM,QAAQ,EAAE,OAAO,aAA4B,CAAC;AAE3D,+DAA+D;AAC/D,MAAM,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC;AAE9C;;;;;;;;;GASG;AACH,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tag-transcript.d.ts","sourceRoot":"","sources":["../../src/shared/tag-transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAGzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AAM/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAkB,MAAM,cAAc,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACjC;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACnC;AAyDD,wBAAgB,aAAa,CACzB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,eAAe,EACnB,OAAO,GAAE,oBAAyB,GACnC,mBAAmB,
|
|
1
|
+
{"version":3,"file":"tag-transcript.d.ts","sourceRoot":"","sources":["../../src/shared/tag-transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAGzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AAM/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAkB,MAAM,cAAc,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACjC;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACnC;AAyDD,wBAAgB,aAAa,CACzB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,eAAe,EACnB,OAAO,GAAE,oBAAyB,GACnC,mBAAmB,CAoKrB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-magic-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenCode plugin for Magic Context — cross-session memory and context management",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"ai-tokenizer": "^1.0.6",
|
|
47
47
|
"better-sqlite3": "^12.9.0",
|
|
48
48
|
"comment-json": "^4.2.5",
|
|
49
|
+
"nanotar": "^0.3.0",
|
|
49
50
|
"zod": "^4.1.8"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
@@ -365,7 +365,7 @@ export function formatConflictShort(result: ConflictResult): string {
|
|
|
365
365
|
"",
|
|
366
366
|
...result.reasons.map((r) => `• ${r}`),
|
|
367
367
|
"",
|
|
368
|
-
"Fix: run `
|
|
368
|
+
"Fix: run `npx @cortexkit/opencode-magic-context@latest doctor`",
|
|
369
369
|
];
|
|
370
370
|
return lines.join("\n");
|
|
371
371
|
}
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
clearModelsDevCache,
|
|
7
7
|
getModelsDevCacheState,
|
|
8
8
|
getModelsDevContextLimit,
|
|
9
|
-
getModelsDevInterleavedField,
|
|
10
9
|
refreshModelLimitsFromApi,
|
|
11
10
|
} from "./models-dev-cache";
|
|
12
11
|
|
|
@@ -276,62 +275,6 @@ describe("models-dev-cache", () => {
|
|
|
276
275
|
expect(state.apiCount).toBe(1);
|
|
277
276
|
});
|
|
278
277
|
|
|
279
|
-
test("reads interleaved reasoning field metadata from models.json", () => {
|
|
280
|
-
const opencodeDir = join(tempDir, "opencode");
|
|
281
|
-
mkdirSync(opencodeDir, { recursive: true });
|
|
282
|
-
writeFileSync(
|
|
283
|
-
join(opencodeDir, "models.json"),
|
|
284
|
-
JSON.stringify({
|
|
285
|
-
"opencode-go": {
|
|
286
|
-
models: {
|
|
287
|
-
"kimi-k2.6": {
|
|
288
|
-
limit: { context: 262144 },
|
|
289
|
-
interleaved: { field: "reasoning_content" },
|
|
290
|
-
},
|
|
291
|
-
"plain-model": {
|
|
292
|
-
limit: { context: 262144 },
|
|
293
|
-
},
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
}),
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
expect(getModelsDevInterleavedField("opencode-go", "kimi-k2.6")).toBe("reasoning_content");
|
|
300
|
-
expect(getModelsDevInterleavedField("opencode-go", "plain-model")).toBeUndefined();
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
test("API cache exposes interleaved reasoning metadata", async () => {
|
|
304
|
-
const mockClient = {
|
|
305
|
-
config: {
|
|
306
|
-
providers: async () => ({
|
|
307
|
-
data: {
|
|
308
|
-
providers: [
|
|
309
|
-
{
|
|
310
|
-
id: "opencode-go",
|
|
311
|
-
models: {
|
|
312
|
-
"kimi-k2.6": {
|
|
313
|
-
limit: { context: 262144 },
|
|
314
|
-
capabilities: {
|
|
315
|
-
interleaved: { field: "reasoning_content" },
|
|
316
|
-
},
|
|
317
|
-
},
|
|
318
|
-
"plain-model": {
|
|
319
|
-
limit: { context: 262144 },
|
|
320
|
-
capabilities: { interleaved: false },
|
|
321
|
-
},
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
],
|
|
325
|
-
},
|
|
326
|
-
}),
|
|
327
|
-
},
|
|
328
|
-
};
|
|
329
|
-
await refreshModelLimitsFromApi(mockClient);
|
|
330
|
-
|
|
331
|
-
expect(getModelsDevInterleavedField("opencode-go", "kimi-k2.6")).toBe("reasoning_content");
|
|
332
|
-
expect(getModelsDevInterleavedField("opencode-go", "plain-model")).toBeUndefined();
|
|
333
|
-
});
|
|
334
|
-
|
|
335
278
|
test("refreshModelLimitsFromApi tolerates empty/malformed responses", async () => {
|
|
336
279
|
// Undefined data.
|
|
337
280
|
await refreshModelLimitsFromApi({
|
|
@@ -356,6 +299,70 @@ describe("models-dev-cache", () => {
|
|
|
356
299
|
expect(getModelsDevCacheState().apiLoaded).toBe(false);
|
|
357
300
|
});
|
|
358
301
|
|
|
302
|
+
test("suppresses repeated logs when API count oscillates between known sizes", async () => {
|
|
303
|
+
// Simulates github-copilot's /models endpoint returning different model sets
|
|
304
|
+
// between calls. We want first sighting of each new size to log, but once a
|
|
305
|
+
// size has been seen before, further flips between known sizes should be
|
|
306
|
+
// silent (with one "oscillating" notice).
|
|
307
|
+
const sizeA = {
|
|
308
|
+
data: {
|
|
309
|
+
providers: [
|
|
310
|
+
{
|
|
311
|
+
id: "p",
|
|
312
|
+
models: {
|
|
313
|
+
m1: { limit: { context: 100 } },
|
|
314
|
+
m2: { limit: { context: 100 } },
|
|
315
|
+
m3: { limit: { context: 100 } },
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
const sizeB = {
|
|
322
|
+
data: {
|
|
323
|
+
providers: [
|
|
324
|
+
{
|
|
325
|
+
id: "p",
|
|
326
|
+
models: {
|
|
327
|
+
m1: { limit: { context: 100 } },
|
|
328
|
+
m2: { limit: { context: 100 } },
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const clientA = { config: { providers: async () => sizeA } };
|
|
336
|
+
const clientB = { config: { providers: async () => sizeB } };
|
|
337
|
+
|
|
338
|
+
// First sighting of size 3 → logs "loaded 3 entries".
|
|
339
|
+
await refreshModelLimitsFromApi(clientA);
|
|
340
|
+
expect(getModelsDevCacheState().apiCount).toBe(3);
|
|
341
|
+
|
|
342
|
+
// First sighting of size 2 → logs "loaded 2 entries (was 3)".
|
|
343
|
+
await refreshModelLimitsFromApi(clientB);
|
|
344
|
+
expect(getModelsDevCacheState().apiCount).toBe(2);
|
|
345
|
+
|
|
346
|
+
// Second sighting of size 3 → logs the "oscillating" notice once.
|
|
347
|
+
await refreshModelLimitsFromApi(clientA);
|
|
348
|
+
expect(getModelsDevCacheState().apiCount).toBe(3);
|
|
349
|
+
|
|
350
|
+
// Second sighting of size 2 → silent (no new log expected).
|
|
351
|
+
await refreshModelLimitsFromApi(clientB);
|
|
352
|
+
expect(getModelsDevCacheState().apiCount).toBe(2);
|
|
353
|
+
|
|
354
|
+
// Third sighting of size 3 → still silent.
|
|
355
|
+
await refreshModelLimitsFromApi(clientA);
|
|
356
|
+
expect(getModelsDevCacheState().apiCount).toBe(3);
|
|
357
|
+
|
|
358
|
+
// The cache itself still updates on every call (model contents are correct
|
|
359
|
+
// for whichever provider response just arrived). The suppression is purely
|
|
360
|
+
// a logging concern. Last call was clientA → all three models present.
|
|
361
|
+
expect(getModelsDevContextLimit("p", "m1")).toBe(100);
|
|
362
|
+
expect(getModelsDevContextLimit("p", "m2")).toBe(100);
|
|
363
|
+
expect(getModelsDevContextLimit("p", "m3")).toBe(100);
|
|
364
|
+
});
|
|
365
|
+
|
|
359
366
|
test("falls back to file layer when API provider/model key is missing", async () => {
|
|
360
367
|
const opencodeDir = join(tempDir, "opencode");
|
|
361
368
|
mkdirSync(opencodeDir, { recursive: true });
|
|
@@ -14,10 +14,9 @@
|
|
|
14
14
|
* Used during cold starts before the API cache warms up and in any
|
|
15
15
|
* code path that cannot reach the SDK client.
|
|
16
16
|
*
|
|
17
|
-
* The public
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* from `src/index.ts` at startup and on a timer.
|
|
17
|
+
* The public getter (`getModelsDevContextLimit()`) is synchronous: it checks
|
|
18
|
+
* the API cache first, then the file cache. The plugin warms and refreshes
|
|
19
|
+
* the API cache from `src/index.ts` at startup and on a timer.
|
|
21
20
|
*/
|
|
22
21
|
|
|
23
22
|
import { createHash } from "node:crypto";
|
|
@@ -33,18 +32,30 @@ interface OpencodeClientLike {
|
|
|
33
32
|
};
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
// File-cache fallback only. The primary `models.json` API refresh is driven
|
|
36
|
+
// by `setInterval(refreshModelLimitsFromApi, ...)` in `index.ts` at a 1-hour
|
|
37
|
+
// cadence; this 5-minute interval governs the on-disk-cache fallback path
|
|
38
|
+
// when the API loader hasn't run yet (e.g. during plugin warmup).
|
|
39
|
+
const RELOAD_INTERVAL_MS = 5 * 60 * 1000;
|
|
37
40
|
|
|
38
41
|
interface CachedModelMetadata {
|
|
39
42
|
limit?: number;
|
|
40
|
-
interleavedField?: string;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
type InterleavedConfig = boolean | { field?: string } | undefined;
|
|
44
|
-
|
|
45
45
|
/** Populated async from OpenCode SDK. Primary source of truth when available. */
|
|
46
46
|
let apiCache: Map<string, CachedModelMetadata> | null = null;
|
|
47
47
|
let apiLoadedAt = 0;
|
|
48
|
+
/**
|
|
49
|
+
* Recently-seen API cache sizes, used to detect oscillation between two
|
|
50
|
+
* stable values (typically caused by upstream provider plugins like
|
|
51
|
+
* github-copilot whose `/models` endpoint returns slightly different model
|
|
52
|
+
* sets between calls based on `model_picker_enabled` toggles). Once the
|
|
53
|
+
* same size has been observed before, we stop logging count changes —
|
|
54
|
+
* the count is a function of upstream behavior we can't control, and
|
|
55
|
+
* repeated logs only add noise.
|
|
56
|
+
*/
|
|
57
|
+
const recentlySeenApiSizes = new Set<number>();
|
|
58
|
+
let oscillationLogged = false;
|
|
48
59
|
|
|
49
60
|
/** Populated sync from disk as fallback. */
|
|
50
61
|
let fileCache: Map<string, CachedModelMetadata> | null = null;
|
|
@@ -110,47 +121,28 @@ function resolveLimit(limit: { context?: number; input?: number } | undefined):
|
|
|
110
121
|
return undefined;
|
|
111
122
|
}
|
|
112
123
|
|
|
113
|
-
function resolveInterleavedField(interleaved: InterleavedConfig): string | undefined {
|
|
114
|
-
if (
|
|
115
|
-
interleaved &&
|
|
116
|
-
typeof interleaved === "object" &&
|
|
117
|
-
typeof interleaved.field === "string" &&
|
|
118
|
-
interleaved.field.length > 0
|
|
119
|
-
) {
|
|
120
|
-
return interleaved.field;
|
|
121
|
-
}
|
|
122
|
-
return undefined;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
124
|
function setCachedModelMetadata(
|
|
126
125
|
cache: Map<string, CachedModelMetadata>,
|
|
127
126
|
key: string,
|
|
128
127
|
model:
|
|
129
128
|
| {
|
|
130
129
|
limit?: { context?: number; input?: number };
|
|
131
|
-
capabilities?: { interleaved?: InterleavedConfig };
|
|
132
|
-
interleaved?: InterleavedConfig;
|
|
133
130
|
experimental?: { modes?: Record<string, unknown> };
|
|
134
131
|
}
|
|
135
132
|
| undefined,
|
|
136
133
|
): void {
|
|
137
134
|
const limit = resolveLimit(model?.limit);
|
|
138
|
-
const interleavedField =
|
|
139
|
-
resolveInterleavedField(model?.capabilities?.interleaved) ??
|
|
140
|
-
resolveInterleavedField(model?.interleaved);
|
|
141
135
|
|
|
142
|
-
if (limit === undefined
|
|
136
|
+
if (limit === undefined) {
|
|
143
137
|
return;
|
|
144
138
|
}
|
|
145
139
|
|
|
146
|
-
const value: CachedModelMetadata = {};
|
|
147
|
-
if (limit !== undefined) value.limit = limit;
|
|
148
|
-
if (interleavedField !== undefined) value.interleavedField = interleavedField;
|
|
140
|
+
const value: CachedModelMetadata = { limit };
|
|
149
141
|
cache.set(key, value);
|
|
150
142
|
|
|
151
143
|
// OpenCode creates derived model IDs from experimental.modes
|
|
152
144
|
// e.g. gpt-5.4 + modes.fast → gpt-5.4-fast. These inherit the same
|
|
153
|
-
// context limit
|
|
145
|
+
// context limit as the parent model.
|
|
154
146
|
const modes = model?.experimental?.modes;
|
|
155
147
|
if (modes && typeof modes === "object") {
|
|
156
148
|
for (const mode of Object.keys(modes)) {
|
|
@@ -176,8 +168,6 @@ function loadModelsDevMetadataFromFile(): Map<string, CachedModelMetadata> {
|
|
|
176
168
|
string,
|
|
177
169
|
{
|
|
178
170
|
limit?: { context?: number; input?: number };
|
|
179
|
-
capabilities?: { interleaved?: InterleavedConfig };
|
|
180
|
-
interleaved?: InterleavedConfig;
|
|
181
171
|
experimental?: { modes?: Record<string, unknown> };
|
|
182
172
|
}
|
|
183
173
|
>;
|
|
@@ -273,8 +263,6 @@ export async function refreshModelLimitsFromApi(client: OpencodeClientLike): Pro
|
|
|
273
263
|
string,
|
|
274
264
|
{
|
|
275
265
|
limit?: { context?: number; input?: number };
|
|
276
|
-
capabilities?: { interleaved?: InterleavedConfig };
|
|
277
|
-
interleaved?: InterleavedConfig;
|
|
278
266
|
experimental?: { modes?: Record<string, unknown> };
|
|
279
267
|
}
|
|
280
268
|
>;
|
|
@@ -288,15 +276,34 @@ export async function refreshModelLimitsFromApi(client: OpencodeClientLike): Pro
|
|
|
288
276
|
const previousSize = apiCache?.size ?? null;
|
|
289
277
|
apiCache = map;
|
|
290
278
|
apiLoadedAt = Date.now();
|
|
291
|
-
|
|
292
|
-
//
|
|
293
|
-
|
|
279
|
+
|
|
280
|
+
// Log policy:
|
|
281
|
+
// - Always log the first successful load.
|
|
282
|
+
// - Log a count change once per new size we haven't seen before.
|
|
283
|
+
// - When the count returns to a previously-seen size, log an
|
|
284
|
+
// "oscillation" message exactly once explaining the cause, then
|
|
285
|
+
// stay silent on further flips between known sizes.
|
|
286
|
+
if (previousSize === null) {
|
|
287
|
+
recentlySeenApiSizes.add(map.size);
|
|
294
288
|
sessionLog(
|
|
295
289
|
"global",
|
|
296
|
-
`models-dev-cache: API layer loaded ${map.size} model metadata entries
|
|
297
|
-
previousSize !== null ? ` (was ${previousSize})` : ""
|
|
298
|
-
}`,
|
|
290
|
+
`models-dev-cache: API layer loaded ${map.size} model metadata entries`,
|
|
299
291
|
);
|
|
292
|
+
} else if (previousSize !== map.size) {
|
|
293
|
+
const sizeAlreadySeen = recentlySeenApiSizes.has(map.size);
|
|
294
|
+
recentlySeenApiSizes.add(map.size);
|
|
295
|
+
if (!sizeAlreadySeen) {
|
|
296
|
+
sessionLog(
|
|
297
|
+
"global",
|
|
298
|
+
`models-dev-cache: API layer loaded ${map.size} model metadata entries (was ${previousSize})`,
|
|
299
|
+
);
|
|
300
|
+
} else if (!oscillationLogged) {
|
|
301
|
+
oscillationLogged = true;
|
|
302
|
+
sessionLog(
|
|
303
|
+
"global",
|
|
304
|
+
`models-dev-cache: API count oscillating between ${[...recentlySeenApiSizes].sort((a, b) => a - b).join(" ↔ ")} — likely upstream provider plugin returning slightly different model sets between calls (e.g. github-copilot's /models endpoint toggling model_picker_enabled). Suppressing further size-change logs.`,
|
|
305
|
+
);
|
|
306
|
+
}
|
|
300
307
|
}
|
|
301
308
|
} catch (error) {
|
|
302
309
|
sessionLog(
|
|
@@ -334,38 +341,12 @@ export function getModelsDevContextLimit(providerID: string, modelID: string): n
|
|
|
334
341
|
return fileCache.get(key)?.limit;
|
|
335
342
|
}
|
|
336
343
|
|
|
337
|
-
/**
|
|
338
|
-
* Returns the provider-specific interleaved reasoning field when the model
|
|
339
|
-
* requires one (for example `reasoning_content` for Moonshot/Kimi style
|
|
340
|
-
* providers). Undefined means the cache has no such capability recorded.
|
|
341
|
-
*/
|
|
342
|
-
export function getModelsDevInterleavedField(
|
|
343
|
-
providerID: string,
|
|
344
|
-
modelID: string,
|
|
345
|
-
): string | undefined {
|
|
346
|
-
const key = `${providerID}/${modelID}`;
|
|
347
|
-
|
|
348
|
-
if (apiCache) {
|
|
349
|
-
const fromApi = apiCache.get(key)?.interleavedField;
|
|
350
|
-
if (typeof fromApi === "string" && fromApi.length > 0) {
|
|
351
|
-
return fromApi;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const now = Date.now();
|
|
356
|
-
if (!fileCache || now - fileLastAttempt > RELOAD_INTERVAL_MS) {
|
|
357
|
-
fileLastAttempt = now;
|
|
358
|
-
fileCache = loadModelsDevMetadataFromFile();
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
const fromFile = fileCache.get(key)?.interleavedField;
|
|
362
|
-
return typeof fromFile === "string" && fromFile.length > 0 ? fromFile : undefined;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
344
|
/** Clear in-memory caches (for testing). */
|
|
366
345
|
export function clearModelsDevCache(): void {
|
|
367
346
|
apiCache = null;
|
|
368
347
|
apiLoadedAt = 0;
|
|
348
|
+
recentlySeenApiSizes.clear();
|
|
349
|
+
oscillationLogged = false;
|
|
369
350
|
fileCache = null;
|
|
370
351
|
fileLastAttempt = 0;
|
|
371
352
|
}
|