pi-mega-compact 0.20.4 → 0.20.6
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/vector-cortex.js +24 -0
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +2 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-render.js +40 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-rollout.js +43 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex.js +2 -0
- package/dist/extensions/dashboard-server/routes.js +1 -1
- package/dist/extensions/dashboard-server/server.js +5 -1
- package/dist/extensions/mega-context/vector-cortex.js +128 -0
- package/dist/extensions/mega-runtime/vector-cortex-live.js +105 -0
- package/dist/src/config/vector-cortex.js +24 -0
- package/dist/src/config.js +1 -1
- package/dist/src/store/sqlite/global-sessions.js +16 -4
- package/dist/src/vector-cortex/provider/registry.js +112 -0
- package/dist/src/vector-cortex/provider/types.js +30 -0
- package/dist/src/vector-cortex/render/_acceptance-fixture.js +58 -0
- package/dist/src/vector-cortex/render/_acceptance-helpers.js +17 -0
- package/dist/src/vector-cortex/render/_acceptance-provider.js +29 -0
- package/dist/src/vector-cortex/render/_acceptance-scenario.js +259 -0
- package/dist/src/vector-cortex/render/renderer.js +119 -0
- package/dist/src/vector-cortex/render/types.js +29 -0
- package/dist/src/vector-cortex/render/validator.js +90 -0
- package/dist/src/vector-cortex/rollout/_acceptance-fixture.js +55 -0
- package/dist/src/vector-cortex/rollout/_acceptance-helpers.js +8 -0
- package/dist/src/vector-cortex/rollout/_acceptance-scenario.js +70 -0
- package/dist/src/vector-cortex/rollout/assign.js +56 -0
- package/dist/src/vector-cortex/rollout/emit.js +50 -0
- package/dist/src/vector-cortex/rollout/gate.js +75 -0
- package/dist/src/vector-cortex/rollout/types.js +39 -0
- package/dist/vector-cortex/provider/registry.js +112 -0
- package/dist/vector-cortex/provider/types.js +30 -0
- package/dist/vector-cortex/render/_acceptance-fixture.js +58 -0
- package/dist/vector-cortex/render/_acceptance-helpers.js +17 -0
- package/dist/vector-cortex/render/_acceptance-provider.js +29 -0
- package/dist/vector-cortex/render/_acceptance-scenario.js +259 -0
- package/dist/vector-cortex/render/renderer.js +119 -0
- package/dist/vector-cortex/render/types.js +29 -0
- package/dist/vector-cortex/render/validator.js +90 -0
- package/dist/vector-cortex/rollout/_acceptance-fixture.js +55 -0
- package/dist/vector-cortex/rollout/_acceptance-helpers.js +8 -0
- package/dist/vector-cortex/rollout/_acceptance-scenario.js +70 -0
- package/dist/vector-cortex/rollout/assign.js +56 -0
- package/dist/vector-cortex/rollout/emit.js +50 -0
- package/dist/vector-cortex/rollout/gate.js +75 -0
- package/dist/vector-cortex/rollout/types.js +39 -0
- package/extensions/dashboard-client/dist/assets/{AreaChart-Cam9-nhT.js → AreaChart-DqTbaSRA.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-Cam9-nhT.js.map → AreaChart-DqTbaSRA.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-DzIY8aM1.js → BarChart-BfNBeOvF.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-DzIY8aM1.js.map → BarChart-BfNBeOvF.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-CKIiYsG_.js → CacheTab-C9SOMYB5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-CKIiYsG_.js.map → CacheTab-C9SOMYB5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-xaaBJLiK.js → EventsTab-GsMKP10_.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-xaaBJLiK.js.map → EventsTab-GsMKP10_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-BGtUKOqG.js → HealthTab-DAOCXwRP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-BGtUKOqG.js.map → HealthTab-DAOCXwRP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-ABgQnTZ9.js → MaintenanceTab-BtBeMkB5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-ABgQnTZ9.js.map → MaintenanceTab-BtBeMkB5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-ChR2GAN-.js → MemoryMapTab-DzndtgkZ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-ChR2GAN-.js.map → MemoryMapTab-DzndtgkZ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-DdCLAgcR.js → MetricsTab-DyxJ4eb8.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-DdCLAgcR.js.map → MetricsTab-DyxJ4eb8.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DtemzJt6.js → OverviewTab-DCiEdVWR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DtemzJt6.js.map → OverviewTab-DCiEdVWR.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-B9mvD9Ep.js → ReposTab-B5SIqVHA.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-B9mvD9Ep.js.map → ReposTab-B5SIqVHA.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BcO38XLS.js → SessionsTab-DLWFJ9xI.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BcO38XLS.js.map → SessionsTab-DLWFJ9xI.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-D_TBNP09.js → SetupTab-CvGXZyej.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-D_TBNP09.js.map → SetupTab-CvGXZyej.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-TjHpbUOo.js → TimeSavedCard-wZuQGxLS.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-TjHpbUOo.js.map → TimeSavedCard-wZuQGxLS.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DprxndCF.js → TurnsTab-B7aOejch.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DprxndCF.js.map → TurnsTab-B7aOejch.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-eHVt9yDx.js +2 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-eHVt9yDx.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-OKgSo15G.js → WikiTab-DhWHjAtt.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-OKgSo15G.js.map → WikiTab-DhWHjAtt.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-D_Qz4q7L.js → button-BrC-W87h.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-D_Qz4q7L.js.map → button-BrC-W87h.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-CsI6Jm3p.js → card-0rgfRgr3.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-CsI6Jm3p.js.map → card-0rgfRgr3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CvHKUdhX.js → generateCategoricalChart-DRresOQF.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CvHKUdhX.js.map → generateCategoricalChart-DRresOQF.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-0Ye_un22.js → index-DohmtZWL.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-0Ye_un22.js.map → index-DohmtZWL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-VQEoJl8Q.js → switch-Bs8g_zB5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-VQEoJl8Q.js.map → switch-Bs8g_zB5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-CAPm4HJo.js → toggle-DVVauIo8.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-CAPm4HJo.js.map → toggle-DVVauIo8.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-pxsNTVkh.js → useSSE-DxH1PTAR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-pxsNTVkh.js.map → useSSE-DxH1PTAR.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/api/vector-cortex.ts +18 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexMetric.tsx +10 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexPlansCard.tsx +39 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexRenderCard.tsx +38 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexRolloutCard.tsx +44 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +19 -38
- package/extensions/dashboard-client/src/types/vector-cortex.ts +26 -0
- package/extensions/dashboard-server/api-contracts/vector-cortex.ts +52 -1
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +12 -0
- package/extensions/dashboard-server/routes-vector-cortex-render.ts +49 -0
- package/extensions/dashboard-server/routes-vector-cortex-rollout.ts +52 -0
- package/extensions/dashboard-server/routes-vector-cortex.ts +2 -0
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +4 -0
- package/extensions/mega-context/vector-cortex.ts +163 -0
- package/extensions/mega-runtime/vector-cortex-live.ts +165 -0
- package/package.json +1 -1
- package/src/config/vector-cortex.ts +26 -0
- package/src/config.ts +2 -0
- package/src/store/sqlite/global-sessions.ts +14 -2
- package/src/vector-cortex/provider/registry.ts +137 -0
- package/src/vector-cortex/provider/types.ts +163 -0
- package/src/vector-cortex/render/_acceptance-fixture.ts +128 -0
- package/src/vector-cortex/render/_acceptance-helpers.ts +39 -0
- package/src/vector-cortex/render/_acceptance-provider.ts +45 -0
- package/src/vector-cortex/render/_acceptance-scenario.ts +319 -0
- package/src/vector-cortex/render/renderer.ts +142 -0
- package/src/vector-cortex/render/types.ts +174 -0
- package/src/vector-cortex/render/validator.ts +120 -0
- package/src/vector-cortex/rollout/_acceptance-fixture.ts +94 -0
- package/src/vector-cortex/rollout/_acceptance-helpers.ts +24 -0
- package/src/vector-cortex/rollout/_acceptance-scenario.ts +96 -0
- package/src/vector-cortex/rollout/assign.ts +67 -0
- package/src/vector-cortex/rollout/emit.ts +81 -0
- package/src/vector-cortex/rollout/gate.ts +119 -0
- package/src/vector-cortex/rollout/types.ts +111 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-Bym21yVQ.js +0 -2
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-Bym21yVQ.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.6",
|
|
4
4
|
"description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -188,6 +188,32 @@ export const VC4C_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC4C");
|
|
|
188
188
|
*/
|
|
189
189
|
export const VC5A_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC5A");
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* VC5B — validated prompt renderer + provider profiles. Default ON.
|
|
193
|
+
* `MEGACOMPACT_VC5B=0` disables and is byte-identical to the predecessor
|
|
194
|
+
* (VC5A): no render manifest is produced, no canonical request is hashed, the
|
|
195
|
+
* `vector_cortex_render_validated` / `vector_cortex_provider_bypassed` events are
|
|
196
|
+
* never emitted, and the prompt continues to be built by the predecessor VC5A
|
|
197
|
+
* DAG/plan path (its goldens are unchanged). The render/validate functions are
|
|
198
|
+
* PURE — flag OFF gates the reporter + dashboard seam, never the arithmetic.
|
|
199
|
+
* This flag MUST also be a dashboard SETTINGS toggle (visible in config UI, never
|
|
200
|
+
* in EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A.
|
|
201
|
+
*/
|
|
202
|
+
export const VC5B_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC5B");
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* VC5C — live graduated rollout. Default ON. `MEGACOMPACT_VC5C=0` disables and
|
|
206
|
+
* is byte-identical to the predecessor (VC5B): no session is assigned a stable
|
|
207
|
+
* 10k bucket, no gate-advance decision runs, the `vector_cortex_rollout_assigned`
|
|
208
|
+
* / `vector_cortex_rollout_promotion_blocked` events are never emitted, and the
|
|
209
|
+
* prompt continues to be built by the predecessor VC5B renderer path (its goldens
|
|
210
|
+
* are unchanged). The assign/gate/emit functions are PURE — flag OFF gates the
|
|
211
|
+
* reporter + dashboard seam, never the arithmetic. This flag MUST also be a
|
|
212
|
+
* dashboard SETTINGS toggle (visible in config UI, never in EXCLUDED_SETTINGS),
|
|
213
|
+
* mirroring VC4A/VC4B/VC4C/VC5A/VC5B.
|
|
214
|
+
*/
|
|
215
|
+
export const VC5C_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC5C");
|
|
216
|
+
|
|
191
217
|
// ---------------------------------------------------------------------------
|
|
192
218
|
// Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
|
|
193
219
|
// Rolled numbers for one 60s window; VC0C consumes these at its breaker seam.
|
package/src/config.ts
CHANGED
|
@@ -82,9 +82,15 @@ export function recordSessionHeartbeat(
|
|
|
82
82
|
stateDir: string,
|
|
83
83
|
ctxWindow: number,
|
|
84
84
|
indexDir: string = getIndexDir(),
|
|
85
|
+
// Optional clock override for deterministic tests only. Production callers
|
|
86
|
+
// omit it → Date.now(), byte-identical behavior. The S39 prune test pins
|
|
87
|
+
// heartbeats to a known-old timestamp so pruneStaleSessions' Date.now()
|
|
88
|
+
// cutoff unambiguously exceeds them (no same-millisecond last_seen ==
|
|
89
|
+
// cutoff race that made pruned !== 2).
|
|
90
|
+
nowMs: number = Date.now(),
|
|
85
91
|
): void {
|
|
86
92
|
const db = openIndexStore(indexDir);
|
|
87
|
-
const now =
|
|
93
|
+
const now = nowMs;
|
|
88
94
|
db.prepare(
|
|
89
95
|
`INSERT INTO session_heartbeats (pid, session_id, repo_root, state_dir, ctx_window, last_seen)
|
|
90
96
|
VALUES (@pid, @session_id, @repo_root, @state_dir, @ctx_window, @last_seen)
|
|
@@ -127,9 +133,15 @@ export function appendTokenSample(
|
|
|
127
133
|
ctxWindow: number,
|
|
128
134
|
eventsLogPath: string | null,
|
|
129
135
|
indexDir: string = getIndexDir(),
|
|
136
|
+
// Optional clock override for deterministic tests only. Production callers
|
|
137
|
+
// omit it → falls back to Date.now(), byte-identical behavior. The S39
|
|
138
|
+
// timeseries test injects distinct timestamps so the per-ts totals map is
|
|
139
|
+
// not collapsed by same-millisecond sample coincidence (the pre-existing
|
|
140
|
+
// flake where 3 samples in one ms made totals.length == 1).
|
|
141
|
+
nowMs: number = Date.now(),
|
|
130
142
|
): void {
|
|
131
143
|
const db = openIndexStore(indexDir);
|
|
132
|
-
const now =
|
|
144
|
+
const now = nowMs;
|
|
133
145
|
db.prepare(
|
|
134
146
|
`INSERT INTO token_samples (session_id, repo_root, tokens, percent, ctx_window, ts)
|
|
135
147
|
VALUES (@session_id, @repo_root, @tokens, @percent, @ctx_window, @ts)`,
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/provider/registry.ts — fixture-backed base provider profile
|
|
3
|
+
* registry (VC5B, task 2).
|
|
4
|
+
*
|
|
5
|
+
* The registry resolves a `(provider, model)` pair to a `ProviderProfileBundle`
|
|
6
|
+
* (CONTRACTS §ProviderProfileV1 + role/tool/cache rules). Unknown provider/model
|
|
7
|
+
* returns `PRO_PROFILE_UNKNOWN` and the renderer CLEANLY bypasses vector-cortex
|
|
8
|
+
* rendering (NOT an error — the host uses the predecessor prompt path).
|
|
9
|
+
*
|
|
10
|
+
* Profiles are fixture-backed: each base profile is declared here as a constant
|
|
11
|
+
* and is also pinned as a conformance fixture under `conformance/vector-cortex/v2/
|
|
12
|
+
* provider/` (registered by `scripts/gen-fixtures/provider.mjs`). The registry is
|
|
13
|
+
* the REAL resolver the acceptance suite drives; the fixtures are the committed
|
|
14
|
+
* canonical corpus that proves each profile's cache-identity behavior.
|
|
15
|
+
*
|
|
16
|
+
* Pi-agnostic, dependency-free, zero network (PREVENT-PI-004 / PREVENT-011).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
PRO_PROFILE_UNKNOWN,
|
|
21
|
+
type ProviderProfileBundle,
|
|
22
|
+
type ProviderProfileResult,
|
|
23
|
+
type ProviderProfileV1,
|
|
24
|
+
} from "./types.js";
|
|
25
|
+
|
|
26
|
+
/** A registry key is `provider` + `model`. Lookup is exact-match. */
|
|
27
|
+
function key(provider: string, model: string): string {
|
|
28
|
+
return `${provider}//${model}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The base role rule every VC5B profile shares: the renderer MUST use the host
|
|
33
|
+
* `before_agent_start` prepend seam and MUST NOT inject `role:"system"`
|
|
34
|
+
* (PREVENT-PI-003, critical).
|
|
35
|
+
*/
|
|
36
|
+
const BASE_ROLE = {
|
|
37
|
+
useHostPrependSeam: true,
|
|
38
|
+
forbidSystemRoleInjection: true,
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The base tool rule every VC5B profile shares: exact tool bytes preserved
|
|
43
|
+
* (PREVENT-PI-002) and the renderer never reorders away from the validator order.
|
|
44
|
+
*/
|
|
45
|
+
const BASE_TOOL = {
|
|
46
|
+
preserveExactToolBytes: true,
|
|
47
|
+
forbidToolReorder: true,
|
|
48
|
+
} as const;
|
|
49
|
+
|
|
50
|
+
/** Build a base cache rule (entire-canonical-request hashing by default). */
|
|
51
|
+
function baseCache(stableFields: readonly string[]): ProviderProfileBundle["cache"] {
|
|
52
|
+
return {
|
|
53
|
+
hashMode: "entire-canonical-request",
|
|
54
|
+
stableFields,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Build a contract-profile shape pinned as a conformance fixture too. */
|
|
59
|
+
function profile(
|
|
60
|
+
id: string,
|
|
61
|
+
version: string,
|
|
62
|
+
excludedJsonPointers: ProviderProfileV1["excludedJsonPointers"],
|
|
63
|
+
): ProviderProfileV1 {
|
|
64
|
+
return {
|
|
65
|
+
schema: "provider-profile-v1",
|
|
66
|
+
id,
|
|
67
|
+
version,
|
|
68
|
+
hashMode: "entire-canonical-request",
|
|
69
|
+
excludedJsonPointers,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The fixture-backed base profiles. Each entry is the REAL bundle the renderer
|
|
75
|
+
* resolves; the parallel conformance fixtures prove the cache-identity behavior.
|
|
76
|
+
*/
|
|
77
|
+
const BASE_PROFILES: readonly ProviderProfileBundle[] = [
|
|
78
|
+
{
|
|
79
|
+
profile: profile("anthropic-claude-opus", "v1", []),
|
|
80
|
+
role: BASE_ROLE,
|
|
81
|
+
tool: BASE_TOOL,
|
|
82
|
+
cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
profile: profile("anthropic-claude-sonnet", "v1", []),
|
|
86
|
+
role: BASE_ROLE,
|
|
87
|
+
tool: BASE_TOOL,
|
|
88
|
+
cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
profile: profile("openai-gpt", "v1", []),
|
|
92
|
+
role: BASE_ROLE,
|
|
93
|
+
tool: BASE_TOOL,
|
|
94
|
+
cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
// A profile that proves a fixture-excluded pointer: a provider whose
|
|
98
|
+
// request-id header cannot affect cache identity. Excluded + versioned.
|
|
99
|
+
profile: profile("google-gemini", "v1", [
|
|
100
|
+
{
|
|
101
|
+
pointer: "/requestId",
|
|
102
|
+
fixtureId: "PRO-EXCLUDE-010",
|
|
103
|
+
proofDigest: "sha256:excluded-request-id-proof",
|
|
104
|
+
},
|
|
105
|
+
]),
|
|
106
|
+
role: BASE_ROLE,
|
|
107
|
+
tool: BASE_TOOL,
|
|
108
|
+
cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const REGISTRY: ReadonlyMap<string, ProviderProfileBundle> = new Map(
|
|
113
|
+
BASE_PROFILES.map((b) => [key(b.profile.id, b.profile.version), b] as const),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Resolve a `(provider, model)` pair to a `ProviderProfileBundle`. Unknown
|
|
118
|
+
* provider / model / version → a CLEAN bypass (`PRO_PROFILE_UNKNOWN`), never an
|
|
119
|
+
* error. The renderer consults this and, on `ok:false`, selects triad C (the
|
|
120
|
+
* predecessor prompt path).
|
|
121
|
+
*/
|
|
122
|
+
export function resolveProviderProfile(
|
|
123
|
+
provider: string,
|
|
124
|
+
model: string,
|
|
125
|
+
): ProviderProfileResult {
|
|
126
|
+
const bundle = REGISTRY.get(key(provider, model));
|
|
127
|
+
if (bundle === undefined) {
|
|
128
|
+
return { ok: false, code: PRO_PROFILE_UNKNOWN };
|
|
129
|
+
}
|
|
130
|
+
return { ok: true, bundle };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** The fixture-backed base profiles (exposed for tests / dashboard introspection). */
|
|
134
|
+
export const BASE_PROVIDER_PROFILES: readonly ProviderProfileBundle[] = BASE_PROFILES;
|
|
135
|
+
|
|
136
|
+
/** The set of known `(provider, model)` keys, for conformance registration. */
|
|
137
|
+
export const KNOWN_PROVIDER_KEYS: readonly string[] = Array.from(REGISTRY.keys());
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/provider/types.ts — provider profile contract (VC5B, task 1).
|
|
3
|
+
*
|
|
4
|
+
* A `ProviderProfileV1` describes how a given provider/model caches and what the
|
|
5
|
+
* renderer may or may not fold into the outbound prompt without breaking the
|
|
6
|
+
* provider's KV-cache identity (CONTRACTS §ProviderProfileV1 + crystals).
|
|
7
|
+
*
|
|
8
|
+
* The renderer hashes the ENTIRE canonical outbound request by default
|
|
9
|
+
* (`hashMode: "entire-canonical-request"`). A profile may EXCLUDE a field only
|
|
10
|
+
* when a fixture proves that field cannot affect provider cache identity; every
|
|
11
|
+
* exclusion is versioned and listed in the manifest. Unknown provider / profile /
|
|
12
|
+
* version → the renderer bypasses vector-cortex rendering (a clean bypass, NOT an
|
|
13
|
+
* error) and the host falls back to the predecessor prompt path.
|
|
14
|
+
*
|
|
15
|
+
* Pure types + registered conformance IDs: no storage, no console, no network
|
|
16
|
+
* (PREVENT-PI-004 / PREVENT-011).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The hash mode the renderer uses for the canonical outbound request. The ONLY
|
|
21
|
+
* supported mode is hashing the entire request bytes — partial hashing is not
|
|
22
|
+
* allowed (it would let a folded field silently break cache identity).
|
|
23
|
+
*/
|
|
24
|
+
export type ProviderHashMode = "entire-canonical-request";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A JSON-pointer exclusion: a documented proof that the named pointer cannot
|
|
28
|
+
* affect provider cache identity. `fixtureId` pins the conformance row that
|
|
29
|
+
* proves it; `proofDigest` is that fixture's SHA-256. Exclusions are versioned
|
|
30
|
+
* per profile and MUST be listed in the manifest (CONTRACTS §ProviderProfileV1).
|
|
31
|
+
*/
|
|
32
|
+
export interface ProviderProfileExclusion {
|
|
33
|
+
readonly pointer: string;
|
|
34
|
+
readonly fixtureId: string;
|
|
35
|
+
readonly proofDigest: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The provider-profile contract. A renderer consumes a profile to decide how the
|
|
40
|
+
* rendered prompt is serialized into the canonical outbound request without
|
|
41
|
+
* invalidating the provider's cache identity.
|
|
42
|
+
*/
|
|
43
|
+
export interface ProviderProfileV1 {
|
|
44
|
+
readonly schema: "provider-profile-v1";
|
|
45
|
+
readonly id: string;
|
|
46
|
+
readonly version: string;
|
|
47
|
+
readonly hashMode: ProviderHashMode;
|
|
48
|
+
/** Versioned, fixture-proven exclusions (empty = hash the whole request). */
|
|
49
|
+
readonly excludedJsonPointers: readonly ProviderProfileExclusion[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Role rules: how the rendered context may be placed relative to the host system
|
|
54
|
+
* prompt. The renderer MUST use the host `before_agent_start` systemPrompt
|
|
55
|
+
* prepend seam — it NEVER injects compacted context as `role:"system"`
|
|
56
|
+
* (PREVENT-PI-003, critical).
|
|
57
|
+
*/
|
|
58
|
+
export interface ProviderRoleRule {
|
|
59
|
+
/** Always true for VC5B: placement is the host prepend seam, never role:system. */
|
|
60
|
+
readonly useHostPrependSeam: true;
|
|
61
|
+
/** A renderer MUST NOT emit a `role:"system"` message for compacted context. */
|
|
62
|
+
readonly forbidSystemRoleInjection: true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Tool rules: the renderer preserves EXACT tool bytes (PREVENT-PI-002 — never
|
|
67
|
+
* split a toolCall/toolResult pair). A profile declares whether the provider
|
|
68
|
+
* tolerates tool reordering; VC5B renders in the validator's stable order and
|
|
69
|
+
* never reorders, so this is a validation constraint, not a permission.
|
|
70
|
+
*/
|
|
71
|
+
export interface ProviderToolRule {
|
|
72
|
+
/** Exact tool bytes are preserved verbatim (no normalization/transcoding). */
|
|
73
|
+
readonly preserveExactToolBytes: true;
|
|
74
|
+
/** The renderer never reorders tools away from the validator's order. */
|
|
75
|
+
readonly forbidToolReorder: true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Cache rules: what the profile pins about provider cache identity so the
|
|
80
|
+
* renderer's canonical hash stays stable across otherwise-equivalent renders.
|
|
81
|
+
*/
|
|
82
|
+
export interface ProviderCacheRule {
|
|
83
|
+
/** The hash mode the renderer uses when this profile is active. */
|
|
84
|
+
readonly hashMode: ProviderHashMode;
|
|
85
|
+
/** Cache-stable fields the renderer must not reorder or drop. */
|
|
86
|
+
readonly stableFields: readonly string[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A fully-resolved provider profile bundle: the contract plus the role/tool/cache
|
|
91
|
+
* rules the renderer enforces. The fixture-backed base registry returns these.
|
|
92
|
+
*/
|
|
93
|
+
export interface ProviderProfileBundle {
|
|
94
|
+
readonly profile: ProviderProfileV1;
|
|
95
|
+
readonly role: ProviderRoleRule;
|
|
96
|
+
readonly tool: ProviderToolRule;
|
|
97
|
+
readonly cache: ProviderCacheRule;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Provider profile resolution failure codes. These are NOT errors that should
|
|
102
|
+
* break the agent loop — `PRO_PROFILE_UNKNOWN` is a clean bypass signal.
|
|
103
|
+
*/
|
|
104
|
+
export type ProviderProfileCode =
|
|
105
|
+
/** No matching profile for the provider/model; the renderer bypasses cleanly. */
|
|
106
|
+
| "PRO_PROFILE_UNKNOWN"
|
|
107
|
+
/** A profile matched but its version is unsupported by the renderer. */
|
|
108
|
+
| "PRO_PROFILE_VERSION_UNSUPPORTED";
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The provider-profile resolution verdict. `ok:true` carries the resolved
|
|
112
|
+
* bundle; `ok:false` carries a bypass code. Every `ok:false` is a CLEAN bypass
|
|
113
|
+
* (the host uses the predecessor prompt path) — never an error.
|
|
114
|
+
*/
|
|
115
|
+
export type ProviderProfileResult =
|
|
116
|
+
| { readonly ok: true; readonly bundle: ProviderProfileBundle }
|
|
117
|
+
| { readonly ok: false; readonly code: ProviderProfileCode };
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The sentinel returned for an unknown provider/model. The renderer sees this as
|
|
121
|
+
* a CLEAN bypass decision, not an error, and emits `vector_cortex_provider_bypassed`.
|
|
122
|
+
*/
|
|
123
|
+
export const PRO_PROFILE_UNKNOWN: ProviderProfileCode = "PRO_PROFILE_UNKNOWN";
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Registered PRO conformance ID range (PRO-001..015). The acceptance aggregator
|
|
127
|
+
* reads these rows from the v2 manifest and asserts each returns its manifest
|
|
128
|
+
* `ok`/`code` / pinned `requestDigest`.
|
|
129
|
+
*/
|
|
130
|
+
export const PRO_IDS: readonly string[] = Array.from(
|
|
131
|
+
{ length: 15 },
|
|
132
|
+
(_v, i) => `PRO-${String(i + 1).padStart(3, "0")}`,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
/** Named VC5B provider conformance assertions (the sprint's headline rows). */
|
|
136
|
+
export const PRO_NAMED_IDS = ["PRO-UNKNOWN-003"] as const;
|
|
137
|
+
|
|
138
|
+
/** Injected emit callback — same (event, fields) shape as the other VC seams. */
|
|
139
|
+
export type ProviderEmitter = (
|
|
140
|
+
event: string,
|
|
141
|
+
fields: Record<string, unknown>,
|
|
142
|
+
) => void;
|
|
143
|
+
|
|
144
|
+
/** The two structured events the VC5B provider seam emits. */
|
|
145
|
+
export type ProviderEventName =
|
|
146
|
+
| "vector_cortex_render_validated"
|
|
147
|
+
| "vector_cortex_provider_bypassed";
|
|
148
|
+
|
|
149
|
+
/** Typed, best-effort reporter bound to the two provider/render event names. */
|
|
150
|
+
export interface ProviderReporter {
|
|
151
|
+
readonly renderValidated: (fields: Record<string, unknown>) => void;
|
|
152
|
+
readonly providerBypassed: (fields: Record<string, unknown>) => void;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Aggregate-only provider/render metrics for the dashboard (counts only, never
|
|
157
|
+
* prompt text or node payloads).
|
|
158
|
+
*/
|
|
159
|
+
export interface ProviderMetricsV1 {
|
|
160
|
+
readonly rendersValidated: number;
|
|
161
|
+
readonly providersBypassed: number;
|
|
162
|
+
readonly nodesRendered: number;
|
|
163
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render/_acceptance-fixture.ts — conformance fixture I/O for VC5B render +
|
|
3
|
+
* provider acceptance rows.
|
|
4
|
+
*
|
|
5
|
+
* Reads the v2 conformance manifest + the per-sprint fixture files, and exposes
|
|
6
|
+
* the `RenderFx` / `ProviderFx` shapes the acceptance aggregator drives. No
|
|
7
|
+
* mocks/stubs: the fixtures are the committed canonical corpus.
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
10
|
+
import { join, dirname } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
|
|
14
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
|
|
16
|
+
function repoRoot(from: string): string {
|
|
17
|
+
let dir = from;
|
|
18
|
+
for (let i = 0; i < 8; i++) {
|
|
19
|
+
if (existsSync(join(dir, "conformance", "vector-cortex"))) return dir;
|
|
20
|
+
const next = dirname(dir);
|
|
21
|
+
if (next === dir) break;
|
|
22
|
+
dir = next;
|
|
23
|
+
}
|
|
24
|
+
throw new Error("conformance corpus not found above " + from);
|
|
25
|
+
}
|
|
26
|
+
export const REPO_ROOT = repoRoot(HERE);
|
|
27
|
+
export const V2 = join(REPO_ROOT, "conformance", "vector-cortex", "v2");
|
|
28
|
+
|
|
29
|
+
export interface ManifestRow {
|
|
30
|
+
id: string;
|
|
31
|
+
path: string;
|
|
32
|
+
algorithm: string;
|
|
33
|
+
expected: string;
|
|
34
|
+
}
|
|
35
|
+
export interface Manifest {
|
|
36
|
+
fixtures: ManifestRow[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function readManifest(): Manifest {
|
|
40
|
+
return JSON.parse(readFileSync(join(V2, "manifest.json"), "utf8")) as Manifest;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readFixture(id: string, prefix: "render" | "provider"): unknown {
|
|
44
|
+
const m = readManifest();
|
|
45
|
+
const row = m.fixtures.find((f) => f.id === id && f.path.startsWith(`${prefix}/`));
|
|
46
|
+
assert.ok(row, `fixture ${id} registered under ${prefix}/ in manifest`);
|
|
47
|
+
return JSON.parse(readFileSync(join(V2, row.path), "utf8"));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface RenderFxInput {
|
|
51
|
+
scenario: string;
|
|
52
|
+
graph: string;
|
|
53
|
+
profile: string;
|
|
54
|
+
mutateByteAfterRender?: boolean;
|
|
55
|
+
swapProfileAfterRender?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface RenderFxExpected {
|
|
58
|
+
ok: boolean;
|
|
59
|
+
code?: string;
|
|
60
|
+
nodeOrder?: string[];
|
|
61
|
+
orderReplay?: boolean;
|
|
62
|
+
permutationInvariant?: boolean;
|
|
63
|
+
toolBytesExact?: boolean;
|
|
64
|
+
invalidUtf8Survives?: boolean;
|
|
65
|
+
requestDigestStable?: boolean;
|
|
66
|
+
requestDigestSensitive?: boolean;
|
|
67
|
+
digestOrderIndependent?: boolean;
|
|
68
|
+
hashModeEntire?: boolean;
|
|
69
|
+
bypassClean?: boolean;
|
|
70
|
+
profileResolved?: boolean;
|
|
71
|
+
selectsTriadC?: boolean;
|
|
72
|
+
usesHostPrependSeam?: boolean;
|
|
73
|
+
forbidsSystemRole?: boolean;
|
|
74
|
+
}
|
|
75
|
+
export interface RenderFx {
|
|
76
|
+
id: string;
|
|
77
|
+
schema: string;
|
|
78
|
+
producer: string;
|
|
79
|
+
assertion: string;
|
|
80
|
+
kind: string;
|
|
81
|
+
input: RenderFxInput;
|
|
82
|
+
expected: RenderFxExpected;
|
|
83
|
+
}
|
|
84
|
+
export function renderFixture(id: string): RenderFx {
|
|
85
|
+
return readFixture(id, "render") as RenderFx;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface ProviderFxInput {
|
|
89
|
+
scenario: string;
|
|
90
|
+
provider: string;
|
|
91
|
+
model: string;
|
|
92
|
+
}
|
|
93
|
+
export interface ProviderFxExpected {
|
|
94
|
+
ok: boolean;
|
|
95
|
+
code?: string;
|
|
96
|
+
profileId?: string;
|
|
97
|
+
hashMode?: string;
|
|
98
|
+
excludedPointers?: string[];
|
|
99
|
+
bypassClean?: boolean;
|
|
100
|
+
cacheStable?: boolean;
|
|
101
|
+
deterministic?: boolean;
|
|
102
|
+
}
|
|
103
|
+
export interface ProviderFx {
|
|
104
|
+
id: string;
|
|
105
|
+
schema: string;
|
|
106
|
+
producer: string;
|
|
107
|
+
assertion: string;
|
|
108
|
+
kind: string;
|
|
109
|
+
input: ProviderFxInput;
|
|
110
|
+
expected: ProviderFxExpected;
|
|
111
|
+
}
|
|
112
|
+
export function providerFixture(id: string): ProviderFx {
|
|
113
|
+
return readFixture(id, "provider") as ProviderFx;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Flag-pinned wrapper: VC5B gated by MEGACOMPACT_VC5B (defaults ON). */
|
|
117
|
+
export function withFlagsOn(fn: () => void): () => void {
|
|
118
|
+
return (): void => {
|
|
119
|
+
const saved = process.env.MEGACOMPACT_VC5B;
|
|
120
|
+
process.env.MEGACOMPACT_VC5B = "1";
|
|
121
|
+
try {
|
|
122
|
+
fn();
|
|
123
|
+
} finally {
|
|
124
|
+
if (saved === undefined) delete process.env.MEGACOMPACT_VC5B;
|
|
125
|
+
else process.env.MEGACOMPACT_VC5B = saved;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render/_acceptance-helpers.ts — barrel re-export for the VC5B render + provider
|
|
3
|
+
* acceptance rows.
|
|
4
|
+
*
|
|
5
|
+
* The implementation is split across focused sibling files to keep each under
|
|
6
|
+
* the src/ 500-line hard limit; this file is the single import surface the
|
|
7
|
+
* acceptance aggregator uses:
|
|
8
|
+
* - `_acceptance-fixture.ts` — manifest I/O + RenderFx/ProviderFx shapes + withFlagsOn
|
|
9
|
+
* - `_acceptance-scenario.ts` — declarative graph materialization + runRenderScenario
|
|
10
|
+
* - `_acceptance-provider.ts` — provider-registry runner + KNOWN_KEYS
|
|
11
|
+
*
|
|
12
|
+
* Every function drives the REAL render / validate / provider logic — no mocks,
|
|
13
|
+
* no stubs.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
REPO_ROOT,
|
|
18
|
+
V2,
|
|
19
|
+
readManifest,
|
|
20
|
+
renderFixture,
|
|
21
|
+
providerFixture,
|
|
22
|
+
withFlagsOn,
|
|
23
|
+
} from "./_acceptance-fixture.js";
|
|
24
|
+
export type {
|
|
25
|
+
ManifestRow,
|
|
26
|
+
Manifest,
|
|
27
|
+
RenderFxInput,
|
|
28
|
+
RenderFxExpected,
|
|
29
|
+
RenderFx,
|
|
30
|
+
ProviderFxInput,
|
|
31
|
+
ProviderFxExpected,
|
|
32
|
+
ProviderFx,
|
|
33
|
+
} from "./_acceptance-fixture.js";
|
|
34
|
+
|
|
35
|
+
export { materializeGraph, runRenderScenario } from "./_acceptance-scenario.js";
|
|
36
|
+
export type { MaterializedGraph, RenderRunOutcome } from "./_acceptance-scenario.js";
|
|
37
|
+
|
|
38
|
+
export { runProviderScenario, KNOWN_KEYS } from "./_acceptance-provider.js";
|
|
39
|
+
export type { ProviderRunOutcome } from "./_acceptance-provider.js";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render/_acceptance-provider.ts — the REAL provider-registry runner for VC5B
|
|
3
|
+
* provider acceptance rows. Drives `resolveProviderProfile` (no mocks).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { resolveProviderProfile, KNOWN_PROVIDER_KEYS } from "../provider/registry.js";
|
|
7
|
+
import type { ProviderFx } from "./_acceptance-fixture.js";
|
|
8
|
+
|
|
9
|
+
export interface ProviderRunOutcome {
|
|
10
|
+
readonly ok: boolean;
|
|
11
|
+
readonly code?: string;
|
|
12
|
+
readonly profileId?: string;
|
|
13
|
+
readonly hashMode?: string;
|
|
14
|
+
readonly excludedPointers?: string[];
|
|
15
|
+
readonly bypassClean?: boolean;
|
|
16
|
+
readonly cacheStable?: boolean;
|
|
17
|
+
readonly deterministic?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Run a provider fixture through the REAL registry resolver. */
|
|
21
|
+
export function runProviderScenario(fx: ProviderFx): ProviderRunOutcome {
|
|
22
|
+
const r = resolveProviderProfile(fx.input.provider, fx.input.model);
|
|
23
|
+
if (!r.ok) {
|
|
24
|
+
return { ok: false, code: r.code, bypassClean: true };
|
|
25
|
+
}
|
|
26
|
+
const p = r.bundle.profile;
|
|
27
|
+
const cacheStable =
|
|
28
|
+
fx.input.scenario === "resolve-excluded-cache" &&
|
|
29
|
+
p.excludedJsonPointers.length === 1 &&
|
|
30
|
+
p.excludedJsonPointers[0].pointer === "/requestId";
|
|
31
|
+
// deterministic: a second identical lookup returns the same profile id.
|
|
32
|
+
const r2 = resolveProviderProfile(fx.input.provider, fx.input.model);
|
|
33
|
+
const deterministic = r2.ok && r2.bundle.profile.id === p.id;
|
|
34
|
+
return {
|
|
35
|
+
ok: true,
|
|
36
|
+
profileId: p.id,
|
|
37
|
+
hashMode: p.hashMode,
|
|
38
|
+
excludedPointers: p.excludedJsonPointers.map((e) => e.pointer),
|
|
39
|
+
cacheStable,
|
|
40
|
+
deterministic,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Exposed for the acceptance registration check. */
|
|
45
|
+
export const KNOWN_KEYS = KNOWN_PROVIDER_KEYS;
|