pi-mega-compact 0.20.64 → 0.20.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/config/vector-cortex-enc2b.js +36 -0
- package/dist/config/vector-cortex.js +2 -0
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
- package/dist/extensions/dashboard-server/routes-setup-enc2b.js +77 -0
- package/dist/extensions/dashboard-server/routes-setup.js +39 -3
- package/dist/extensions/mega-compact-s38.test/_helpers.js +2 -1
- package/dist/extensions/mega-compact.test/_helpers.js +4 -1
- package/dist/extensions/mega-events/agent-handlers/agentEndHandler.js +2 -2
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry-transient.js +131 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.js +13 -137
- package/dist/extensions/mega-events/compact-handlers.js +3 -3
- package/dist/extensions/mega-events/outage-advisor.js +5 -5
- package/dist/extensions/mega-events/send-safe.js +16 -0
- package/dist/src/config/vector-cortex-enc2b.js +36 -0
- package/dist/src/config/vector-cortex.js +2 -0
- package/dist/src/config.js +1 -1
- package/dist/src/vector-cortex/encoder/native-qualify-retest.js +225 -0
- package/dist/vector-cortex/encoder/native-qualify-retest.js +225 -0
- package/extensions/dashboard-client/dist/assets/{AdminTab-sjV8P5A4.js → AdminTab-B31efZ_R.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AdminTab-sjV8P5A4.js.map → AdminTab-B31efZ_R.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{AreaChart-C03yuld4.js → AreaChart-BpjHNUZm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-C03yuld4.js.map → AreaChart-BpjHNUZm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-B9jtJ7ud.js → BarChart-CY2FndXC.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-B9jtJ7ud.js.map → BarChart-CY2FndXC.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-B7Eh3qfp.js → CacheTab-kMSrMD3e.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-B7Eh3qfp.js.map → CacheTab-kMSrMD3e.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ConfigTab-DDTqosv2.js → ConfigTab-BrKpv2YG.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-DDTqosv2.js.map → ConfigTab-BrKpv2YG.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-BmyQGmUa.js → EventsTab-DaClreCm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-BmyQGmUa.js.map → EventsTab-DaClreCm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-DzPqNqpm.js → HealthTab-CxKQDqF3.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-DzPqNqpm.js.map → HealthTab-CxKQDqF3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-6g3d-M1p.js → MaintenanceTab-BKSlSjv0.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-6g3d-M1p.js.map → MaintenanceTab-BKSlSjv0.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Nk2yYPMS.js → MemoryMapTab-C2goWDt8.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Nk2yYPMS.js.map → MemoryMapTab-C2goWDt8.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsCards-BlIiuZp5.js → MetricsCards-DCPuCnrw.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsCards-BlIiuZp5.js.map → MetricsCards-DCPuCnrw.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-Bz0Mdyb_.js +2 -0
- package/extensions/dashboard-client/dist/assets/{MetricsTab-DfRDIAEU.js.map → MetricsTab-Bz0Mdyb_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-xkuFE5GV.js → OverviewTab-B-eZ-eDR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-xkuFE5GV.js.map → OverviewTab-B-eZ-eDR.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-C6qZmmgQ.js → ReposTab-W_NKRWpk.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-C6qZmmgQ.js.map → ReposTab-W_NKRWpk.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-Bkd_In3n.js → SessionsTab-GYrtNt4_.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-Bkd_In3n.js.map → SessionsTab-GYrtNt4_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-_1xa6eTJ.js +3 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-_1xa6eTJ.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Dr0KNUed.js → TimeSavedCard-Ciu8anVG.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Dr0KNUed.js.map → TimeSavedCard-Ciu8anVG.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnMemoryView-CjmSiRQ_.js → TurnMemoryView-BhXLIjWr.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnMemoryView-CjmSiRQ_.js.map → TurnMemoryView-BhXLIjWr.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/TurnsTab-C-f3DggL.js +2 -0
- package/extensions/dashboard-client/dist/assets/{TurnsTab-JsDstDDA.js.map → TurnsTab-C-f3DggL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-C96wfemg.js → VcStatusBadge-D97V04WE.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-C96wfemg.js.map → VcStatusBadge-D97V04WE.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{VectorCortexTab-DGV3kA7X.js → VectorCortexTab-cyDJPAPB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{VectorCortexTab-DGV3kA7X.js.map → VectorCortexTab-cyDJPAPB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{WikiTab-EFfy5v-D.js → WikiTab-dQ96DyjP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-EFfy5v-D.js.map → WikiTab-dQ96DyjP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-CvaPx9Bu.js → button-B1beLEiV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-CvaPx9Bu.js.map → button-B1beLEiV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-Crs5SwRQ.js → card-CK9KH_ob.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-Crs5SwRQ.js.map → card-CK9KH_ob.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{client-extra-Bd27WKSo.js → client-extra-gN7xjggX.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{client-extra-Bd27WKSo.js.map → client-extra-gN7xjggX.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-qnHql9Es.js → generateCategoricalChart-MB2_0u55.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-qnHql9Es.js.map → generateCategoricalChart-MB2_0u55.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-B4twCFnn.js → index-CKVd_Ym_.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-B4twCFnn.js.map → index-CKVd_Ym_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-BXutQI5d.js → switch-CswmYrO_.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-BXutQI5d.js.map → switch-CswmYrO_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-uE-OSaba.js → toggle-CMepFsf0.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-uE-OSaba.js.map → toggle-CMepFsf0.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-BRgPAqeS.js → useSSE-Bl55LQFZ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BRgPAqeS.js.map → useSSE-Bl55LQFZ.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/tabs/SetupTab/CortexRetestCard.tsx +104 -0
- package/extensions/dashboard-client/src/tabs/SetupTab/CortexSetup.tsx +2 -0
- package/extensions/dashboard-server/api-contracts/setup.ts +30 -0
- package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
- package/extensions/dashboard-server/routes-setup-enc2b.ts +82 -0
- package/extensions/dashboard-server/routes-setup.ts +39 -3
- package/extensions/mega-compact-s38.test/_helpers.ts +1 -1
- package/extensions/mega-compact.test/_helpers.ts +3 -1
- package/extensions/mega-events/agent-handlers/agentEndHandler.ts +2 -2
- package/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry-transient.ts +170 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.ts +12 -147
- package/extensions/mega-events/compact-handlers.ts +3 -3
- package/extensions/mega-events/outage-advisor.ts +5 -5
- package/extensions/mega-events/send-safe.ts +35 -4
- package/package.json +1 -1
- package/src/config/vector-cortex-enc2b.ts +38 -0
- package/src/config/vector-cortex.ts +2 -0
- package/src/config.ts +1 -0
- package/src/vector-cortex/encoder/native-qualify-retest.ts +264 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-DfRDIAEU.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-CEXL71VP.js +0 -3
- package/extensions/dashboard-client/dist/assets/SetupTab-CEXL71VP.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TurnsTab-JsDstDDA.js +0 -2
package/README.md
CHANGED
|
@@ -90,7 +90,7 @@ Set env vars before starting pi. Defaults are in `src/config/dedup.ts`.
|
|
|
90
90
|
| `MEGACOMPACT_WIKI_INCREMENTAL` | `true` | Incremental wiki updates (no full rebuild) |
|
|
91
91
|
| `MEGACOMPACT_AUTO_WIKI` | `true` | Auto-generate wiki from turns |
|
|
92
92
|
| `MEGACOMPACT_NEW_UI` | `true` | Use the new Tailwind/shadcn dashboard shell |
|
|
93
|
-
| `MEGACOMPACT_COST_API_ENABLED` | `false` | Opt-in: fetch model pricing from an external API (PREVENT-PI-004
|
|
93
|
+
| `MEGACOMPACT_COST_API_ENABLED` | `false` | Opt-in: fetch model pricing from an external API (PREVENT-PI-004 applies to defaults; opt-in features are exempt). Enriches dashboard cost data for models not in the local pricing table |
|
|
94
94
|
| `MEGACOMPACT_COST_API_URL` | _(unset)_ | OpenRouter-compatible model pricing endpoint (e.g. `https://openrouter.ai/api/v1/models`). Only contacted when `MEGACOMPACT_COST_API_ENABLED=true` |
|
|
95
95
|
|
|
96
96
|
Full config reference: [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config/vector-cortex-enc2b.ts — ENC-2b native onnxruntime qualification
|
|
3
|
+
* retest flag.
|
|
4
|
+
*
|
|
5
|
+
* Sibling of vector-cortex-enc2a.ts / vector-cortex-enc2budget.ts so
|
|
6
|
+
* vector-cortex.ts stays under the 300-line soft limit (soft-as-hard gate).
|
|
7
|
+
* vector-cortex.ts re-exports the flag below and root src/config.ts re-exports
|
|
8
|
+
* it, so no consumer import path changes.
|
|
9
|
+
*
|
|
10
|
+
* ENC-2b re-probes and re-qualifies the installed native onnxruntime binding
|
|
11
|
+
* (`onnxruntime-node` in `~/.pi/mega-compact/native-ort/`, operator-installed
|
|
12
|
+
* via the ENC-2a guide). When the operator installs the binding the read path
|
|
13
|
+
* does not automatically re-evaluate the qualification verdict; this sprint
|
|
14
|
+
* delivers a reader-only retest that loads the LOCAL on-disk binding, runs a
|
|
15
|
+
* bounded warmup + p95 probe against it, measures RSS, computes a fresh
|
|
16
|
+
* qualification verdict against the ENC-0f p95 budget + the operator
|
|
17
|
+
* install-budget, and surfaces it on the Setup Cortex sub-tab and the GET
|
|
18
|
+
* `/api/setup-status` response.
|
|
19
|
+
*
|
|
20
|
+
* `MEGACOMPACT_ENC_2B=0` disables: the GET omits both new retest fields, the
|
|
21
|
+
* POST ignores the retest-request key, the Cortex sub-tab renders no retest
|
|
22
|
+
* card — byte-identical to the ENC-2a predecessor. The flag MUST also be a
|
|
23
|
+
* dashboard SETTINGS toggle (visible in config UI, never in EXCLUDED_SETTINGS
|
|
24
|
+
* — see `feedback_dashboard-flags-toggleable` memory).
|
|
25
|
+
*
|
|
26
|
+
* Pi-agnostic, dependency-free (PREVENT-PI-004 / PREVENT-011).
|
|
27
|
+
*/
|
|
28
|
+
import { sprintFlag } from "./vector-cortex-flag.js";
|
|
29
|
+
/**
|
|
30
|
+
* ENC-2b — native onnxruntime qualification retest (detect installed binding,
|
|
31
|
+
* re-probe p95/RSS, re-qualify). Default ON. `MEGACOMPACT_ENC_2B=0` disables and
|
|
32
|
+
* is byte-identical to the ENC-2a predecessor: no retest GET fields, no retest
|
|
33
|
+
* POST branch, no retest card. This flag MUST also be a dashboard SETTINGS
|
|
34
|
+
* toggle (visible in config UI, never in EXCLUDED_SETTINGS).
|
|
35
|
+
*/
|
|
36
|
+
export const ENC_2B_ENABLED = () => sprintFlag("MEGACOMPACT_ENC_2B");
|
|
@@ -52,5 +52,7 @@ export { ENC_1B_ENABLED, ENC_1B_MAX_EMBEDDING_DIM, ENC_1B_EMBEDDING_DIM_ENV, ENC
|
|
|
52
52
|
export { ENC_2BUDGET_ENABLED, ENC_2BUDGET_NATIVE_ORT_BUDGET_ENV, ENC_2BUDGET_MAX_MIB, ENC_2BUDGET_DEFAULT_MIB, } from "./vector-cortex-enc2budget.js";
|
|
53
53
|
// ENC-2a native onnxruntime install-guide flag extracted to vector-cortex-enc2a.ts.
|
|
54
54
|
export { ENC_2A_ENABLED } from "./vector-cortex-enc2a.js";
|
|
55
|
+
// ENC-2b native onnxruntime qualification-retest flag extracted to vector-cortex-enc2b.ts.
|
|
56
|
+
export { ENC_2B_ENABLED } from "./vector-cortex-enc2b.js";
|
|
55
57
|
// Breaker constants (TRIAD_RESILIENCE.md §breaker) extracted to vector-cortex-breakers.ts.
|
|
56
58
|
export { BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./vector-cortex-breakers.js";
|
package/dist/config.js
CHANGED
|
@@ -114,4 +114,4 @@ export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
|
|
|
114
114
|
// default ON, `=0`/`_DISABLED` off. Re-exported from src/config/vector-cortex.ts
|
|
115
115
|
// so root consumers share one source of truth.
|
|
116
116
|
// ---------------------------------------------------------------------------
|
|
117
|
-
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, VC6B_ENABLED, VC6C_ENABLED, VC7A_ENABLED, VC7B_ENABLED, VC7C_ENABLED, VC8A_ENABLED, VC8B_ENABLED, VC8C_ENABLED, VC9A_ENABLED, VC9B_ENABLED, VC9C_ENABLED, VC9D_ENABLED, PCC_ENABLED, ML5A_ENABLED, ML5B_ENABLED, ML5C_ENABLED, ML5D_ENABLED, ML5E_ENABLED, DEDUP_ATTR_ENABLED, ENC_0A_ENABLED, ENC_0B_ENABLED, ENC_0C_ENABLED, ENC_0D_ENABLED, ENC_0E_ENABLED, ENC_0F_ENABLED, ENC_0G_ENABLED, ENC_1A_ENABLED, ENC_1B_ENABLED, ENC_2BUDGET_ENABLED, ENC_2BUDGET_NATIVE_ORT_BUDGET_ENV, ENC_2BUDGET_MAX_MIB, ENC_2BUDGET_DEFAULT_MIB, ENC_2A_ENABLED, COSINE_FP_BENCH_ENABLED, COSINE_FP_REAL_ENABLED, REPO_CORPUS_ENABLED, DASH_0A_ENABLED, DASH_0B_ENABLED, DASH_0C_ENABLED, DASH_0D_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
|
117
|
+
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, VC6B_ENABLED, VC6C_ENABLED, VC7A_ENABLED, VC7B_ENABLED, VC7C_ENABLED, VC8A_ENABLED, VC8B_ENABLED, VC8C_ENABLED, VC9A_ENABLED, VC9B_ENABLED, VC9C_ENABLED, VC9D_ENABLED, PCC_ENABLED, ML5A_ENABLED, ML5B_ENABLED, ML5C_ENABLED, ML5D_ENABLED, ML5E_ENABLED, DEDUP_ATTR_ENABLED, ENC_0A_ENABLED, ENC_0B_ENABLED, ENC_0C_ENABLED, ENC_0D_ENABLED, ENC_0E_ENABLED, ENC_0F_ENABLED, ENC_0G_ENABLED, ENC_1A_ENABLED, ENC_1B_ENABLED, ENC_2BUDGET_ENABLED, ENC_2BUDGET_NATIVE_ORT_BUDGET_ENV, ENC_2BUDGET_MAX_MIB, ENC_2BUDGET_DEFAULT_MIB, ENC_2A_ENABLED, ENC_2B_ENABLED, COSINE_FP_BENCH_ENABLED, COSINE_FP_REAL_ENABLED, REPO_CORPUS_ENABLED, DASH_0A_ENABLED, DASH_0B_ENABLED, DASH_0C_ENABLED, DASH_0D_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
|
@@ -71,6 +71,7 @@ export const VECTOR_CORTEX_SETTINGS = {
|
|
|
71
71
|
boolDirect("MEGACOMPACT_ENC_1B", "ENC-1b ONNX runtime backend + embedder API settings", "ENC-1b completes the embedder API Settings surface (dim / headers / allow-remote persisted to the per-repo .mega-compact.env) and surfaces the ONNX runtime selection knob (MEGACOMPACT_ENCODER_NATIVE) as a toggle plus the current effective backend + demotion reason. OFF = no new GET fields, no writer branch, no new Settings rows or toggle (byte-identical ENC-1a predecessor).", true),
|
|
72
72
|
boolDirect("MEGACOMPACT_ENC_2BUDGET", "ENC-2a operator-configurable native install-budget knob", "ENC-2a exposes the runtime operator knob MEGACOMPACT_NATIVE_ORT_BUDGET_MIB (default 300 MiB, clamp 8192) as a dashboard Settings field. The knob gates budgetOk in the encoder runtime decision (decision.ts installBudgetMib + runtime-select.ts selectRuntimeBackend). Persisted to the per-repo .mega-compact.env via the setup-configure writer branch; the GET status route surfaces the persisted value AND the effective integer installBudgetMib() resolves to (so the dashboard shows the runtime's actual operand). OFF = no new GET fields, no writer branch, no new Settings rows (byte-identical ENC-1b predecessor).", true),
|
|
73
73
|
boolDirect("MEGACOMPACT_ENC_2A", "ENC-2a native onnxruntime install GUIDE (detect + copy-paste)", "ENC-2a surfaces the platform-matched native onnxruntime install-guide (three copy-paste steps + script path) on the Setup Cortex sub-tab when the operator opted into the native backend but the effective runtime is still wasm (onnxruntime-node absent). Pure guidance — the extension NEVER executes or fetches (PREVENT-PI-004 unchanged); the committed operator script scripts/encoder/install-native-ort.mjs runs in the USER's shell. OFF = no guide GET fields, no guide-request POST branch, no install card (byte-identical ENC-1b predecessor).", true),
|
|
74
|
+
boolDirect("MEGACOMPACT_ENC_2B", "ENC-2b native ONNX runtime qualification retest (detect + re-qualify)", "ENC-2b re-probes and re-qualifies the installed native onnxruntime binding (onnxruntime-node in ~/.pi/mega-compact/native-ort/, operator-installed via the ENC-2a guide): loads the LOCAL on-disk binding, runs a bounded warmup + p95 probe, measures RSS, and computes a fresh qualification verdict against the ENC-0f p95 budget + the operator install-budget. Never downloads or trains (PREVENT-PI-004 / HG-1 unchanged); surfaces nativeOrtRetestResult + nativeOrtBackendEffective on the GET /api/setup-status. OFF = no retest GET fields, no retest POST branch, no retest card (byte-identical ENC-2a predecessor).", true),
|
|
74
75
|
boolDirect("MEGACOMPACT_COSINE_FP_BENCH", "COS-FP-A Synthetic FP Bench", "Synthetic FP harness + L2 cosine threshold calibration (COS-FP-A): drives the deterministic synthetic-corpus grid sweep (scripts/cosine-fp/bench.mjs) that measures L2 cosine false-positive rates and emits a recommended default, and serves the reader-only GET /api/cosine-fp-report. Synthetic-corpus-only — never reads real session/ledger bytes. OFF = bench inert, endpoint 404s, no report write, L2_COSINE plain MEGACOMPACT_L2_THRESHOLD (byte-identical predecessor).", true),
|
|
75
76
|
boolDirect("MEGACOMPACT_COSINE_FP_REAL", "COS-FP-R Real-Corpus Validation", "Real-corpus L2 cosine FP validation (COS-FP-R): runs the real-corpus grid sweep (scripts/cosine-fp/real-bench.mjs) against donated, consent-approved sessions only (SECURITY_PRIVACY Lifecycle/Consent), scoring FP/FN with Wilson intervals + session-grouped bootstrap, and appends CI-backed per-content-type recommendations to the cosine-threshold report. Runs only when a valid consented corpus exists; absent corpus = no_corpus (normative pre-donation state, nothing written). OFF = script inert, nothing executes, no writes (byte-identical predecessor).", true),
|
|
76
77
|
boolDirect("MEGACOMPACT_REPO_CORPUS", "Cross-Repo Corpus (REPO-A)", "Cross-repo corpus preparation (REPO-A): the governed, consent-gated reader-only path that builds a pseudonymous corpus manifest from per-repo events.log slices and serves GET /api/repo-corpus answering which pseudonymous repos/sessions are in the corpus, what cross-repo overlap exists, and whether consent is active for each — counts + IDs + status only, never payload content. Consent is append-only (consent.mjs, CLI/ops-only); revocation freezes the builder + route. OFF = builder refuses to run, reader route 404s, single-repo recall untouched (byte-identical predecessor).", true),
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-server/routes-setup-enc2b.ts — ENC-2b native onnxruntime
|
|
3
|
+
* qualification retest (reader-only probe) + POST retest action.
|
|
4
|
+
*
|
|
5
|
+
* Extracted out of routes-setup.ts (which hovers at the 300-line source soft
|
|
6
|
+
* cap, mirroring the ENC-1b/ENC-2a sibling extracts) so the ENC-2b additive
|
|
7
|
+
* GET field — the native qualification retest result + effective backend —
|
|
8
|
+
* lives in a sibling impl file and routes-setup.ts stays a thin host.
|
|
9
|
+
*
|
|
10
|
+
* The retest's job is RE-QUALIFICATION: when the operator installed
|
|
11
|
+
* onnxruntime-node via the ENC-2a guide, the encoder read path does not
|
|
12
|
+
* automatically re-evaluate the qualification verdict. This sibling surface
|
|
13
|
+
* runs `runNativeRetest` (a bounded, reader-only, local probe) and:
|
|
14
|
+
*
|
|
15
|
+
* - surfaces `nativeOrtRetestResult` + `nativeOrtBackendEffective` on the GET
|
|
16
|
+
* when the flag is on AND a binding is installed — absent otherwise
|
|
17
|
+
* (byte-identical to the ENC-2a-era shape on flag-off/none);
|
|
18
|
+
* - handles the POST `nativeOrtRetest` key (`false` → 400
|
|
19
|
+
* `retest_rejected_false_nothing_to_do`; `true` → run + echo the fresh
|
|
20
|
+
* result; flag-off → the key is unrecognized, byte-identical predecessor).
|
|
21
|
+
*
|
|
22
|
+
* `nativeOrtBackendEffective` is `"native"` only when the retest verdict is
|
|
23
|
+
* `qualified`; any `degraded`/`failed` verdict keeps the backend `"wasm"` — the
|
|
24
|
+
* runtime never silently switches to a backend whose retest failed.
|
|
25
|
+
*
|
|
26
|
+
* Guardrails: PREVENT-PI-004 (local filesystem + in-process binding load only,
|
|
27
|
+
* zero network — the retest NEVER fetches), PREVENT-011 (no `any`).
|
|
28
|
+
*/
|
|
29
|
+
import { ENC_2B_ENABLED } from "../../src/config/vector-cortex.js";
|
|
30
|
+
import { runNativeRetest, } from "../../src/vector-cortex/encoder/native-qualify-retest.js";
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the ENC-2b additive GET fields. Returns BOTH fields when the flag is
|
|
33
|
+
* on AND a native binding is installed (the retest ran); returns `{}` on
|
|
34
|
+
* flag-off AND when no binding is installed — both fields are OMITTED (absent,
|
|
35
|
+
* not null), so the GET stays byte-identical to the ENC-2a-era shape and the
|
|
36
|
+
* client hides the retest card. Flag-off omits, never nulls: the ENC-2a-era
|
|
37
|
+
* host spreads `...{}` so no new key appears. Mirror of the ENC-2a/ENC-1b
|
|
38
|
+
* sibling idiom; async because the retest loads the binding in-process.
|
|
39
|
+
*/
|
|
40
|
+
export async function readEnc2bRetest(stateDir) {
|
|
41
|
+
if (!ENC_2B_ENABLED())
|
|
42
|
+
return {};
|
|
43
|
+
const result = await runNativeRetest(stateDir);
|
|
44
|
+
if (result === null)
|
|
45
|
+
return {};
|
|
46
|
+
return {
|
|
47
|
+
nativeOrtRetestResult: result,
|
|
48
|
+
nativeOrtBackendEffective: result.verdict === "qualified" ? "native" : "wasm",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* ENC-2b POST retest-action key handling (flag-gated, additive).
|
|
53
|
+
*
|
|
54
|
+
* `true` → { action: "run" } (the host runs the retest and returns the result)
|
|
55
|
+
* `false` → { action: "reject" } (400 `retest_rejected_false_nothing_to_do`)
|
|
56
|
+
* other → null (key absent or flag off — falls through unrecognized,
|
|
57
|
+
* byte-identical ENC-2a-era predecessor).
|
|
58
|
+
*/
|
|
59
|
+
export function enc2bRetestRequest(body) {
|
|
60
|
+
if (!ENC_2B_ENABLED())
|
|
61
|
+
return null;
|
|
62
|
+
if (body.nativeOrtRetest === true)
|
|
63
|
+
return "run";
|
|
64
|
+
if (body.nativeOrtRetest === false)
|
|
65
|
+
return "reject";
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Run the ENC-2b retest now (bounded, synchronous on the request) and return
|
|
70
|
+
* the fresh result. Flag-off or no binding → null. Used by the POST "run"
|
|
71
|
+
* branch; the result is returned on the response body, never persisted.
|
|
72
|
+
*/
|
|
73
|
+
export async function runEnc2bRetest(stateDir) {
|
|
74
|
+
if (!ENC_2B_ENABLED())
|
|
75
|
+
return null;
|
|
76
|
+
return runNativeRetest(stateDir);
|
|
77
|
+
}
|
|
@@ -15,6 +15,7 @@ import { enc1aStatusFields, writeEnc1aEnv, tryEnc1aConfigure, wantsEnc1a } from
|
|
|
15
15
|
import { enc1bStatusFields, tryEnc1bConfigure, tryEnc1bInto, enc1bValidateCombined } from "./routes-setup-enc1b.js";
|
|
16
16
|
import { enc2BudgetStatusFields, tryEnc2BudgetInto, enc2BudgetValidateCombined, } from "./routes-setup-enc2budget.js";
|
|
17
17
|
import { readEnc2aGuide, enc2aGuideRequest } from "./routes-setup-enc2a.js";
|
|
18
|
+
import { readEnc2bRetest, enc2bRetestRequest, runEnc2bRetest } from "./routes-setup-enc2b.js";
|
|
18
19
|
import { writeEmbedderEnv } from "./routes-setup-env-upsert.js";
|
|
19
20
|
import { detectOllama, detectLlamaCpp, detectOnnx, memoizedDetectOllama, memoizedDetectLlamaCpp, memoizedDetectOnnx, } from "./routes-setup-detect-cache.js";
|
|
20
21
|
// ---------------------------------------------------------------------------
|
|
@@ -69,7 +70,7 @@ export function handleSetupStatus(req, res, ctx) {
|
|
|
69
70
|
// ENC-1a (flag-gated, additive): echo the persisted endpoint URL and an
|
|
70
71
|
// embeddingApiKeySet boolean ONLY — the raw API key is never returned. When
|
|
71
72
|
// the flag is off (or neither is set) both fields are simply omitted.
|
|
72
|
-
const
|
|
73
|
+
const base = {
|
|
73
74
|
currentEmbedder: active,
|
|
74
75
|
configuredEmbedder: configured.embedder,
|
|
75
76
|
configuredUrl: configured.url,
|
|
@@ -82,9 +83,20 @@ export function handleSetupStatus(req, res, ctx) {
|
|
|
82
83
|
...(enc2a.guide !== null ? { nativeOrtInstallGuide: enc2a.guide } : {}),
|
|
83
84
|
...(enc2a.installedVersion !== null ? { nativeOrtInstalledVersion: enc2a.installedVersion } : {}),
|
|
84
85
|
};
|
|
86
|
+
// ENC-2b (flag-gated, additive): the native qualification retest fields. The
|
|
87
|
+
// retest loads the on-disk binding in-process (bounded, never a fetch), so
|
|
88
|
+
// readEnc2bRetest is async; the handler returns true synchronously for the
|
|
89
|
+
// dispatch and writes the response after the await. Flag-off/none mutates
|
|
90
|
+
// the body additively and stays byte-identical to the ENC-2a-era shape.
|
|
85
91
|
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
readEnc2bRetest(ctx.stateDir).then((enc2b) => {
|
|
93
|
+
const body = { ...base, ...enc2b };
|
|
94
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
95
|
+
res.end(JSON.stringify(body));
|
|
96
|
+
}).catch(() => {
|
|
97
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
98
|
+
res.end(JSON.stringify(base));
|
|
99
|
+
});
|
|
88
100
|
return true;
|
|
89
101
|
}
|
|
90
102
|
// ---------------------------------------------------------------------------
|
|
@@ -204,6 +216,30 @@ export function handleSetupConfigure(req, res, ctx) {
|
|
|
204
216
|
res.end(JSON.stringify({ nativeOrtInstallGuide: readEnc2aGuide(ctx.stateDir).guide }));
|
|
205
217
|
return;
|
|
206
218
|
}
|
|
219
|
+
// ENC-2b retest-action key (flag-gated, additive): `false` → 400 (an
|
|
220
|
+
// operator-driven no-op — nothing to retest), `true` → run the bounded
|
|
221
|
+
// retest synchronously and return the fresh result. Flag-off: the key
|
|
222
|
+
// falls through unrecognized (byte-identical ENC-2a-era predecessor).
|
|
223
|
+
// The retest never fetches — the binding is on disk from the ENC-2a
|
|
224
|
+
// install (PREVENT-PI-004 clean).
|
|
225
|
+
const retestReq = enc2bRetestRequest(body);
|
|
226
|
+
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
227
|
+
if (retestReq === "reject") {
|
|
228
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
229
|
+
res.end(JSON.stringify({ error: "retest_rejected_false_nothing_to_do" }));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (retestReq === "run") {
|
|
233
|
+
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
234
|
+
runEnc2bRetest(ctx.stateDir).then((fresh) => {
|
|
235
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
236
|
+
res.end(JSON.stringify({ nativeOrtRetestResult: fresh }));
|
|
237
|
+
}).catch(() => {
|
|
238
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
239
|
+
res.end(JSON.stringify({ error: "retest_failed" }));
|
|
240
|
+
});
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
207
243
|
if (tryEnc1aConfigure(body, res, ctx))
|
|
208
244
|
return;
|
|
209
245
|
if (tryEnc1bConfigure(body, res, ctx))
|
|
@@ -131,7 +131,8 @@ export function harness(opts = {}) {
|
|
|
131
131
|
registerCommand: () => { }, registerTool: () => { }, registerShortcut: () => { },
|
|
132
132
|
registerFlag: () => { }, getFlag: () => undefined,
|
|
133
133
|
registerMessageRenderer: () => { }, registerEntryRenderer: () => { },
|
|
134
|
-
sendMessage: () => {
|
|
134
|
+
sendMessage: (m) => { if (typeof m?.content === "string")
|
|
135
|
+
sendUserMessages.push(m.content); }, sendUserMessage: (m) => { sendUserMessages.push(m); },
|
|
135
136
|
appendEntry: (t, d) => appended.push({ t, d }),
|
|
136
137
|
setSessionName: () => { }, getSessionName: () => undefined, setLabel: () => { },
|
|
137
138
|
exec: async () => ({ stdout: "", stderr: "", code: 0 }),
|
|
@@ -186,7 +186,10 @@ export function harness(opts = {}) {
|
|
|
186
186
|
getFlag: () => undefined,
|
|
187
187
|
registerMessageRenderer: () => { },
|
|
188
188
|
registerEntryRenderer: () => { },
|
|
189
|
-
sendMessage: (
|
|
189
|
+
sendMessage: (m) => {
|
|
190
|
+
if (typeof m?.content === "string")
|
|
191
|
+
sendUserMessages.push(m.content);
|
|
192
|
+
},
|
|
190
193
|
sendUserMessage: (m) => {
|
|
191
194
|
sendUserMessages.push(m);
|
|
192
195
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { piCompactWouldNoop } from "../../mega-pipeline.js";
|
|
2
|
-
import {
|
|
2
|
+
import { safeSendInvisibleMessage } from "../send-safe.js";
|
|
3
3
|
/** Handle the `agent_end` pi event. Non-fatal end-to-end. */
|
|
4
4
|
export async function handleAgentEnd(ctx, pi, runtime, config) {
|
|
5
5
|
runtime.activeAgents = Math.max(0, runtime.activeAgents - 1);
|
|
@@ -200,7 +200,7 @@ export async function handleAgentEnd(ctx, pi, runtime, config) {
|
|
|
200
200
|
const nudgeMsg = lengthStop && !didDurableTrim
|
|
201
201
|
? "[mega-compact] the last response hit the output-token cap; continue from where it stopped."
|
|
202
202
|
: "[mega-compact] continue from the compacted context above.";
|
|
203
|
-
await
|
|
203
|
+
await safeSendInvisibleMessage(pi, nudgeMsg);
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
catch {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { errorRetryBackoffMs } from "../../error-classifier.js";
|
|
2
|
+
import { safeSendInvisibleMessage } from "../../send-safe.js";
|
|
3
|
+
import { maybeSendProviderOutageAdvisory } from "../../outage-advisor.js";
|
|
4
|
+
/** Handle the transient or permanent error-retry path. Non-fatal. */
|
|
5
|
+
export async function transientRetry(event, _ctx, pi, runtime, config, tc) {
|
|
6
|
+
const { effectiveCategory, detail, errSig } = tc;
|
|
7
|
+
// S38.7: hard-stop switch — bypass ALL retry logic when set.
|
|
8
|
+
if (config.errorRetryHardStop) {
|
|
9
|
+
runtime.rt.errorRetryCount = 0;
|
|
10
|
+
runtime.dashboard.event("error_retry_disabled", {
|
|
11
|
+
category: effectiveCategory,
|
|
12
|
+
turnIndex: event.turnIndex,
|
|
13
|
+
reason: "hard-stop",
|
|
14
|
+
});
|
|
15
|
+
return; // early exit — no retry
|
|
16
|
+
}
|
|
17
|
+
// S38.6: circuit-breaker — stop retrying after too many consecutive errors.
|
|
18
|
+
runtime.rt.consecutiveErrors++;
|
|
19
|
+
// R10: send calm "provider outage" advisory once per episode.
|
|
20
|
+
await maybeSendProviderOutageAdvisory(effectiveCategory, runtime, pi, config, { signal: detail.signal, rawText: (errSig || '').slice(0, 500) });
|
|
21
|
+
if (runtime.rt.consecutiveErrors > config.maxConsecutiveErrors) {
|
|
22
|
+
runtime.dashboard.event("error_retry_circuit_open", {
|
|
23
|
+
consecutive: runtime.rt.consecutiveErrors,
|
|
24
|
+
max: config.maxConsecutiveErrors,
|
|
25
|
+
turnIndex: event.turnIndex,
|
|
26
|
+
});
|
|
27
|
+
runtime.logger.warn("error-retry-circuit-open", {
|
|
28
|
+
sessionId: runtime.rt.sessionId,
|
|
29
|
+
consecutive: runtime.rt.consecutiveErrors,
|
|
30
|
+
max: config.maxConsecutiveErrors,
|
|
31
|
+
});
|
|
32
|
+
return; // early exit — circuit breaker tripped
|
|
33
|
+
}
|
|
34
|
+
const max = effectiveCategory === "transient"
|
|
35
|
+
? config.autoRetryTransientMax
|
|
36
|
+
: config.autoRetryPermanentMax;
|
|
37
|
+
// max === 0 disables the category entirely (revert to S28-only).
|
|
38
|
+
if (max <= 0) {
|
|
39
|
+
runtime.rt.errorRetryCount = 0;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
runtime.rt.errorRetryCount++;
|
|
43
|
+
if (runtime.rt.errorRetryCount > max) {
|
|
44
|
+
// Exhausted — surface the error, reset for the next burst.
|
|
45
|
+
runtime.dashboard.event("error_retry_exhausted", {
|
|
46
|
+
category: effectiveCategory,
|
|
47
|
+
count: runtime.rt.errorRetryCount,
|
|
48
|
+
max,
|
|
49
|
+
turnIndex: event.turnIndex,
|
|
50
|
+
});
|
|
51
|
+
runtime.logger.info("error-retry-exhausted", {
|
|
52
|
+
sessionId: runtime.rt.sessionId,
|
|
53
|
+
category: effectiveCategory,
|
|
54
|
+
count: runtime.rt.errorRetryCount,
|
|
55
|
+
max,
|
|
56
|
+
});
|
|
57
|
+
runtime.rt.errorRetryCount = 0;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// R2: session-global cap — total S38 nudges per session across ALL bursts.
|
|
61
|
+
// Independent of the per-burst max and the circuit breaker. Hitting it is
|
|
62
|
+
// terminal for the session: log + dashboard event, stop nudging. `0` disables.
|
|
63
|
+
if (config.errorRetrySessionMax > 0 &&
|
|
64
|
+
runtime.rt.errorRetrySessionCount >= config.errorRetrySessionMax) {
|
|
65
|
+
runtime.dashboard.event("error_retry_session_exhausted", {
|
|
66
|
+
count: runtime.rt.errorRetrySessionCount,
|
|
67
|
+
max: config.errorRetrySessionMax,
|
|
68
|
+
category: effectiveCategory,
|
|
69
|
+
turnIndex: event.turnIndex,
|
|
70
|
+
});
|
|
71
|
+
runtime.logger.warn("error-retry-session-exhausted", {
|
|
72
|
+
sessionId: runtime.rt.sessionId,
|
|
73
|
+
count: runtime.rt.errorRetrySessionCount,
|
|
74
|
+
max: config.errorRetrySessionMax,
|
|
75
|
+
category: effectiveCategory,
|
|
76
|
+
});
|
|
77
|
+
runtime.rt.errorRetryCount = 0;
|
|
78
|
+
return; // terminal for the session — no nudge
|
|
79
|
+
}
|
|
80
|
+
// R1: in-flight nudge dedup — a nudge queued via deliverAs:'followUp' must
|
|
81
|
+
// not be re-sent until consumed by an actual new agent turn (turn_start
|
|
82
|
+
// resets retryNudgePending). Without this, a fast-erroring provider + a
|
|
83
|
+
// per-turn nudge → N nudges queue up and pi dispatches N retry turns, each
|
|
84
|
+
// re-submitting the same failing prompt (the 2026-07-28 incident).
|
|
85
|
+
if (runtime.rt.retryNudgePending) {
|
|
86
|
+
runtime.dashboard.event("error_retry_dedup_skip", {
|
|
87
|
+
category: effectiveCategory,
|
|
88
|
+
count: runtime.rt.errorRetryCount,
|
|
89
|
+
max,
|
|
90
|
+
turnIndex: event.turnIndex,
|
|
91
|
+
});
|
|
92
|
+
return; // pending nudge not yet consumed — skip
|
|
93
|
+
}
|
|
94
|
+
// R1: gating backoff — errorRetryUntil is GATING. A nudge cannot fire before
|
|
95
|
+
// the previous backoff elapses. Paces retries (5s/10s/20s/30s default).
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
if (now < runtime.rt.errorRetryUntil) {
|
|
98
|
+
runtime.dashboard.event("error_retry_backoff_skip", {
|
|
99
|
+
category: effectiveCategory,
|
|
100
|
+
count: runtime.rt.errorRetryCount,
|
|
101
|
+
max,
|
|
102
|
+
turnIndex: event.turnIndex,
|
|
103
|
+
});
|
|
104
|
+
return; // backoff not elapsed — skip
|
|
105
|
+
}
|
|
106
|
+
// Fire the retry nudge. Set pending (R1 dedup) + backoff (R1 pacing) +
|
|
107
|
+
// session count (R2 cap) BEFORE the await so a re-entrant turn_end during
|
|
108
|
+
// the send can't double-fire.
|
|
109
|
+
runtime.rt.retryNudgePending = true;
|
|
110
|
+
runtime.rt.lastErrorRetryAt = now;
|
|
111
|
+
runtime.rt.errorRetryUntil =
|
|
112
|
+
now +
|
|
113
|
+
errorRetryBackoffMs(runtime.rt.errorRetryCount, config.errorRetryBackoffMs);
|
|
114
|
+
runtime.rt.errorRetrySessionCount++;
|
|
115
|
+
runtime.dashboard.event("error_retry", {
|
|
116
|
+
category: effectiveCategory,
|
|
117
|
+
count: runtime.rt.errorRetryCount,
|
|
118
|
+
max,
|
|
119
|
+
turnIndex: event.turnIndex,
|
|
120
|
+
});
|
|
121
|
+
runtime.logger.info("error-retry", {
|
|
122
|
+
sessionId: runtime.rt.sessionId,
|
|
123
|
+
category: effectiveCategory,
|
|
124
|
+
count: runtime.rt.errorRetryCount,
|
|
125
|
+
max,
|
|
126
|
+
});
|
|
127
|
+
// PREVENT-PI-003: user-role only (queued + catch-guarded).
|
|
128
|
+
// Invisible: display:false so the retry trigger fires without
|
|
129
|
+
// rendering "Follow-up:" spam in the conversation UI.
|
|
130
|
+
await safeSendInvisibleMessage(pi, "[mega-compact] the last turn ended with an error; please retry.");
|
|
131
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { piCompactWouldNoop } from "../../../mega-pipeline.js";
|
|
2
|
-
import { classifyError, classifyErrorDetailed,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { classifyError, classifyErrorDetailed, extractErrorSignature, isKnownRetryableTransient, } from "../../error-classifier.js";
|
|
3
|
+
import { safeSendInvisibleMessage } from "../../send-safe.js";
|
|
4
|
+
import { transientRetry } from "./errorRetry-transient.js";
|
|
5
5
|
/** S38: broader error-retry safety net. Non-fatal end-to-end. */
|
|
6
6
|
export async function errorRetry(event, ctx, pi, runtime, config) {
|
|
7
7
|
// S38: broader error-retry safety net. S28 only catches stopReason==='length';
|
|
@@ -209,7 +209,10 @@ export async function errorRetry(event, ctx, pi, runtime, config) {
|
|
|
209
209
|
// Default ON (advisoryChannel=true): dashboard-only, no user message.
|
|
210
210
|
if (!config.advisoryChannel) {
|
|
211
211
|
// PREVENT-PI-003: user-role sendUserMessage only.
|
|
212
|
-
|
|
212
|
+
// Invisible: display:false — the dashboard event + log already
|
|
213
|
+
// capture the poisoned-context signal; the user does not need a
|
|
214
|
+
// visible "Follow-up:" message.
|
|
215
|
+
await safeSendInvisibleMessage(pi, "[mega-compact] this session's context may be poisoned (the provider is rejecting every request). Run /clear or /new to start a fresh context.");
|
|
213
216
|
}
|
|
214
217
|
// (c) one guarded compact per error signature (attempt to remove
|
|
215
218
|
// the poisoned region). Race-guarded + deferred, mirroring the
|
|
@@ -245,139 +248,12 @@ export async function errorRetry(event, ctx, pi, runtime, config) {
|
|
|
245
248
|
}
|
|
246
249
|
else {
|
|
247
250
|
// (5) transient or permanent — retry with exponential backoff.
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
reason: "hard-stop",
|
|
255
|
-
});
|
|
256
|
-
return; // early exit — no retry
|
|
257
|
-
}
|
|
258
|
-
// S38.6: circuit-breaker — stop retrying after too many consecutive errors.
|
|
259
|
-
runtime.rt.consecutiveErrors++;
|
|
260
|
-
// R10: send calm "provider outage" advisory once per episode.
|
|
261
|
-
await maybeSendProviderOutageAdvisory(effectiveCategory, runtime, pi, config, { signal: detail.signal, rawText: (errSig || '').slice(0, 500) });
|
|
262
|
-
if (runtime.rt.consecutiveErrors > config.maxConsecutiveErrors) {
|
|
263
|
-
runtime.dashboard.event("error_retry_circuit_open", {
|
|
264
|
-
consecutive: runtime.rt.consecutiveErrors,
|
|
265
|
-
max: config.maxConsecutiveErrors,
|
|
266
|
-
turnIndex: event.turnIndex,
|
|
267
|
-
});
|
|
268
|
-
runtime.logger.warn("error-retry-circuit-open", {
|
|
269
|
-
sessionId: runtime.rt.sessionId,
|
|
270
|
-
consecutive: runtime.rt.consecutiveErrors,
|
|
271
|
-
max: config.maxConsecutiveErrors,
|
|
272
|
-
});
|
|
273
|
-
return; // early exit — circuit breaker tripped
|
|
274
|
-
}
|
|
275
|
-
const max = effectiveCategory === "transient"
|
|
276
|
-
? config.autoRetryTransientMax
|
|
277
|
-
: config.autoRetryPermanentMax;
|
|
278
|
-
// max === 0 disables the category entirely (revert to S28-only).
|
|
279
|
-
if (max <= 0) {
|
|
280
|
-
runtime.rt.errorRetryCount = 0;
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
runtime.rt.errorRetryCount++;
|
|
284
|
-
if (runtime.rt.errorRetryCount > max) {
|
|
285
|
-
// Exhausted — surface the error, reset for the next burst.
|
|
286
|
-
runtime.dashboard.event("error_retry_exhausted", {
|
|
287
|
-
category: effectiveCategory,
|
|
288
|
-
count: runtime.rt.errorRetryCount,
|
|
289
|
-
max,
|
|
290
|
-
turnIndex: event.turnIndex,
|
|
291
|
-
});
|
|
292
|
-
runtime.logger.info("error-retry-exhausted", {
|
|
293
|
-
sessionId: runtime.rt.sessionId,
|
|
294
|
-
category: effectiveCategory,
|
|
295
|
-
count: runtime.rt.errorRetryCount,
|
|
296
|
-
max,
|
|
297
|
-
});
|
|
298
|
-
runtime.rt.errorRetryCount = 0;
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
// R2: session-global cap — total S38 nudges per session across
|
|
302
|
-
// ALL bursts. Independent of the per-burst max and the circuit
|
|
303
|
-
// breaker. Hitting it is terminal for the session: log +
|
|
304
|
-
// dashboard event, stop nudging. `0` disables (reverts to
|
|
305
|
-
// per-burst + circuit-breaker only).
|
|
306
|
-
if (config.errorRetrySessionMax > 0 &&
|
|
307
|
-
runtime.rt.errorRetrySessionCount >= config.errorRetrySessionMax) {
|
|
308
|
-
runtime.dashboard.event("error_retry_session_exhausted", {
|
|
309
|
-
count: runtime.rt.errorRetrySessionCount,
|
|
310
|
-
max: config.errorRetrySessionMax,
|
|
311
|
-
category: effectiveCategory,
|
|
312
|
-
turnIndex: event.turnIndex,
|
|
313
|
-
});
|
|
314
|
-
runtime.logger.warn("error-retry-session-exhausted", {
|
|
315
|
-
sessionId: runtime.rt.sessionId,
|
|
316
|
-
count: runtime.rt.errorRetrySessionCount,
|
|
317
|
-
max: config.errorRetrySessionMax,
|
|
318
|
-
category: effectiveCategory,
|
|
319
|
-
});
|
|
320
|
-
runtime.rt.errorRetryCount = 0;
|
|
321
|
-
return; // terminal for the session — no nudge
|
|
322
|
-
}
|
|
323
|
-
// R1: in-flight nudge dedup — a nudge queued via
|
|
324
|
-
// deliverAs:'followUp' must not be re-sent until it has been
|
|
325
|
-
// consumed by an actual new agent turn (turn_start resets
|
|
326
|
-
// retryNudgePending). Without this, a fast-erroring provider +
|
|
327
|
-
// a per-turn nudge → N nudges queue up and pi dispatches N
|
|
328
|
-
// retry turns, each re-submitting the same failing prompt
|
|
329
|
-
// (the 2026-07-28 incident). errorRetryCount still advances
|
|
330
|
-
// (this IS an error turn), so the per-burst max + circuit
|
|
331
|
-
// breaker still bound the burst.
|
|
332
|
-
if (runtime.rt.retryNudgePending) {
|
|
333
|
-
runtime.dashboard.event("error_retry_dedup_skip", {
|
|
334
|
-
category: effectiveCategory,
|
|
335
|
-
count: runtime.rt.errorRetryCount,
|
|
336
|
-
max,
|
|
337
|
-
turnIndex: event.turnIndex,
|
|
338
|
-
});
|
|
339
|
-
return; // pending nudge not yet consumed — skip
|
|
340
|
-
}
|
|
341
|
-
// R1: gating backoff — errorRetryUntil is now GATING
|
|
342
|
-
// (previously documented as non-gating). A nudge cannot fire
|
|
343
|
-
// before the previous backoff elapses. This paces retries
|
|
344
|
-
// (5s/10s/20s/30s by default) so a fast-erroring provider
|
|
345
|
-
// doesn't slam N nudges in <1s.
|
|
346
|
-
const now = Date.now();
|
|
347
|
-
if (now < runtime.rt.errorRetryUntil) {
|
|
348
|
-
runtime.dashboard.event("error_retry_backoff_skip", {
|
|
349
|
-
category: effectiveCategory,
|
|
350
|
-
count: runtime.rt.errorRetryCount,
|
|
351
|
-
max,
|
|
352
|
-
turnIndex: event.turnIndex,
|
|
353
|
-
});
|
|
354
|
-
return; // backoff not elapsed — skip
|
|
355
|
-
}
|
|
356
|
-
// Fire the retry nudge. Set pending (R1 dedup) + backoff
|
|
357
|
-
// (R1 pacing) + session count (R2 cap) BEFORE the await so a
|
|
358
|
-
// re-entrant turn_end during the send can't double-fire.
|
|
359
|
-
runtime.rt.retryNudgePending = true;
|
|
360
|
-
runtime.rt.lastErrorRetryAt = now;
|
|
361
|
-
runtime.rt.errorRetryUntil =
|
|
362
|
-
now +
|
|
363
|
-
errorRetryBackoffMs(runtime.rt.errorRetryCount, config.errorRetryBackoffMs);
|
|
364
|
-
runtime.rt.errorRetrySessionCount++;
|
|
365
|
-
runtime.dashboard.event("error_retry", {
|
|
366
|
-
category: effectiveCategory,
|
|
367
|
-
count: runtime.rt.errorRetryCount,
|
|
368
|
-
max,
|
|
369
|
-
turnIndex: event.turnIndex,
|
|
370
|
-
});
|
|
371
|
-
runtime.logger.info("error-retry", {
|
|
372
|
-
sessionId: runtime.rt.sessionId,
|
|
373
|
-
category: effectiveCategory,
|
|
374
|
-
count: runtime.rt.errorRetryCount,
|
|
375
|
-
max,
|
|
376
|
-
});
|
|
377
|
-
// PREVENT-PI-003: user-role sendUserMessage only (queued + catch-guarded).
|
|
378
|
-
await safeSendUserMessage(pi, "[mega-compact] the last turn ended with an error; please retry.");
|
|
379
|
-
}
|
|
380
|
-
}
|
|
251
|
+
// Delegated to errorRetry-transient.ts (delegate-shell split).
|
|
252
|
+
await transientRetry(event, ctx, pi, runtime, config, {
|
|
253
|
+
effectiveCategory,
|
|
254
|
+
detail,
|
|
255
|
+
errSig,
|
|
256
|
+
});
|
|
381
257
|
}
|
|
382
258
|
}
|
|
383
259
|
}
|
|
@@ -3,7 +3,7 @@ import { estimateBlockTokens } from "../../src/tokens.js";
|
|
|
3
3
|
import { recordScore, getDedupStats } from "../../src/store/sqlite.js";
|
|
4
4
|
import { evaluateAndUnlockAchievements } from "../../src/store/sqlite/game-achievements.js";
|
|
5
5
|
import { resolveRepoRoot } from "../mega-config.js";
|
|
6
|
-
import {
|
|
6
|
+
import { safeSendInvisibleMessage } from "./send-safe.js";
|
|
7
7
|
/**
|
|
8
8
|
* Build a minimal fallback compaction so pi never runs its throwing compact().
|
|
9
9
|
*
|
|
@@ -36,7 +36,7 @@ function fallbackCompaction(event) {
|
|
|
36
36
|
* Debounced resume-nudge: restart the agent loop after a compaction (which
|
|
37
37
|
* may have stopped it). Idempotent — one nudge per 30s, never blocks.
|
|
38
38
|
*
|
|
39
|
-
* Uses
|
|
39
|
+
* Uses safeSendInvisibleMessage ({ deliverAs: 'followUp' } + catch-guard) so that a
|
|
40
40
|
* nudge fired during session_before_compact (which is mid-prompt-submission,
|
|
41
41
|
* so the agent can be busy) QUEUES instead of throwing
|
|
42
42
|
* "Agent is already processing. Specify streamingBehavior (steer or followUp)".
|
|
@@ -47,7 +47,7 @@ async function nudgeResume(pi, runtime) {
|
|
|
47
47
|
if (now >= runtime.resumeNudgeUntil) {
|
|
48
48
|
runtime.resumeNudgeUntil = now + 30_000;
|
|
49
49
|
runtime.rt.extensionInitiatedTurn = true; // R13: suppress self-classification
|
|
50
|
-
await
|
|
50
|
+
await safeSendInvisibleMessage(pi, "[mega-compact] continue from the compacted context above.");
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
catch {
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* Distinct from the poisoned-context /clear advise: the outage advisory is
|
|
10
10
|
* for transient errors where the user's context is fine.
|
|
11
11
|
*
|
|
12
|
-
* PREVENT-PI-003: sends via
|
|
12
|
+
* PREVENT-PI-003: sends via safeSendInvisibleMessage (user-role only).
|
|
13
13
|
* PREVENT-PI-004: local ctx call, no network.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
15
|
+
import { safeSendInvisibleMessage } from "./send-safe.js";
|
|
16
16
|
/**
|
|
17
17
|
* Check the provider-outage advisory condition and fire once per episode.
|
|
18
18
|
*
|
|
@@ -21,7 +21,7 @@ import { safeSendUserMessage } from "./send-safe.js";
|
|
|
21
21
|
*
|
|
22
22
|
* @param effectiveCategory — must be "transient" for the advisory to fire.
|
|
23
23
|
* @param runtime — the live MegaRuntime (mutated on advisory fire).
|
|
24
|
-
* @param pi — pi ExtensionAPI for
|
|
24
|
+
* @param pi — pi ExtensionAPI for safeSendInvisibleMessage.
|
|
25
25
|
* @param config — providerOutageAdviseThreshold config.
|
|
26
26
|
* @param detail — R11: optional signal + rawText for forensics.
|
|
27
27
|
*/
|
|
@@ -53,7 +53,7 @@ export async function maybeSendProviderOutageAdvisory(effectiveCategory, runtime
|
|
|
53
53
|
// injecting into the conversation scares users and triggers false-positive
|
|
54
54
|
// re-classification of the resulting turn (2026-07-31 incident).
|
|
55
55
|
if (!config.advisoryChannel) {
|
|
56
|
-
// Legacy path (flag OFF): user-role message
|
|
57
|
-
await
|
|
56
|
+
// Legacy path (flag OFF): invisible user-role message (display:false).
|
|
57
|
+
await safeSendInvisibleMessage(pi, `[mega-compact] the provider is having issues (${runtime.rt.consecutiveErrors} consecutive failures — timeouts/5xx/rate-limits). Retries are bounded and continue automatically; your context is fine — do NOT clear or reset it. Work resumes as soon as the provider recovers.`);
|
|
58
58
|
}
|
|
59
59
|
}
|