@retaw106/mmc-oc 1.2.3 → 2.0.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/cli.js +7095 -0
- package/dist/config/index.d.ts +0 -12
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/legacy-harness-blocks.d.ts +27 -0
- package/dist/config/legacy-harness-blocks.d.ts.map +1 -0
- package/dist/config/live-run-config.d.ts.map +1 -1
- package/dist/config/live-snapshot.d.ts +2 -1
- package/dist/config/live-snapshot.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +4 -5
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/raw-loader.d.ts +7 -26
- package/dist/config/raw-loader.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +164 -259
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/config/variable.d.ts +8 -0
- package/dist/config/variable.d.ts.map +1 -1
- package/dist/config-init.js +5595 -0
- package/dist/{context-a6px04s0.js → context-95zkksn3.js} +2471 -2438
- package/dist/{derive-budgets-fhk00zv8.js → derive-budgets-rjjz5f7y.js} +2 -2
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/message-index.d.ts +2 -0
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/migration-v3-perf-schema.d.ts +8 -0
- package/dist/features/magic-context/migration-v3-perf-schema.d.ts.map +1 -0
- package/dist/features/magic-context/migration-v4-temporal-decisions.d.ts +12 -0
- package/dist/features/magic-context/migration-v4-temporal-decisions.d.ts.map +1 -0
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts +6 -0
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/reclaim-protection.d.ts +15 -10
- package/dist/features/magic-context/reclaim-protection.d.ts.map +1 -1
- package/dist/features/magic-context/session-cache-ttl.d.ts +4 -2
- package/dist/features/magic-context/session-cache-ttl.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-historian-runs.d.ts +2 -0
- package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
- package/dist/features/magic-context/storage-ops.d.ts +2 -0
- package/dist/features/magic-context/storage-ops.d.ts.map +1 -1
- package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts +15 -2
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +2 -2
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/temporal-decisions.d.ts +10 -0
- package/dist/features/magic-context/temporal-decisions.d.ts.map +1 -0
- package/dist/features/magic-context/tool-definition-tokens.d.ts +3 -0
- package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/active-anthropic-turn.d.ts +8 -0
- package/dist/hooks/magic-context/active-anthropic-turn.d.ts.map +1 -0
- package/dist/hooks/magic-context/apply-operations.d.ts +15 -0
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-deadline.d.ts +10 -3
- package/dist/hooks/magic-context/auto-search-deadline.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-cycle.d.ts +2 -0
- package/dist/hooks/magic-context/channel2-cycle.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-render-epoch.d.ts +8 -0
- package/dist/hooks/magic-context/compartment-render-epoch.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts +3 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +12 -0
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
- package/dist/hooks/magic-context/decision-calibration.d.ts +3 -0
- package/dist/hooks/magic-context/decision-calibration.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-drop.d.ts +2 -0
- package/dist/hooks/magic-context/emergency-drop.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-fail-closed.d.ts +21 -1
- package/dist/hooks/magic-context/emergency-fail-closed.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +30 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +2 -0
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +10 -8
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/latest-assistant-turn.d.ts +25 -0
- package/dist/hooks/magic-context/latest-assistant-turn.d.ts.map +1 -0
- package/dist/hooks/magic-context/lkg-measured-request.d.ts +69 -0
- package/dist/hooks/magic-context/lkg-measured-request.d.ts.map +1 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts +3 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
- package/dist/hooks/magic-context/reasoning-budget-status.d.ts +15 -0
- package/dist/hooks/magic-context/reasoning-budget-status.d.ts.map +1 -0
- package/dist/hooks/magic-context/reasoning-budget.d.ts +40 -0
- package/dist/hooks/magic-context/reasoning-budget.d.ts.map +1 -0
- package/dist/hooks/magic-context/reasoning-removal.d.ts +6 -6
- package/dist/hooks/magic-context/reasoning-removal.d.ts.map +1 -1
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts +2 -0
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
- package/dist/hooks/magic-context/sentinel.d.ts +2 -0
- package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +7 -3
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/supersession-reclaim.d.ts +4 -0
- package/dist/hooks/magic-context/supersession-reclaim.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +13 -0
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +5 -0
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
- package/dist/hooks/magic-context/temporal-awareness.d.ts +6 -3
- package/dist/hooks/magic-context/temporal-awareness.d.ts.map +1 -1
- package/dist/hooks/magic-context/temporal-served-projection.d.ts +4 -0
- package/dist/hooks/magic-context/temporal-served-projection.d.ts.map +1 -0
- package/dist/hooks/magic-context/tool-reclaim.d.ts +2 -0
- package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +34 -12
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +11 -5
- 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-wgt4gamw.js → index-1mjhzqq7.js} +422 -31
- package/dist/index-33gprt9g.js +30 -0
- package/dist/{index-zx63j2e2.js → index-5cep2r9n.js} +3 -2
- package/dist/{index-by6h2pav.js → index-bdt9my9y.js} +374 -239
- package/dist/{index-3wagyrnj.js → index-x37vg474.js} +5 -1
- package/dist/{index-1vs6anwz.js → index-xt7y10x6.js} +2 -1
- package/dist/{index-me3zcj5t.js → index-z2g20msd.js} +4 -8
- package/dist/index.js +1 -1
- package/dist/{message-time-backfill-5ewk0axe.js → message-time-backfill-zfernxbw.js} +1 -1
- package/dist/{model-resolution-5haaxnpt.js → model-resolution-wazv26n0.js} +1 -1
- package/dist/plugin/rpc-handlers.d.ts +4 -0
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/{read-session-chunk-apjwrttc.js → read-session-chunk-hp464g13.js} +2 -2
- package/dist/{recomp-orchestrator-gyeevxaw.js → recomp-orchestrator-3qxeqwb9.js} +6 -6
- package/dist/shared/auto-search-lifecycle.d.ts +7 -0
- package/dist/shared/auto-search-lifecycle.d.ts.map +1 -0
- package/dist/shared/cache-ttl-display.d.ts +6 -4
- package/dist/shared/cache-ttl-display.d.ts.map +1 -1
- package/dist/shared/model-cache-ttl.d.ts +1 -1
- package/dist/shared/model-cache-ttl.d.ts.map +1 -1
- package/dist/shared/model-resolution.d.ts +5 -2
- package/dist/shared/model-resolution.d.ts.map +1 -1
- package/dist/shared/model-suggestion-retry.d.ts +2 -0
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/protected-tools-policy.d.ts +6 -0
- package/dist/shared/protected-tools-policy.d.ts.map +1 -0
- package/dist/shared/provider-output-failure.d.ts +19 -0
- package/dist/shared/provider-output-failure.d.ts.map +1 -0
- package/dist/shared/temporal-upgrade-fixture.d.ts +19 -0
- package/dist/shared/temporal-upgrade-fixture.d.ts.map +1 -0
- package/dist/shared/window-geometry.d.ts +4 -0
- package/dist/shared/window-geometry.d.ts.map +1 -1
- package/dist/shared/write-transaction-timing.d.ts +8 -1
- package/dist/shared/write-transaction-timing.d.ts.map +1 -1
- package/dist/tools/ctx-reduce/tools.d.ts +2 -0
- package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
- package/dist/v2/hidden-child-native.d.ts +7 -0
- package/dist/v2/hidden-child-native.d.ts.map +1 -1
- package/dist/v2/hidden-child-record.d.ts +13 -0
- package/dist/v2/hidden-child-record.d.ts.map +1 -1
- package/dist/v2/hidden-completion.d.ts.map +1 -1
- package/dist/v2/hooks/context.d.ts.map +1 -1
- package/dist/v2/hooks/hidden-child.d.ts +46 -2
- package/dist/v2/hooks/hidden-child.d.ts.map +1 -1
- package/dist/v2/hooks/payload.d.ts.map +1 -1
- package/dist/v2/hooks/store.d.ts.map +1 -1
- package/dist/v2/hooks/tools.d.ts.map +1 -1
- package/dist/v2/hooks/types.d.ts +8 -0
- package/dist/v2/hooks/types.d.ts.map +1 -1
- package/dist/v2/server.js +4540 -3889
- package/dist/v2/tool-result.d.ts +8 -0
- package/dist/v2/tool-result.d.ts.map +1 -0
- package/dist/web/api.d.ts +6 -0
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/context-store.d.ts +13 -0
- package/dist/web/context-store.d.ts.map +1 -1
- package/dist/web/display-path.d.ts +28 -0
- package/dist/web/display-path.d.ts.map +1 -0
- package/dist/web/entry-args.d.ts +33 -0
- package/dist/web/entry-args.d.ts.map +1 -0
- package/dist/web/host-pi.d.ts +2 -0
- package/dist/web/host-pi.d.ts.map +1 -1
- package/dist/web/main.d.ts +4 -6
- package/dist/web/main.d.ts.map +1 -1
- package/dist/web/main.js +772 -77
- package/dist/web/message-stats.d.ts +45 -0
- package/dist/web/message-stats.d.ts.map +1 -0
- package/dist/web/ui/index.html +1 -0
- package/dist/web/ui/panel.css +5 -0
- package/dist/web/ui/panel.js +220 -70
- package/dist/web/views.d.ts +23 -0
- package/dist/web/views.d.ts.map +1 -1
- package/dist/{wrapup-orchestrator-z9sqpqjf.js → wrapup-orchestrator-wgb2nd19.js} +7 -6
- package/package.json +7 -2
- package/src/config/index.test.ts +257 -126
- package/src/config/index.ts +45 -39
- package/src/config/legacy-harness-blocks.test.ts +78 -0
- package/src/config/legacy-harness-blocks.ts +71 -0
- package/src/config/live-run-config.test.ts +49 -5
- package/src/config/live-run-config.ts +1 -0
- package/src/config/live-snapshot.ts +5 -1
- package/src/config/profiles.ts +1 -1
- package/src/config/project-config-trust-boundary.test.ts +2 -2
- package/src/config/project-security.hostile-matrix.test.ts +51 -65
- package/src/config/project-security.test.ts +6 -1
- package/src/config/project-security.ts +13 -12
- package/src/config/protected-tokens.test.ts +1 -1
- package/src/config/raw-loader.test.ts +29 -352
- package/src/config/raw-loader.ts +17 -406
- package/src/config/removed-config-keys.test.ts +1 -1
- package/src/config/schema/magic-context.test.ts +60 -64
- package/src/config/schema/magic-context.ts +105 -137
- package/src/config/variable.ts +21 -12
- package/src/shared/auto-search-lifecycle.ts +27 -0
- package/src/shared/cache-ttl-display.test.ts +1 -1
- package/src/shared/cache-ttl-display.ts +17 -16
- package/src/shared/model-cache-ttl.test.ts +2 -2
- package/src/shared/model-cache-ttl.ts +8 -4
- package/src/shared/model-resolution.test.ts +22 -32
- package/src/shared/model-resolution.ts +13 -18
- package/src/shared/model-suggestion-retry.ts +63 -7
- package/src/shared/protected-tools-policy.ts +25 -0
- package/src/shared/provider-output-failure.ts +176 -0
- package/src/shared/status-detail-text.test.ts +1 -1
- package/src/shared/status-view.test.ts +1 -1
- package/src/shared/temporal-upgrade-fixture.ts +50 -0
- package/src/shared/window-geometry.ts +10 -0
- package/src/shared/write-transaction-timing.ts +25 -1
- package/dist/features/magic-context/window-report-ledger.d.ts +0 -70
- package/dist/features/magic-context/window-report-ledger.d.ts.map +0 -1
- package/dist/index-e1q8002h.js +0 -16
- package/src/config/misplaced-agent-model.test.ts +0 -55
|
@@ -18,8 +18,9 @@ describe("model-resolution", () => {
|
|
|
18
18
|
const resolved = resolveHistorianAgentOverrides({
|
|
19
19
|
...(fixture.temperature !== undefined ? { temperature: fixture.temperature } : {}),
|
|
20
20
|
maxTokens: 16_000,
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
model: "google/flash",
|
|
22
|
+
variant: "low",
|
|
23
|
+
thinking_level: "high",
|
|
23
24
|
});
|
|
24
25
|
expect(resolved).toEqual({
|
|
25
26
|
maxTokens: 16_000,
|
|
@@ -61,8 +62,7 @@ describe("model-resolution", () => {
|
|
|
61
62
|
test("keeps the flash calibration available only as an explicit override", () => {
|
|
62
63
|
expect(
|
|
63
64
|
resolveHistorianAgentOverrides({
|
|
64
|
-
|
|
65
|
-
pi: { model: "pi/ignored" },
|
|
65
|
+
model: { model: "google/flash", variant: "fast" },
|
|
66
66
|
two_pass: true,
|
|
67
67
|
}),
|
|
68
68
|
).toEqual({
|
|
@@ -76,56 +76,46 @@ describe("model-resolution", () => {
|
|
|
76
76
|
).toMatchObject({ temperature: 0.1, maxTokens: 12_000 });
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
test("does not inherit
|
|
79
|
+
test("does not inherit the primary qualifier into unqualified fallbacks", () => {
|
|
80
80
|
const config = {
|
|
81
81
|
historian: {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
87
|
-
pi: {
|
|
88
|
-
model: "pi/primary",
|
|
89
|
-
fallback_models: ["pi/fallback"],
|
|
90
|
-
thinking_level: "high",
|
|
91
|
-
},
|
|
82
|
+
model: "open/primary",
|
|
83
|
+
fallback_models: ["open/fallback"],
|
|
84
|
+
variant: "primary-only",
|
|
85
|
+
thinking_level: "high",
|
|
92
86
|
},
|
|
93
87
|
};
|
|
94
88
|
|
|
95
89
|
// Inheritance is deliberately absent because a fallback model may not support the
|
|
96
|
-
// qualifier selected for the primary model.
|
|
90
|
+
// qualifier selected for the primary model. Each harness reads only its own
|
|
91
|
+
// qualifier (OpenCode variant, Pi thinking_level) from the flat entry.
|
|
97
92
|
expect(resolveHistorianModel(config, "opencode")).toEqual({
|
|
98
93
|
primary: { model: "open/primary", qualifier: "primary-only" },
|
|
99
94
|
fallbacks: [{ model: "open/fallback" }],
|
|
100
95
|
});
|
|
101
96
|
expect(resolveHistorianModel(config, "pi")).toEqual({
|
|
102
|
-
primary: { model: "
|
|
103
|
-
fallbacks: [{ model: "
|
|
97
|
+
primary: { model: "open/primary", qualifier: "high" },
|
|
98
|
+
fallbacks: [{ model: "open/fallback" }],
|
|
104
99
|
});
|
|
105
100
|
});
|
|
106
101
|
|
|
107
|
-
test("
|
|
102
|
+
test("selects the requested harness's qualifier from a shared flat entry", () => {
|
|
108
103
|
const config = {
|
|
109
104
|
historian: {
|
|
110
|
-
model: "
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
},
|
|
115
|
-
pi: {
|
|
116
|
-
model: { model: "pi/model", thinking_level: "high" },
|
|
117
|
-
fallback_models: [{ model: "pi/fallback", thinking_level: "max" }],
|
|
118
|
-
},
|
|
105
|
+
model: { model: "shared/model", variant: "oc-primary", thinking_level: "high" },
|
|
106
|
+
fallback_models: [
|
|
107
|
+
{ model: "shared/fallback", variant: "oc-fallback", thinking_level: "max" },
|
|
108
|
+
],
|
|
119
109
|
},
|
|
120
110
|
};
|
|
121
111
|
|
|
122
112
|
expect(resolveHistorianModel(config, "opencode")).toEqual({
|
|
123
|
-
primary: { model: "
|
|
124
|
-
fallbacks: [{ model: "
|
|
113
|
+
primary: { model: "shared/model", qualifier: "oc-primary" },
|
|
114
|
+
fallbacks: [{ model: "shared/fallback", qualifier: "oc-fallback" }],
|
|
125
115
|
});
|
|
126
116
|
expect(resolveHistorianModel(config, "pi")).toEqual({
|
|
127
|
-
primary: { model: "
|
|
128
|
-
fallbacks: [{ model: "
|
|
117
|
+
primary: { model: "shared/model", qualifier: "high" },
|
|
118
|
+
fallbacks: [{ model: "shared/fallback", qualifier: "max" }],
|
|
129
119
|
});
|
|
130
120
|
expect(resolveHistorianModel({}, "pi")).toEqual({ fallbacks: [] });
|
|
131
121
|
});
|
|
@@ -23,13 +23,6 @@ function readString(value: unknown): string | undefined {
|
|
|
23
23
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function resolveHarnessBlock(
|
|
27
|
-
container: ConfigRecord | undefined,
|
|
28
|
-
harness: ModelHarness,
|
|
29
|
-
): ConfigRecord | undefined {
|
|
30
|
-
return asRecord(container?.[harness]);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
26
|
/**
|
|
34
27
|
* Collapses the string and object spellings of a harness model entry into the
|
|
35
28
|
* same model identity. Qualifiers stay separate because they alter an attempt,
|
|
@@ -101,29 +94,31 @@ export interface ResolvedHistorianModel {
|
|
|
101
94
|
}
|
|
102
95
|
|
|
103
96
|
/**
|
|
104
|
-
* Resolve historian model attempts from the
|
|
97
|
+
* Resolve historian model attempts from the flat historian config. The harness
|
|
98
|
+
* parameter selects which qualifier is read: OpenCode uses `variant`, Pi uses
|
|
99
|
+
* `thinking_level`. The other harness's qualifier is ignored (no cross-harness
|
|
100
|
+
* mapping).
|
|
105
101
|
*/
|
|
106
102
|
export function resolveHistorianModel(
|
|
107
103
|
config: unknown,
|
|
108
104
|
harness: ModelHarness,
|
|
109
105
|
): ResolvedHistorianModel {
|
|
110
106
|
const historian = asRecord(asRecord(config)?.historian);
|
|
111
|
-
|
|
112
|
-
if (!block) return { fallbacks: [] };
|
|
107
|
+
if (!historian) return { fallbacks: [] };
|
|
113
108
|
|
|
114
109
|
return {
|
|
115
110
|
primary: resolvePrimaryEntry({
|
|
116
|
-
entry:
|
|
117
|
-
defaultQualifier:
|
|
111
|
+
entry: historian.model,
|
|
112
|
+
defaultQualifier: historian[harness === "opencode" ? "variant" : "thinking_level"],
|
|
118
113
|
harness,
|
|
119
114
|
}),
|
|
120
|
-
fallbacks: resolveFallbackEntries(
|
|
115
|
+
fallbacks: resolveFallbackEntries(historian.fallback_models, harness),
|
|
121
116
|
};
|
|
122
117
|
}
|
|
123
118
|
|
|
124
119
|
/**
|
|
125
120
|
* Keep non-model agent settings at their original level while replacing only
|
|
126
|
-
* OpenCode's model-resolution fields with the
|
|
121
|
+
* OpenCode's model-resolution fields with the flat primary entry.
|
|
127
122
|
*/
|
|
128
123
|
export function resolveOpenCodeAgentOverrides(agent: unknown): Record<string, unknown> {
|
|
129
124
|
const source = asRecord(agent);
|
|
@@ -133,14 +128,14 @@ export function resolveOpenCodeAgentOverrides(agent: unknown): Record<string, un
|
|
|
133
128
|
fallback_models: _fallbackModels,
|
|
134
129
|
variant: _variant,
|
|
135
130
|
thinking_level: _thinking,
|
|
136
|
-
opencode,
|
|
137
|
-
pi,
|
|
131
|
+
opencode: _opencode,
|
|
132
|
+
pi: _pi,
|
|
138
133
|
tasks,
|
|
139
134
|
...rest
|
|
140
135
|
} = source;
|
|
141
136
|
const primary = resolvePrimaryEntry({
|
|
142
|
-
entry:
|
|
143
|
-
defaultQualifier:
|
|
137
|
+
entry: source.model,
|
|
138
|
+
defaultQualifier: source.variant,
|
|
144
139
|
harness: "opencode",
|
|
145
140
|
});
|
|
146
141
|
return {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { createOpencodeClient } from "@opencode-ai/sdk";
|
|
2
|
-
|
|
3
2
|
import { detectOverflow } from "../features/magic-context/overflow-detection";
|
|
4
3
|
import { HiddenCompletionRefusal } from "../hooks/magic-context/compartment-runner-types";
|
|
5
4
|
import { HiddenAgentStepLimit } from "../v2/hooks/hidden-child";
|
|
@@ -11,6 +10,12 @@ import {
|
|
|
11
10
|
} from "./assistant-message-extractor";
|
|
12
11
|
import { log } from "./logger";
|
|
13
12
|
import type { ModelInput } from "./model-resolution";
|
|
13
|
+
import {
|
|
14
|
+
ProviderOutputFailureError,
|
|
15
|
+
primaryQuotaFailure,
|
|
16
|
+
providerOutputFailureFromInvalidManifest,
|
|
17
|
+
rememberPrimaryQuota,
|
|
18
|
+
} from "./provider-output-failure";
|
|
14
19
|
import { sanitizeDiagnosticText } from "./redaction";
|
|
15
20
|
import { parseProviderModel, toModelEntry } from "./resolve-fallbacks";
|
|
16
21
|
|
|
@@ -59,6 +64,8 @@ export interface PromptAttemptInfo {
|
|
|
59
64
|
totalAttempts: number;
|
|
60
65
|
/** Explicit model override for this attempt, when one was supplied. */
|
|
61
66
|
model?: { providerID: string; modelID: string };
|
|
67
|
+
/** A run-local quota deadline prevented dispatch; not an actual model call. */
|
|
68
|
+
skipped?: boolean;
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
export interface PromptRetryOptions {
|
|
@@ -433,14 +440,25 @@ function throwWithPromptFailure(
|
|
|
433
440
|
legacyError instanceof Error ? legacyError : new Error(extractMessage(legacyError));
|
|
434
441
|
const last = failedAttempts.at(-1);
|
|
435
442
|
const failureClass = last?.failureClass ?? "unknown";
|
|
443
|
+
const quota = failedAttempts.find(
|
|
444
|
+
(failure) =>
|
|
445
|
+
failure.error instanceof ProviderOutputFailureError &&
|
|
446
|
+
failure.error.quotaResetAt !== undefined,
|
|
447
|
+
)?.error as ProviderOutputFailureError | undefined;
|
|
448
|
+
if (quota && error !== quota && !error.message.includes(quota.message))
|
|
449
|
+
error.message = `${quota.message}; ${error.message}`;
|
|
436
450
|
const providerError =
|
|
437
|
-
failureClass === "provider_error"
|
|
438
|
-
? sanitizeDiagnosticText(
|
|
451
|
+
failureClass === "provider_error" || quota
|
|
452
|
+
? sanitizeDiagnosticText(
|
|
453
|
+
`${quota ? `${quota.message}; ` : ""}${shortErr(last?.error ?? legacyError)}`,
|
|
454
|
+
).slice(0, 500)
|
|
439
455
|
: null;
|
|
440
456
|
promptFailureDetails.set(error, {
|
|
441
457
|
failureClass,
|
|
442
458
|
modelAttempted: last?.attempt.label ?? null,
|
|
443
|
-
modelsTried: failedAttempts
|
|
459
|
+
modelsTried: failedAttempts
|
|
460
|
+
.filter((failure) => !failure.attempt.skipped)
|
|
461
|
+
.map((failure) => failure.attempt.label),
|
|
444
462
|
providerError,
|
|
445
463
|
timeoutMs: failureClass === "provider_timeout" ? timeoutMs : null,
|
|
446
464
|
childSessionId: transport ? (transport.childSessionId ?? null) : args.path.id || null,
|
|
@@ -664,6 +682,16 @@ async function attemptAndValidate<TOutput, TValidated>(
|
|
|
664
682
|
}
|
|
665
683
|
|
|
666
684
|
try {
|
|
685
|
+
// Only the structured account-pool notice bypasses caller validation.
|
|
686
|
+
// The near-zero-token heuristic remains owned by manifest callers.
|
|
687
|
+
const messages = Array.isArray(output)
|
|
688
|
+
? output
|
|
689
|
+
: (output as { messages?: unknown } | null)?.messages;
|
|
690
|
+
const text = extractLatestAssistantText(messages);
|
|
691
|
+
if (text) {
|
|
692
|
+
const failure = providerOutputFailureFromInvalidManifest(messages, text);
|
|
693
|
+
if (failure?.quotaResetAt !== undefined) throw failure;
|
|
694
|
+
}
|
|
667
695
|
if (!extractLatestAssistantText(output)) {
|
|
668
696
|
const assistantFailure = extractLatestAssistantFailure(output);
|
|
669
697
|
if (assistantFailure) {
|
|
@@ -718,8 +746,22 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
|
|
|
718
746
|
const failedAttempts: FailedAttempt[] = [];
|
|
719
747
|
let firstError: unknown = null;
|
|
720
748
|
let lastError: unknown = null;
|
|
749
|
+
const cooledPrimary = primaryQuotaFailure(explicitPrimaryLabel);
|
|
721
750
|
|
|
722
751
|
try {
|
|
752
|
+
if (cooledPrimary)
|
|
753
|
+
throw {
|
|
754
|
+
error: cooledPrimary,
|
|
755
|
+
failureClass: "provider_error",
|
|
756
|
+
attempt: {
|
|
757
|
+
label: explicitPrimaryLabel,
|
|
758
|
+
attemptIndex: 0,
|
|
759
|
+
isFallback: false,
|
|
760
|
+
totalAttempts,
|
|
761
|
+
model: baseBody.model,
|
|
762
|
+
skipped: true,
|
|
763
|
+
},
|
|
764
|
+
} satisfies FailedAttempt;
|
|
723
765
|
return await attemptAndValidate(
|
|
724
766
|
client,
|
|
725
767
|
baseArgs,
|
|
@@ -739,6 +781,19 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
|
|
|
739
781
|
const failure = caught as FailedAttempt;
|
|
740
782
|
failedAttempts.push(failure);
|
|
741
783
|
firstError = failure.error;
|
|
784
|
+
if (
|
|
785
|
+
failure.error instanceof ProviderOutputFailureError &&
|
|
786
|
+
failure.error.quotaResetAt !== undefined
|
|
787
|
+
)
|
|
788
|
+
failure.error.message = failure.error.message.replace(
|
|
789
|
+
/^provider quota exhausted/,
|
|
790
|
+
"primary quota exhausted",
|
|
791
|
+
);
|
|
792
|
+
const quota = !cooledPrimary && rememberPrimaryQuota(explicitPrimaryLabel, failure.error);
|
|
793
|
+
if (quota)
|
|
794
|
+
log(
|
|
795
|
+
`[${callContext}] ${quota.message}; skipping ${explicitPrimaryLabel} for this run until reset`,
|
|
796
|
+
);
|
|
742
797
|
lastError = failure.error;
|
|
743
798
|
if (isNonRetryable(failure.error, options.signal)) {
|
|
744
799
|
throwWithPromptFailure(
|
|
@@ -760,9 +815,10 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
|
|
|
760
815
|
);
|
|
761
816
|
}
|
|
762
817
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
818
|
+
if (!cooledPrimary && !quota)
|
|
819
|
+
log(
|
|
820
|
+
`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(failure.error)}; trying ${fallbacks.length} fallback(s)`,
|
|
821
|
+
);
|
|
766
822
|
}
|
|
767
823
|
|
|
768
824
|
for (let i = 0; i < fallbacks.length; i += 1) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Default result keep counts shared by runtime and configuration. */
|
|
2
|
+
export const DEFAULT_PROTECTED_TOOLS: Readonly<Record<string, number>> = {
|
|
3
|
+
todowrite: 1,
|
|
4
|
+
// The fork's tool name; upstream's ctx_reduce alias merges onto the same key.
|
|
5
|
+
mmc_reduce: 3,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function normalizeProtectedToolName(name: string | null): string {
|
|
9
|
+
const lowered = (name ?? "").toLowerCase().replace(/^mcp_/, "");
|
|
10
|
+
return lowered === "ctx_reduce" ? "mmc_reduce" : lowered;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Normalize before merging tiers so aliases cannot defeat a project override. */
|
|
14
|
+
export function mergeProtectedTools(
|
|
15
|
+
...maps: (Readonly<Record<string, number>> | undefined)[]
|
|
16
|
+
): Record<string, number> {
|
|
17
|
+
return Object.fromEntries(
|
|
18
|
+
[DEFAULT_PROTECTED_TOOLS, ...maps].flatMap((map) =>
|
|
19
|
+
Object.entries(map ?? {}).map(([name, count]) => [
|
|
20
|
+
normalizeProtectedToolName(name),
|
|
21
|
+
count,
|
|
22
|
+
]),
|
|
23
|
+
),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
import { isRecord } from "./record-type-guard";
|
|
5
|
+
|
|
6
|
+
const MAX_NEAR_ZERO_OUTPUT_TOKENS = 32;
|
|
7
|
+
|
|
8
|
+
interface AssistantCompletionShape {
|
|
9
|
+
createdAt: number;
|
|
10
|
+
finish: string | null;
|
|
11
|
+
error: unknown;
|
|
12
|
+
outputTokens: number | null;
|
|
13
|
+
reasoningTokens: number | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** A provider transport failure that arrived as an ordinary assistant completion. */
|
|
17
|
+
export class ProviderOutputFailureError extends Error {
|
|
18
|
+
readonly transient = true;
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
readonly fingerprint: string,
|
|
22
|
+
readonly outputTokens: number,
|
|
23
|
+
readonly reasoningTokens: number,
|
|
24
|
+
responseText: string,
|
|
25
|
+
readonly quotaResetAt?: number,
|
|
26
|
+
) {
|
|
27
|
+
const preview = responseText.trim().replace(/\s+/g, " ").slice(0, 160);
|
|
28
|
+
super(
|
|
29
|
+
quotaResetAt !== undefined
|
|
30
|
+
? `provider quota exhausted until ${new Date(quotaResetAt).toISOString()} (account-pool rate limit)`
|
|
31
|
+
: `provider outage completion (output_tokens=${outputTokens}, reasoning_tokens=${reasoningTokens}): ${JSON.stringify(preview)}`,
|
|
32
|
+
);
|
|
33
|
+
this.name = "ProviderOutputFailureError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Concurrent maintenance runs must not share provider health, and a new run
|
|
38
|
+
// must re-probe its configured primary. Nested verify batches inherit the scope.
|
|
39
|
+
const primaryQuotas = new AsyncLocalStorage<Map<string, ProviderOutputFailureError>>();
|
|
40
|
+
|
|
41
|
+
export function withDreamerModelCooldown<T>(run: () => T): T {
|
|
42
|
+
return primaryQuotas.getStore() ? run() : primaryQuotas.run(new Map(), run);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function rememberPrimaryQuota(
|
|
46
|
+
model: string,
|
|
47
|
+
error: unknown,
|
|
48
|
+
): ProviderOutputFailureError | null {
|
|
49
|
+
const quotas = primaryQuotas.getStore();
|
|
50
|
+
if (
|
|
51
|
+
!quotas ||
|
|
52
|
+
model === "primary" ||
|
|
53
|
+
!(error instanceof ProviderOutputFailureError) ||
|
|
54
|
+
error.quotaResetAt === undefined ||
|
|
55
|
+
error.quotaResetAt <= Date.now()
|
|
56
|
+
)
|
|
57
|
+
return null;
|
|
58
|
+
const cached = new ProviderOutputFailureError(
|
|
59
|
+
error.fingerprint,
|
|
60
|
+
error.outputTokens,
|
|
61
|
+
error.reasoningTokens,
|
|
62
|
+
"",
|
|
63
|
+
error.quotaResetAt,
|
|
64
|
+
);
|
|
65
|
+
cached.message = error.message;
|
|
66
|
+
quotas.set(model, cached);
|
|
67
|
+
return error;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function primaryQuotaFailure(model: string): ProviderOutputFailureError | null {
|
|
71
|
+
const quotas = primaryQuotas.getStore();
|
|
72
|
+
const error = quotas?.get(model);
|
|
73
|
+
if (!error) return null;
|
|
74
|
+
if (error.quotaResetAt === undefined || error.quotaResetAt <= Date.now()) {
|
|
75
|
+
quotas?.delete(model);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const failure = new ProviderOutputFailureError(
|
|
79
|
+
error.fingerprint,
|
|
80
|
+
error.outputTokens,
|
|
81
|
+
error.reasoningTokens,
|
|
82
|
+
"",
|
|
83
|
+
error.quotaResetAt,
|
|
84
|
+
);
|
|
85
|
+
failure.message = error.message;
|
|
86
|
+
return failure;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The auth adapter emits this entire notice as assistant text, not a host error.
|
|
90
|
+
* Match the envelope, never a quoted notice inside a real manifest or narration. */
|
|
91
|
+
function quotaResetAt(responseText: string, now: number): number | undefined {
|
|
92
|
+
const notice =
|
|
93
|
+
/^All [1-9]\d* account\(s\) rate-limited for [\w.-]+\. Quota resets in ((?:\d+[hms] ?)+)\. Add more accounts with `opencode auth login` or wait and retry\.$/.exec(
|
|
94
|
+
responseText.trim(),
|
|
95
|
+
);
|
|
96
|
+
if (!notice) return undefined;
|
|
97
|
+
const units: Record<string, number> = { h: 3_600_000, m: 60_000, s: 1_000 };
|
|
98
|
+
const duration = [...notice[1].matchAll(/(\d+)([hms])/g)].reduce(
|
|
99
|
+
(sum, match) => sum + Number(match[1]) * units[match[2]],
|
|
100
|
+
0,
|
|
101
|
+
);
|
|
102
|
+
const resetAt = now + duration;
|
|
103
|
+
return duration > 0 && Number.isSafeInteger(resetAt) && resetAt < 8.64e15 ? resetAt : undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function finiteTokenCount(value: unknown): number | null {
|
|
107
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function completionShape(value: unknown): AssistantCompletionShape | null {
|
|
111
|
+
if (!isRecord(value)) return null;
|
|
112
|
+
const info = isRecord(value.info) ? value.info : value;
|
|
113
|
+
if (info.role !== "assistant") return null;
|
|
114
|
+
|
|
115
|
+
const time = isRecord(info.time) ? info.time : null;
|
|
116
|
+
const tokens = isRecord(info.tokens) ? info.tokens : null;
|
|
117
|
+
return {
|
|
118
|
+
createdAt: typeof time?.created === "number" ? time.created : 0,
|
|
119
|
+
finish:
|
|
120
|
+
typeof info.finish === "string"
|
|
121
|
+
? info.finish
|
|
122
|
+
: typeof info.finish_reason === "string"
|
|
123
|
+
? info.finish_reason
|
|
124
|
+
: typeof info.finishReason === "string"
|
|
125
|
+
? info.finishReason
|
|
126
|
+
: null,
|
|
127
|
+
error: info.error,
|
|
128
|
+
outputTokens: finiteTokenCount(tokens?.output),
|
|
129
|
+
reasoningTokens: finiteTokenCount(tokens?.reasoning),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function latestAssistantCompletion(messages: unknown): AssistantCompletionShape | null {
|
|
134
|
+
if (!Array.isArray(messages)) return null;
|
|
135
|
+
let latest: AssistantCompletionShape | null = null;
|
|
136
|
+
for (const message of messages) {
|
|
137
|
+
const completion = completionShape(message);
|
|
138
|
+
if (completion && (!latest || completion.createdAt >= latest.createdAt))
|
|
139
|
+
latest = completion;
|
|
140
|
+
}
|
|
141
|
+
return latest;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* OpenCode can serialize a provider outage as a successful `finish=stop` assistant
|
|
146
|
+
* message. Only classify that shape after manifest validation has already failed:
|
|
147
|
+
* a real manifest remains authoritative regardless of its token counts.
|
|
148
|
+
*/
|
|
149
|
+
export function providerOutputFailureFromInvalidManifest(
|
|
150
|
+
messages: unknown,
|
|
151
|
+
responseText: string,
|
|
152
|
+
now = Date.now(),
|
|
153
|
+
): ProviderOutputFailureError | null {
|
|
154
|
+
const completion = latestAssistantCompletion(messages);
|
|
155
|
+
if (completion?.finish?.toLowerCase() !== "stop") return null;
|
|
156
|
+
if (
|
|
157
|
+
completion.error != null ||
|
|
158
|
+
completion.outputTokens === null ||
|
|
159
|
+
completion.reasoningTokens !== 0
|
|
160
|
+
) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const normalized = responseText.trim().replace(/\s+/g, " ").toLowerCase();
|
|
165
|
+
if (!normalized) return null;
|
|
166
|
+
const resetAt = quotaResetAt(responseText, now);
|
|
167
|
+
if (resetAt === undefined && completion.outputTokens > MAX_NEAR_ZERO_OUTPUT_TOKENS) return null;
|
|
168
|
+
const fingerprint = createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
169
|
+
return new ProviderOutputFailureError(
|
|
170
|
+
fingerprint,
|
|
171
|
+
completion.outputTokens,
|
|
172
|
+
completion.reasoningTokens,
|
|
173
|
+
responseText,
|
|
174
|
+
resetAt,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
@@ -92,7 +92,7 @@ describe("status detail text", () => {
|
|
|
92
92
|
expect(formatStatusDetailMarkdown(STATUS_FIXTURE)).toBe(`## Magic Context Status
|
|
93
93
|
|
|
94
94
|
- **Context:** 75.0% of usable context (96,000 / 128,000 tokens)
|
|
95
|
-
- **Cache lifetime:** 1h (config
|
|
95
|
+
- **Cache lifetime:** 1h (your config)
|
|
96
96
|
- **Automatic compression:** at 65.0% of usable context
|
|
97
97
|
- **History compression:** Compressing history · 12 history blocks
|
|
98
98
|
- **Reclaimable:** 3 spent tool outputs (~14k tokens)
|
|
@@ -256,7 +256,7 @@ describe("status view model", () => {
|
|
|
256
256
|
* gap fit; otherwise the caller draws one column.
|
|
257
257
|
*/
|
|
258
258
|
test("draws two columns only when both columns' values fit", () => {
|
|
259
|
-
const sections = view().sections;
|
|
259
|
+
const sections = view({ cacheTtlSource: "default" }).sections;
|
|
260
260
|
for (const section of sections) {
|
|
261
261
|
const longest = Math.max(...section.rows.map((row) => row.value.length));
|
|
262
262
|
expect(statusSectionWidth(section)).toBe(section.labelWidth + 1 + longest);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fixture from "../../../../testdata/temporal-upgrade-projections.json";
|
|
2
|
+
import type { Database } from "./sqlite";
|
|
3
|
+
|
|
4
|
+
export type TemporalFixtureRuntime = "Pi" | "OpenCode 1" | "OpenCode 2";
|
|
5
|
+
type Value = null | string | number | { base64: string };
|
|
6
|
+
interface CapturedSession {
|
|
7
|
+
sessionId: string;
|
|
8
|
+
cwd?: string;
|
|
9
|
+
input: unknown[];
|
|
10
|
+
entryIds?: string[];
|
|
11
|
+
projection: unknown[];
|
|
12
|
+
projectionJson: string;
|
|
13
|
+
state: Record<string, Array<Record<string, Value>>>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const temporalUpgradeCases: Record<TemporalFixtureRuntime, CapturedSession> = fixture.cases;
|
|
17
|
+
|
|
18
|
+
/** Restore old served state, never by first rendering it with the current code. */
|
|
19
|
+
export function seedTemporalUpgradeFixture(
|
|
20
|
+
db: Database,
|
|
21
|
+
runtime: TemporalFixtureRuntime,
|
|
22
|
+
): CapturedSession {
|
|
23
|
+
const captured = temporalUpgradeCases[runtime];
|
|
24
|
+
db.transaction(() => {
|
|
25
|
+
for (const [table, rows] of Object.entries(captured.state)) {
|
|
26
|
+
for (const row of rows) {
|
|
27
|
+
const columns = Object.keys(row);
|
|
28
|
+
const values = Object.values(row).map((value) =>
|
|
29
|
+
value !== null && typeof value === "object"
|
|
30
|
+
? Buffer.from(value.base64, "base64")
|
|
31
|
+
: value,
|
|
32
|
+
);
|
|
33
|
+
db.prepare(
|
|
34
|
+
`INSERT INTO ${table} (${columns.join(",")}) VALUES (${columns.map(() => "?").join(",")})`,
|
|
35
|
+
).run(...values);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Advance only the cache clock: timestamp-derived marker bytes and
|
|
39
|
+
// the old input/projection are the immutable captured fixture.
|
|
40
|
+
db.prepare("UPDATE session_meta SET last_response_time=? WHERE session_id=?").run(
|
|
41
|
+
Date.now(),
|
|
42
|
+
captured.sessionId,
|
|
43
|
+
);
|
|
44
|
+
db.prepare("UPDATE lkg_slots SET captured_at=? WHERE session_id=?").run(
|
|
45
|
+
Date.now(),
|
|
46
|
+
captured.sessionId,
|
|
47
|
+
);
|
|
48
|
+
}).immediate();
|
|
49
|
+
return captured;
|
|
50
|
+
}
|
|
@@ -89,6 +89,8 @@ export interface WindowDerivation {
|
|
|
89
89
|
geometry: WindowGeometry;
|
|
90
90
|
windowSource: WindowLimitSource;
|
|
91
91
|
absoluteWall: number;
|
|
92
|
+
/** Accepted provider input used to raise the usable denominator above metadata. */
|
|
93
|
+
provenInputTokens?: number;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
export interface WindowGeometryResult {
|
|
@@ -691,6 +693,7 @@ export function applyProvenInputFloor(
|
|
|
691
693
|
absoluteWall: trustedAbsoluteWall
|
|
692
694
|
? geometry.derivation.absoluteWall
|
|
693
695
|
: Math.max(geometry.derivation.absoluteWall, provenInputTokens),
|
|
696
|
+
provenInputTokens,
|
|
694
697
|
},
|
|
695
698
|
},
|
|
696
699
|
};
|
|
@@ -715,6 +718,13 @@ export function formatWindowDerivationLine(
|
|
|
715
718
|
return `${formatCompactTokens(inputTokens)} / ${formatCompactTokens(result.usableSoft)} usable · window ${formatCompactTokens(result.derivation.window)} · ${formatCompactTokens(result.derivation.reserve)} ${reserveLabel}`;
|
|
716
719
|
}
|
|
717
720
|
|
|
721
|
+
/** Distinguish metadata from a denominator raised by an accepted input measurement. */
|
|
722
|
+
export function formatWindowSource(result: WindowGeometryResult | undefined): string {
|
|
723
|
+
return result?.derivation.provenInputTokens
|
|
724
|
+
? `provider-measured floor (${result.derivation.windowSource})`
|
|
725
|
+
: (result?.derivation.windowSource ?? "unknown");
|
|
726
|
+
}
|
|
727
|
+
|
|
718
728
|
export function formatCompactTokens(value: number): string {
|
|
719
729
|
if (Math.abs(value) >= 1_000_000) {
|
|
720
730
|
return `${(value / 1_000_000).toFixed(1).replace(/\.0$/, "")}m`;
|
|
@@ -15,12 +15,36 @@ export function logSlowWriteTransaction(
|
|
|
15
15
|
startedAt: number,
|
|
16
16
|
thresholdMs = SLOW_WRITE_TRANSACTION_THRESHOLD_MS,
|
|
17
17
|
completedAtMs = performance.now(),
|
|
18
|
+
steps?: Readonly<Record<string, number>>,
|
|
18
19
|
): void {
|
|
19
20
|
try {
|
|
20
21
|
const durationMs = completedAtMs - startedAt;
|
|
21
22
|
if (durationMs < thresholdMs) return;
|
|
22
|
-
|
|
23
|
+
const detail = steps
|
|
24
|
+
? Object.entries(steps)
|
|
25
|
+
.map(([name, ms]) => `${name}=${ms.toFixed(1)}ms`)
|
|
26
|
+
.join(" ")
|
|
27
|
+
: "";
|
|
28
|
+
log(
|
|
29
|
+
`[magic-context] slow write transaction: site=${site} held=${durationMs.toFixed(1)}ms${detail ? ` ${detail}` : ""}`,
|
|
30
|
+
);
|
|
23
31
|
} catch {
|
|
24
32
|
// Timing diagnostics must never make a committed write appear to fail.
|
|
25
33
|
}
|
|
26
34
|
}
|
|
35
|
+
|
|
36
|
+
/** Step durations include precomputation when named `pre_*`; held time never does. */
|
|
37
|
+
export class WriteTransactionSteps {
|
|
38
|
+
readonly durations: Record<string, number> = {};
|
|
39
|
+
private last = performance.now();
|
|
40
|
+
|
|
41
|
+
mark(name: string): void {
|
|
42
|
+
const now = performance.now();
|
|
43
|
+
this.durations[name] = now - this.last;
|
|
44
|
+
this.last = now;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
reset(): void {
|
|
48
|
+
this.last = performance.now();
|
|
49
|
+
}
|
|
50
|
+
}
|