@cortexkit/opencode-magic-context 0.36.0 → 0.37.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/dist/config/index.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +2 -1
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +10 -0
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-dw9fs934.js → conflict-warning-hook-an4fxhwf.js} +4 -4
- package/dist/{derive-budgets-88mxk2w1.js → derive-budgets-0f8xpyw2.js} +3 -2
- package/dist/features/magic-context/context-authority.d.ts +12 -0
- package/dist/features/magic-context/context-authority.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts +2 -0
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts +2 -0
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/lease.d.ts +10 -3
- package/dist/features/magic-context/dreamer/lease.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts +2 -0
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/module-apply.d.ts +5 -0
- package/dist/features/magic-context/dreamer/module-apply.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/promote-primers.d.ts +2 -0
- package/dist/features/magic-context/dreamer/promote-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts +2 -0
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts +5 -2
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts +2 -0
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-openai.d.ts +3 -1
- package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
- package/dist/features/magic-context/memory/project-identity.d.ts +6 -0
- package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
- package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/mural/compress-cues.d.ts +2 -0
- package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/schema-fence-probe.d.ts +1 -0
- package/dist/features/magic-context/schema-fence-probe.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/condition-compiler.d.ts +1 -0
- package/dist/features/magic-context/smart-notes/condition-compiler.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/wake-plane.d.ts +21 -0
- package/dist/features/magic-context/smart-notes/wake-plane.d.ts.map +1 -0
- package/dist/features/magic-context/storage-clone.d.ts +6 -0
- package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +14 -3
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-identity-merge.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +22 -0
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
- package/dist/features/magic-context/storage-primers.d.ts +11 -0
- package/dist/features/magic-context/storage-primers.d.ts.map +1 -1
- package/dist/features/magic-context/storage-schema-helpers.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts +2 -0
- 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/user-memory/review-user-memories.d.ts +2 -0
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts +7 -0
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
- package/dist/features/magic-context/window-report-ledger.d.ts +70 -0
- package/dist/features/magic-context/window-report-ledger.d.ts.map +1 -0
- package/dist/hooks/magic-context/child-session-spawn.d.ts +1 -0
- package/dist/hooks/magic-context/child-session-spawn.d.ts.map +1 -1
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-off-transition.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-render-epoch.d.ts +5 -3
- package/dist/hooks/magic-context/compartment-render-epoch.d.ts.map +1 -1
- package/dist/hooks/magic-context/embed-session-state.d.ts +1 -1
- package/dist/hooks/magic-context/embed-session-state.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts +4 -0
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/execute-status.d.ts +2 -1
- package/dist/hooks/magic-context/execute-status.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 +8 -0
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-replay.d.ts +5 -2
- package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-slot.d.ts +20 -0
- package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-state-sync.d.ts +9 -0
- package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-transport.d.ts +20 -3
- package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-wire.d.ts +6 -1
- package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-formatting.d.ts +1 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +20 -0
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +31 -1
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +12 -3
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +10 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
- package/dist/{index-3nfgbsp4.js → index-0ze9p983.js} +897 -114
- package/dist/{index-nsx2n9eg.js → index-4cz5nfxf.js} +3 -3
- package/dist/{index-0c9dmw9m.js → index-8mtsfhyg.js} +33 -3
- package/dist/{index-f1tnb3vm.js → index-8pqfcghm.js} +21 -9
- package/dist/{index-37rt2bka.js → index-cae89eas.js} +26 -7
- package/dist/index-cgyfn1s2.js +53 -0
- package/dist/{index-z8v31d85.js → index-crke4z5j.js} +144 -14
- package/dist/{index-tsgs7td8.js → index-pck9vadr.js} +1 -1
- package/dist/index-q0t189ay.js +741 -0
- package/dist/{index-3tnkymva.js → index-sw8gnq34.js} +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1952 -795
- package/dist/plugin/hooks/create-session-hooks.d.ts +3 -0
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/rust-tool-backends.d.ts +4 -0
- package/dist/plugin/rust-tool-backends.d.ts.map +1 -1
- package/dist/{recomp-orchestrator-7wbbvf87.js → recomp-orchestrator-c3ff16k7.js} +8 -7
- package/dist/{safe-notification-target-c11fdda2.js → safe-notification-target-b693f9x3.js} +2 -2
- package/dist/{send-session-notification-bgtf4853.js → send-session-notification-dxske5zm.js} +3 -3
- package/dist/shared/conflict-detector.d.ts +63 -0
- package/dist/shared/conflict-detector.d.ts.map +1 -1
- package/dist/shared/conflict-fixer.d.ts.map +1 -1
- package/dist/shared/harness-provider-map.d.ts +8 -0
- package/dist/shared/harness-provider-map.d.ts.map +1 -1
- package/dist/shared/jsonc-edit.d.ts +18 -0
- package/dist/shared/jsonc-edit.d.ts.map +1 -0
- package/dist/shared/jsonc-parser.d.ts +11 -1
- package/dist/shared/jsonc-parser.d.ts.map +1 -1
- package/dist/shared/logger.d.ts +8 -0
- package/dist/shared/logger.d.ts.map +1 -1
- package/dist/shared/models-dev-cache.d.ts +5 -0
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/prompt-surface-runtime.d.ts.map +1 -1
- package/dist/shared/prompt-surface.d.ts +7 -3
- package/dist/shared/prompt-surface.d.ts.map +1 -1
- package/dist/shared/rpc-client.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +14 -0
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/rpc-utils.d.ts +32 -6
- package/dist/shared/rpc-utils.d.ts.map +1 -1
- package/dist/shared/tui-preferences.d.ts +9 -0
- package/dist/shared/tui-preferences.d.ts.map +1 -1
- package/dist/shared/window-geometry.d.ts +89 -0
- package/dist/shared/window-geometry.d.ts.map +1 -0
- package/dist/{storage-meta-session-kh5egr11.js → storage-meta-session-mk4gsnke.js} +4 -3
- package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
- package/dist/tools/ctx-note/tools.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/dist/tui-compiled/data/context-db.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/config/index.test.ts +23 -0
- package/src/config/index.ts +43 -9
- package/src/config/latch-permanence-guard.test.ts +218 -0
- package/src/config/project-security.ts +10 -1
- package/src/config/schema/magic-context.test.ts +2 -1
- package/src/config/schema/magic-context.ts +13 -3
- package/src/shared/__fixtures__/window-overlay.v1.json +337 -0
- package/src/shared/conflict-detector.test.ts +176 -1
- package/src/shared/conflict-detector.ts +126 -3
- package/src/shared/conflict-fixer.test.ts +174 -0
- package/src/shared/conflict-fixer.ts +66 -70
- package/src/shared/harness-provider-map.test.ts +20 -0
- package/src/shared/harness-provider-map.ts +19 -0
- package/src/shared/jsonc-edit.ts +295 -0
- package/src/shared/jsonc-parser.test.ts +33 -0
- package/src/shared/jsonc-parser.ts +54 -2
- package/src/shared/logger.test.ts +119 -0
- package/src/shared/logger.ts +33 -15
- package/src/shared/models-dev-cache.test.ts +17 -0
- package/src/shared/models-dev-cache.ts +58 -6
- package/src/shared/prompt-surface-runtime.ts +5 -3
- package/src/shared/prompt-surface.test.ts +91 -2
- package/src/shared/prompt-surface.ts +35 -11
- package/src/shared/rpc-client.ts +4 -2
- package/src/shared/rpc-types.ts +14 -0
- package/src/shared/rpc-utils.test.ts +173 -13
- package/src/shared/rpc-utils.ts +202 -18
- package/src/shared/tui-preferences.test.ts +52 -2
- package/src/shared/tui-preferences.ts +42 -17
- package/src/shared/window-geometry.test.ts +309 -0
- package/src/shared/window-geometry.ts +629 -0
- package/src/tui/data/context-db.ts +5 -0
- package/src/tui/index.tsx +22 -0
- package/src/tui-compiled/data/context-db.ts +5 -0
- package/src/tui-compiled/index.tsx +319 -255
- package/dist/index-wgvxkqpx.js +0 -342
|
@@ -30,9 +30,15 @@ import { join } from "node:path";
|
|
|
30
30
|
import type { ContextLimitProvenance } from "./context-limit-provenance";
|
|
31
31
|
import { getMagicContextStorageDir } from "./data-path";
|
|
32
32
|
import { getHarness } from "./harness";
|
|
33
|
-
import {
|
|
33
|
+
import { modelRefLookupOrder } from "./harness-provider-map";
|
|
34
34
|
import { sessionLog } from "./logger";
|
|
35
35
|
import { shouldEnforcePrivateStoragePermissions } from "./storage-permissions";
|
|
36
|
+
import {
|
|
37
|
+
deriveWindowGeometry,
|
|
38
|
+
getWindowOverlay,
|
|
39
|
+
resolveWindowOverlayFacts,
|
|
40
|
+
type WindowGeometryResult,
|
|
41
|
+
} from "./window-geometry";
|
|
36
42
|
|
|
37
43
|
interface OpencodeClientLike {
|
|
38
44
|
config: {
|
|
@@ -196,14 +202,11 @@ function isFinitePositive(value: number | undefined): value is number {
|
|
|
196
202
|
}
|
|
197
203
|
|
|
198
204
|
function modelKeyLookupOrder(providerID: string, modelID: string): string[] {
|
|
199
|
-
const
|
|
200
|
-
const canonicalFull = piModelRefToCanonical(full);
|
|
201
|
-
const candidates = [full, canonicalFull, modelID];
|
|
205
|
+
const candidates = [...modelRefLookupOrder(`${providerID}/${modelID}`), modelID];
|
|
202
206
|
const colon = modelID.lastIndexOf(":");
|
|
203
207
|
if (colon > 0) {
|
|
204
208
|
const bareModel = modelID.slice(0, colon);
|
|
205
|
-
|
|
206
|
-
candidates.push(providerBare, piModelRefToCanonical(providerBare), bareModel);
|
|
209
|
+
candidates.push(...modelRefLookupOrder(`${providerID}/${bareModel}`), bareModel);
|
|
207
210
|
}
|
|
208
211
|
return [...new Set(candidates)];
|
|
209
212
|
}
|
|
@@ -493,6 +496,50 @@ async function refreshModelLimitsOnce(client: OpencodeClientLike): Promise<boole
|
|
|
493
496
|
* `ctx.model.contextWindow`), so for Pi this returns `undefined` and Pi's
|
|
494
497
|
* own resolution path is used.
|
|
495
498
|
*/
|
|
499
|
+
export function getSdkWindowGeometry(
|
|
500
|
+
providerID: string,
|
|
501
|
+
modelID: string,
|
|
502
|
+
detectedContextLimit?: number,
|
|
503
|
+
options?: {
|
|
504
|
+
detectedLimitProvenance?: ContextLimitProvenance;
|
|
505
|
+
harness?: "opencode" | "pi";
|
|
506
|
+
},
|
|
507
|
+
): WindowGeometryResult | undefined {
|
|
508
|
+
loadPersistedApiCacheOnce();
|
|
509
|
+
const metadata = lookupMetadataWithTagFallback(apiCache, providerID, modelID);
|
|
510
|
+
if (!metadata) return undefined;
|
|
511
|
+
const rawContext = metadata.contextLimit ?? metadata.limit;
|
|
512
|
+
const promptOnlyDetected =
|
|
513
|
+
options?.detectedLimitProvenance === "prompt_only" && isFinitePositive(detectedContextLimit)
|
|
514
|
+
? detectedContextLimit
|
|
515
|
+
: undefined;
|
|
516
|
+
const result = deriveWindowGeometry(
|
|
517
|
+
providerID,
|
|
518
|
+
modelID,
|
|
519
|
+
{
|
|
520
|
+
context: rawContext,
|
|
521
|
+
input: metadata.inputLimit,
|
|
522
|
+
output: metadata.outputLimit,
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
overlay: resolveWindowOverlayFacts(providerID, modelID, getWindowOverlay()),
|
|
526
|
+
reserveConfig: outputReserveConfig,
|
|
527
|
+
harness: options?.harness ?? "opencode",
|
|
528
|
+
contextCap:
|
|
529
|
+
promptOnlyDetected === undefined && isFinitePositive(detectedContextLimit)
|
|
530
|
+
? detectedContextLimit
|
|
531
|
+
: undefined,
|
|
532
|
+
},
|
|
533
|
+
);
|
|
534
|
+
if (!result || promptOnlyDetected === undefined) return result;
|
|
535
|
+
const usableSoft = promptOnlyDetected;
|
|
536
|
+
return {
|
|
537
|
+
...result,
|
|
538
|
+
usableSoft,
|
|
539
|
+
usableHard: Math.max(usableSoft, Math.min(result.usableHard, promptOnlyDetected)),
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
496
543
|
export function getSdkContextLimit(
|
|
497
544
|
providerID: string,
|
|
498
545
|
modelID: string,
|
|
@@ -502,6 +549,11 @@ export function getSdkContextLimit(
|
|
|
502
549
|
detectedLimitProvenance?: ContextLimitProvenance;
|
|
503
550
|
},
|
|
504
551
|
): number | undefined {
|
|
552
|
+
if (options?.reservation !== "none") {
|
|
553
|
+
return getSdkWindowGeometry(providerID, modelID, detectedContextLimit, {
|
|
554
|
+
detectedLimitProvenance: options?.detectedLimitProvenance,
|
|
555
|
+
})?.usableSoft;
|
|
556
|
+
}
|
|
505
557
|
loadPersistedApiCacheOnce();
|
|
506
558
|
const metadata = lookupMetadataWithTagFallback(apiCache, providerID, modelID);
|
|
507
559
|
if (!metadata) return undefined;
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
CTX_REDUCE_LIGHT_DESCRIPTION,
|
|
14
14
|
CTX_SEARCH_LIGHT_DESCRIPTION,
|
|
15
15
|
} from "../tools/light-descriptions";
|
|
16
|
+
import { piModelRefToCanonical } from "./harness-provider-map";
|
|
16
17
|
import { detectConfigFile } from "./jsonc-parser";
|
|
17
18
|
import {
|
|
18
19
|
type PromptSurfaceConfig,
|
|
@@ -242,13 +243,14 @@ export function createPromptSurfaceGuidanceEpochCache(runtime: PromptSurfaceRunt
|
|
|
242
243
|
|
|
243
244
|
return {
|
|
244
245
|
resolve(sessionId, config, modelKey) {
|
|
246
|
+
const canonicalModelKey = modelKey ? piModelRefToCanonical(modelKey) : undefined;
|
|
245
247
|
const cached = epochs.get(sessionId);
|
|
246
|
-
if (cached && cached.config === config && cached.modelKey ===
|
|
248
|
+
if (cached && cached.config === config && cached.modelKey === canonicalModelKey) {
|
|
247
249
|
return cached.selection;
|
|
248
250
|
}
|
|
249
251
|
|
|
250
|
-
const selection = runtime.resolveGuidance(config,
|
|
251
|
-
epochs.set(sessionId, { config, modelKey, selection });
|
|
252
|
+
const selection = runtime.resolveGuidance(config, canonicalModelKey);
|
|
253
|
+
epochs.set(sessionId, { config, modelKey: canonicalModelKey, selection });
|
|
252
254
|
return selection;
|
|
253
255
|
},
|
|
254
256
|
clear(sessionId) {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
2
4
|
|
|
3
5
|
import { resolveCacheTtl } from "../hooks/magic-context/event-resolvers";
|
|
4
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
modelKeyLookupOrder,
|
|
8
|
+
promptSurfaceConfigIdentity,
|
|
9
|
+
resolvePromptSurface,
|
|
10
|
+
} from "./prompt-surface";
|
|
5
11
|
|
|
6
12
|
describe("prompt-surface resolution", () => {
|
|
7
13
|
it("keeps cache_ttl and prompt-surface routing on the same lookup walk", () => {
|
|
@@ -11,6 +17,7 @@ describe("prompt-surface resolution", () => {
|
|
|
11
17
|
"google/*": "light" as const,
|
|
12
18
|
"CaseSensitive/model": "light" as const,
|
|
13
19
|
"progressive/base": "light" as const,
|
|
20
|
+
"bare-model": "light" as const,
|
|
14
21
|
};
|
|
15
22
|
const cacheTtl = {
|
|
16
23
|
default: "full",
|
|
@@ -26,6 +33,7 @@ describe("prompt-surface resolution", () => {
|
|
|
26
33
|
["casesensitive/model", "full"],
|
|
27
34
|
["CaseSensitive/model", "light"],
|
|
28
35
|
["progressive/base-extra", "light"],
|
|
36
|
+
["provider/bare-model", "light"],
|
|
29
37
|
["unknown/model", "full"],
|
|
30
38
|
[undefined, "full"],
|
|
31
39
|
] as const;
|
|
@@ -39,11 +47,88 @@ describe("prompt-surface resolution", () => {
|
|
|
39
47
|
}
|
|
40
48
|
});
|
|
41
49
|
|
|
42
|
-
it("
|
|
50
|
+
it("resolves Pi-native and canonical model keys with canonical precedence", () => {
|
|
51
|
+
const nativeKey = "openai-codex/gpt-5.6-sol";
|
|
52
|
+
const canonicalKey = "openai/gpt-5.6-sol";
|
|
53
|
+
|
|
54
|
+
expect(resolveCacheTtl({ default: "5m", [nativeKey]: "60m" }, canonicalKey)).toBe("60m");
|
|
55
|
+
expect(
|
|
56
|
+
resolvePromptSurface(
|
|
57
|
+
{ default: "full", models: { [nativeKey]: "light" } },
|
|
58
|
+
canonicalKey,
|
|
59
|
+
),
|
|
60
|
+
).toEqual({ preset: "light", source: "exact" });
|
|
61
|
+
expect(
|
|
62
|
+
resolveCacheTtl(
|
|
63
|
+
{ default: "5m", [nativeKey]: "60m", [canonicalKey]: "30m" },
|
|
64
|
+
nativeKey,
|
|
65
|
+
),
|
|
66
|
+
).toBe("30m");
|
|
67
|
+
expect(
|
|
68
|
+
resolvePromptSurface(
|
|
69
|
+
{ default: "full", models: { [nativeKey]: "light", [canonicalKey]: "full" } },
|
|
70
|
+
nativeKey,
|
|
71
|
+
),
|
|
72
|
+
).toEqual({ preset: "full", source: "exact" });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("leaves unknown provider keys unchanged", () => {
|
|
76
|
+
expect(
|
|
77
|
+
resolveCacheTtl(
|
|
78
|
+
{ default: "5m", "custom-provider/model": "1m" },
|
|
79
|
+
"custom-provider/model",
|
|
80
|
+
),
|
|
81
|
+
).toBe("1m");
|
|
82
|
+
expect(modelKeyLookupOrder("custom-provider/model")[0]).toEqual({
|
|
83
|
+
key: "custom-provider/model",
|
|
84
|
+
source: "exact",
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("derives a stable config identity independent of object key order", () => {
|
|
89
|
+
const left = promptSurfaceConfigIdentity({
|
|
90
|
+
default: "full",
|
|
91
|
+
models: { "provider/b": "light", "provider/a": "full" },
|
|
92
|
+
tool_descriptions: { ctx_search: "Search", ctx_note: "Note" },
|
|
93
|
+
});
|
|
94
|
+
const right = promptSurfaceConfigIdentity({
|
|
95
|
+
tool_descriptions: { ctx_note: "Note", ctx_search: "Search" },
|
|
96
|
+
models: { "provider/a": "full", "provider/b": "light" },
|
|
97
|
+
default: "full",
|
|
98
|
+
});
|
|
99
|
+
expect(left).toBe(right);
|
|
100
|
+
expect(
|
|
101
|
+
promptSurfaceConfigIdentity({ default: "light", models: { "provider/a": "full" } }),
|
|
102
|
+
).not.toBe(right);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("matches the Rust cache_ttl resolver over shared routing vectors", () => {
|
|
106
|
+
const vectors = JSON.parse(
|
|
107
|
+
readFileSync(
|
|
108
|
+
resolve(
|
|
109
|
+
import.meta.dir,
|
|
110
|
+
"../../../../crates/mc-module/testdata/cache-ttl-routing-vectors.json",
|
|
111
|
+
),
|
|
112
|
+
"utf8",
|
|
113
|
+
),
|
|
114
|
+
) as {
|
|
115
|
+
default: string;
|
|
116
|
+
models: Record<string, string>;
|
|
117
|
+
cases: Array<{ name: string; modelKey: string; expected: string }>;
|
|
118
|
+
};
|
|
119
|
+
const config = { default: vectors.default, ...vectors.models };
|
|
120
|
+
|
|
121
|
+
for (const vector of vectors.cases) {
|
|
122
|
+
expect(resolveCacheTtl(config, vector.modelKey), vector.name).toBe(vector.expected);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("applies exact, bare, wildcard, then default precedence", () => {
|
|
43
127
|
const config = {
|
|
44
128
|
default: "full" as const,
|
|
45
129
|
models: {
|
|
46
130
|
"provider/model": "light" as const,
|
|
131
|
+
bare: "light" as const,
|
|
47
132
|
"provider/*": "full" as const,
|
|
48
133
|
},
|
|
49
134
|
};
|
|
@@ -52,6 +137,10 @@ describe("prompt-surface resolution", () => {
|
|
|
52
137
|
preset: "light",
|
|
53
138
|
source: "exact",
|
|
54
139
|
});
|
|
140
|
+
expect(resolvePromptSurface(config, "provider/bare")).toEqual({
|
|
141
|
+
preset: "light",
|
|
142
|
+
source: "bare",
|
|
143
|
+
});
|
|
55
144
|
expect(resolvePromptSurface(config, "provider/other")).toEqual({
|
|
56
145
|
preset: "full",
|
|
57
146
|
source: "wildcard",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { modelRefLookupOrder } from "./harness-provider-map";
|
|
2
|
+
|
|
1
3
|
/** The built-in prompt-surface variants. */
|
|
2
4
|
export type PromptSurfacePreset = "full" | "light";
|
|
3
5
|
|
|
@@ -13,14 +15,29 @@ export interface PromptSurfaceConfig {
|
|
|
13
15
|
tool_descriptions?: Readonly<Record<string, string>>;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
/** Stable wire identity for the config fields that can alter a served prompt surface. */
|
|
19
|
+
export function promptSurfaceConfigIdentity(config: PromptSurfaceConfig | undefined): string {
|
|
20
|
+
return JSON.stringify({
|
|
21
|
+
default: config?.default ?? "full",
|
|
22
|
+
models: Object.entries(config?.models ?? {}).sort(([left], [right]) =>
|
|
23
|
+
left < right ? -1 : left > right ? 1 : 0,
|
|
24
|
+
),
|
|
25
|
+
guidanceOverridePath: config?.guidance_override_path ?? null,
|
|
26
|
+
toolDescriptions: Object.entries(config?.tool_descriptions ?? {}).sort(([left], [right]) =>
|
|
27
|
+
left < right ? -1 : left > right ? 1 : 0,
|
|
28
|
+
),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type PromptSurfaceResolutionSource = "exact" | "bare" | "wildcard" | "default";
|
|
17
33
|
|
|
18
|
-
/** Validate
|
|
34
|
+
/** Validate bare model, provider/model, and provider/* routing keys. */
|
|
19
35
|
export function isValidPromptSurfaceModelKey(key: string): boolean {
|
|
20
36
|
if (key.length === 0 || key.trim() !== key) return false;
|
|
21
37
|
|
|
22
38
|
const slash = key.indexOf("/");
|
|
23
|
-
if (slash
|
|
39
|
+
if (slash < 0) return !key.includes("*");
|
|
40
|
+
if (slash === 0 || slash === key.length - 1) return false;
|
|
24
41
|
|
|
25
42
|
const provider = key.slice(0, slash);
|
|
26
43
|
const modelID = key.slice(slash + 1);
|
|
@@ -42,7 +59,7 @@ export function isValidPromptSurfaceModelKey(key: string): boolean {
|
|
|
42
59
|
);
|
|
43
60
|
}
|
|
44
61
|
|
|
45
|
-
export type ModelKeyLookupSource = PromptSurfaceResolutionSource
|
|
62
|
+
export type ModelKeyLookupSource = Exclude<PromptSurfaceResolutionSource, "default">;
|
|
46
63
|
|
|
47
64
|
export interface ModelKeyCandidate {
|
|
48
65
|
key: string;
|
|
@@ -54,6 +71,8 @@ export interface ModelKeyCandidate {
|
|
|
54
71
|
* provider/model boundary is the first slash; the rest of the string remains
|
|
55
72
|
* the model ID, including additional slashes. Candidates are case-sensitive.
|
|
56
73
|
*
|
|
74
|
+
* Known harness provider aliases are checked canonical-first at each specificity,
|
|
75
|
+
* so one shared config works on every harness and canonical wins on collisions.
|
|
57
76
|
* Provider wildcards are checked after progressively less-specific model keys,
|
|
58
77
|
* but before the caller's default. That keeps an exact or base-model override
|
|
59
78
|
* authoritative while still allowing `provider/*` to cover otherwise-unlisted
|
|
@@ -67,10 +86,15 @@ export function modelKeyLookupOrder(modelKey: string | undefined): ModelKeyCandi
|
|
|
67
86
|
|
|
68
87
|
const provider = modelKey.slice(0, slash);
|
|
69
88
|
let modelID = modelKey.slice(slash + 1);
|
|
89
|
+
const providerRefs = modelRefLookupOrder(`${provider}/${modelID}`);
|
|
70
90
|
const candidates: ModelKeyCandidate[] = [];
|
|
71
91
|
|
|
72
92
|
while (modelID.length > 0) {
|
|
73
|
-
|
|
93
|
+
for (const providerRef of providerRefs) {
|
|
94
|
+
const providerSlash = providerRef.indexOf("/");
|
|
95
|
+
const providerPrefix = providerRef.slice(0, providerSlash);
|
|
96
|
+
candidates.push({ key: `${providerPrefix}/${modelID}`, source: "exact" });
|
|
97
|
+
}
|
|
74
98
|
candidates.push({ key: modelID, source: "bare" });
|
|
75
99
|
|
|
76
100
|
const lastDash = modelID.lastIndexOf("-");
|
|
@@ -78,7 +102,11 @@ export function modelKeyLookupOrder(modelKey: string | undefined): ModelKeyCandi
|
|
|
78
102
|
modelID = modelID.slice(0, lastDash);
|
|
79
103
|
}
|
|
80
104
|
|
|
81
|
-
|
|
105
|
+
for (const providerRef of providerRefs) {
|
|
106
|
+
const providerSlash = providerRef.indexOf("/");
|
|
107
|
+
const providerPrefix = providerRef.slice(0, providerSlash);
|
|
108
|
+
candidates.push({ key: `${providerPrefix}/*`, source: "wildcard" });
|
|
109
|
+
}
|
|
82
110
|
|
|
83
111
|
const seen = new Set<string>();
|
|
84
112
|
return candidates.filter((candidate) => {
|
|
@@ -117,11 +145,7 @@ export function resolvePromptSurface(
|
|
|
117
145
|
const fallback = config?.default ?? "full";
|
|
118
146
|
const match = resolveModelConfigValue(config?.models, modelKey);
|
|
119
147
|
|
|
120
|
-
|
|
121
|
-
// prompt_surface schema deliberately accepts only provider/model keys and
|
|
122
|
-
// provider wildcards. Ignore a bare entry if an unvalidated object reaches
|
|
123
|
-
// this API so prompt routing keeps its documented grammar.
|
|
124
|
-
if (match && match.source !== "bare") {
|
|
148
|
+
if (match) {
|
|
125
149
|
return { preset: match.value, source: match.source };
|
|
126
150
|
}
|
|
127
151
|
|
package/src/shared/rpc-client.ts
CHANGED
|
@@ -149,7 +149,9 @@ export class MagicContextRpcClient {
|
|
|
149
149
|
for (const entry of readdirSync(this.portDir)) {
|
|
150
150
|
if (!entry.startsWith("port-") || !entry.endsWith(".json")) continue;
|
|
151
151
|
const record = parseRpcPortFile(readFileSync(join(this.portDir, entry), "utf-8"));
|
|
152
|
-
|
|
152
|
+
// A denied liveness probe still leaves this as a candidate; the
|
|
153
|
+
// mandatory health check below confirms whether its RPC server is reachable.
|
|
154
|
+
if (!record || isPidAlive(record.pid) === "dead") continue;
|
|
153
155
|
records.push(record);
|
|
154
156
|
}
|
|
155
157
|
} catch {
|
|
@@ -158,7 +160,7 @@ export class MagicContextRpcClient {
|
|
|
158
160
|
|
|
159
161
|
try {
|
|
160
162
|
const legacy = parseRpcPortFile(readFileSync(this.legacyPortFilePath, "utf-8"));
|
|
161
|
-
if (legacy && (!legacy.pid || isPidAlive(legacy.pid))) records.push(legacy);
|
|
163
|
+
if (legacy && (!legacy.pid || isPidAlive(legacy.pid) !== "dead")) records.push(legacy);
|
|
162
164
|
} catch {
|
|
163
165
|
// Legacy discovery is optional.
|
|
164
166
|
}
|
package/src/shared/rpc-types.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
DreamTaskBacklogMap,
|
|
8
8
|
DreamTaskProgress,
|
|
9
9
|
} from "../features/magic-context/dreamer/task-registry";
|
|
10
|
+
import type { LoggerDiagnostics } from "./logger";
|
|
10
11
|
|
|
11
12
|
export interface SidebarSnapshot {
|
|
12
13
|
sessionId: string;
|
|
@@ -135,6 +136,17 @@ export interface StatusDetail extends SidebarSnapshot {
|
|
|
135
136
|
isSubagent: boolean;
|
|
136
137
|
pendingOps: Array<{ tagId: number; operation: string }>;
|
|
137
138
|
contextLimit: number;
|
|
139
|
+
windowGeometry?: {
|
|
140
|
+
usableSoft: number;
|
|
141
|
+
usableHard: number;
|
|
142
|
+
geometry: "shared_upfront" | "shared_truncating" | "separate";
|
|
143
|
+
derivation: {
|
|
144
|
+
window: number;
|
|
145
|
+
reserve: number;
|
|
146
|
+
reserveSource: "output_catalog" | "output_config" | "wall_margin" | "none";
|
|
147
|
+
geometry: "shared_upfront" | "shared_truncating" | "separate";
|
|
148
|
+
};
|
|
149
|
+
};
|
|
138
150
|
/**
|
|
139
151
|
* Parsed cache TTL in ms. -1 = never expires (cacheTtl "never"; Infinity
|
|
140
152
|
* cannot ride JSON-RPC and 0 would be indistinguishable from unset). The
|
|
@@ -170,6 +182,8 @@ export interface StatusDetail extends SidebarSnapshot {
|
|
|
170
182
|
toastDurationMs: number;
|
|
171
183
|
/** One-line status data for the experimental memory mural. */
|
|
172
184
|
mural?: { present: boolean; ageMs: number | null };
|
|
185
|
+
/** Runtime logger write failures observed by this plugin process. */
|
|
186
|
+
loggerDiagnostics: LoggerDiagnostics;
|
|
173
187
|
/**
|
|
174
188
|
* Stable storage-version probe: "which schema is the DB at, which fence does
|
|
175
189
|
* this binary carry". Field names are deliberately snake_case, mirroring the
|