pi-mega-compact 0.15.4 → 0.16.1

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.
Files changed (81) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +7 -0
  2. package/dist/extensions/dashboard-server/api-contracts/model-thresholds.js +1 -0
  3. package/dist/extensions/dashboard-server/routes-model-thresholds.js +167 -0
  4. package/dist/extensions/dashboard-server/routes.js +3 -2
  5. package/dist/extensions/dashboard-server/server.js +6 -2
  6. package/dist/extensions/mega-events/context-handler.js +102 -9
  7. package/dist/src/store/sqlite/model-snapshots.js +21 -0
  8. package/dist/src/store/sqlite/model-thresholds.js +93 -0
  9. package/dist/src/store/sqlite/schema/core.js +10 -0
  10. package/dist/src/store/sqlite.js +1 -0
  11. package/extensions/dashboard-client/dist/assets/{AreaChart-CrmTcE7w.js → AreaChart-Y_X0Bbjd.js} +2 -2
  12. package/extensions/dashboard-client/dist/assets/{AreaChart-CrmTcE7w.js.map → AreaChart-Y_X0Bbjd.js.map} +1 -1
  13. package/extensions/dashboard-client/dist/assets/{BarChart-Cf8DTaXS.js → BarChart-B9XDb-Ob.js} +2 -2
  14. package/extensions/dashboard-client/dist/assets/{BarChart-Cf8DTaXS.js.map → BarChart-B9XDb-Ob.js.map} +1 -1
  15. package/extensions/dashboard-client/dist/assets/{CacheTab-Ce0MES6p.js → CacheTab-BUGVb1G1.js} +2 -2
  16. package/extensions/dashboard-client/dist/assets/{CacheTab-Ce0MES6p.js.map → CacheTab-BUGVb1G1.js.map} +1 -1
  17. package/extensions/dashboard-client/dist/assets/{EventsTab-DbaUuA58.js → EventsTab-dOqLYTsy.js} +2 -2
  18. package/extensions/dashboard-client/dist/assets/{EventsTab-DbaUuA58.js.map → EventsTab-dOqLYTsy.js.map} +1 -1
  19. package/extensions/dashboard-client/dist/assets/{HealthTab-BmEXkcfL.js → HealthTab-c2vezKg1.js} +2 -2
  20. package/extensions/dashboard-client/dist/assets/{HealthTab-BmEXkcfL.js.map → HealthTab-c2vezKg1.js.map} +1 -1
  21. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-n2JQrMy7.js → MaintenanceTab-bQYHIcdx.js} +2 -2
  22. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-n2JQrMy7.js.map → MaintenanceTab-bQYHIcdx.js.map} +1 -1
  23. package/extensions/dashboard-client/dist/assets/MemoryMapTab-2L8pH6y2.js +2 -0
  24. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Dvu7vkOm.js.map → MemoryMapTab-2L8pH6y2.js.map} +1 -1
  25. package/extensions/dashboard-client/dist/assets/{MetricsTab-Q0g32AmP.js → MetricsTab-CzUx1jpo.js} +2 -2
  26. package/extensions/dashboard-client/dist/assets/{MetricsTab-Q0g32AmP.js.map → MetricsTab-CzUx1jpo.js.map} +1 -1
  27. package/extensions/dashboard-client/dist/assets/{OverviewTab-iKiEcOLL.js → OverviewTab-eCNoaclv.js} +2 -2
  28. package/extensions/dashboard-client/dist/assets/OverviewTab-eCNoaclv.js.map +1 -0
  29. package/extensions/dashboard-client/dist/assets/{ReposTab-DWpnv77y.js → ReposTab-tb0IW3NF.js} +2 -2
  30. package/extensions/dashboard-client/dist/assets/{ReposTab-DWpnv77y.js.map → ReposTab-tb0IW3NF.js.map} +1 -1
  31. package/extensions/dashboard-client/dist/assets/{SessionsTab-G0YxR1vG.js → SessionsTab-C-IoLw5h.js} +2 -2
  32. package/extensions/dashboard-client/dist/assets/{SessionsTab-G0YxR1vG.js.map → SessionsTab-C-IoLw5h.js.map} +1 -1
  33. package/extensions/dashboard-client/dist/assets/SetupTab-BvmFi8gf.js +2 -0
  34. package/extensions/dashboard-client/dist/assets/SetupTab-BvmFi8gf.js.map +1 -0
  35. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-1_LCFiJp.js → TimeSavedCard-DBH-cOlA.js} +2 -2
  36. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-1_LCFiJp.js.map → TimeSavedCard-DBH-cOlA.js.map} +1 -1
  37. package/extensions/dashboard-client/dist/assets/{TurnsTab-pqOkjE4L.js → TurnsTab-DBMgmvnX.js} +3 -3
  38. package/extensions/dashboard-client/dist/assets/{TurnsTab-pqOkjE4L.js.map → TurnsTab-DBMgmvnX.js.map} +1 -1
  39. package/extensions/dashboard-client/dist/assets/WikiTab-1LcQrXk3.js +2 -0
  40. package/extensions/dashboard-client/dist/assets/{WikiTab-99sKE4nB.js.map → WikiTab-1LcQrXk3.js.map} +1 -1
  41. package/extensions/dashboard-client/dist/assets/button-DtEsZTUE.js +2 -0
  42. package/extensions/dashboard-client/dist/assets/{button-YcV2VQEP.js.map → button-DtEsZTUE.js.map} +1 -1
  43. package/extensions/dashboard-client/dist/assets/{card-Bwxzo50p.js → card-DcfTmw-4.js} +2 -2
  44. package/extensions/dashboard-client/dist/assets/{card-Bwxzo50p.js.map → card-DcfTmw-4.js.map} +1 -1
  45. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-C6hlfMYG.js → generateCategoricalChart-BsIxUDyw.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-C6hlfMYG.js.map → generateCategoricalChart-BsIxUDyw.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/{index-DRa2d4TF.js → index-B93cewiX.js} +14 -14
  48. package/extensions/dashboard-client/dist/assets/index-B93cewiX.js.map +1 -0
  49. package/extensions/dashboard-client/dist/assets/index-C_tCbOI0.css +1 -0
  50. package/extensions/dashboard-client/dist/assets/{switch-rU-lor0v.js → switch-Bowd7jCD.js} +2 -2
  51. package/extensions/dashboard-client/dist/assets/{switch-rU-lor0v.js.map → switch-Bowd7jCD.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{toggle-h-9B4w9X.js → toggle-Bo7kJZ4u.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{toggle-h-9B4w9X.js.map → toggle-Bo7kJZ4u.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/{useSSE-BxJORWx8.js → useSSE-zNhb0c3u.js} +2 -2
  55. package/extensions/dashboard-client/dist/assets/{useSSE-BxJORWx8.js.map → useSSE-zNhb0c3u.js.map} +1 -1
  56. package/extensions/dashboard-client/dist/index.html +2 -2
  57. package/extensions/dashboard-client/src/api/client.ts +38 -13
  58. package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +6 -1
  59. package/extensions/dashboard-client/src/tabs/SetupTab/ThresholdsPanel.tsx +334 -0
  60. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +9 -1
  61. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +50 -17
  62. package/extensions/dashboard-server/api-contracts/index.ts +28 -3
  63. package/extensions/dashboard-server/api-contracts/model-thresholds.ts +76 -0
  64. package/extensions/dashboard-server/resolve-snapshot.ts +1 -1
  65. package/extensions/dashboard-server/routes-model-thresholds.ts +222 -0
  66. package/extensions/dashboard-server/routes.ts +11 -2
  67. package/extensions/dashboard-server/server.ts +5 -1
  68. package/extensions/mega-events/context-handler.ts +159 -50
  69. package/package.json +1 -1
  70. package/src/store/sqlite/model-snapshots.ts +99 -62
  71. package/src/store/sqlite/model-thresholds.ts +168 -0
  72. package/src/store/sqlite/schema/core.ts +10 -0
  73. package/src/store/sqlite.ts +1 -0
  74. package/extensions/dashboard-client/dist/assets/MemoryMapTab-Dvu7vkOm.js +0 -2
  75. package/extensions/dashboard-client/dist/assets/OverviewTab-iKiEcOLL.js.map +0 -1
  76. package/extensions/dashboard-client/dist/assets/SetupTab-C-y6H-F-.js +0 -2
  77. package/extensions/dashboard-client/dist/assets/SetupTab-C-y6H-F-.js.map +0 -1
  78. package/extensions/dashboard-client/dist/assets/WikiTab-99sKE4nB.js +0 -2
  79. package/extensions/dashboard-client/dist/assets/button-YcV2VQEP.js +0 -2
  80. package/extensions/dashboard-client/dist/assets/index-DRa2d4TF.js.map +0 -1
  81. package/extensions/dashboard-client/dist/assets/index-za_gmEmQ.css +0 -1
@@ -297,4 +297,11 @@ export const ENDPOINTS = {
297
297
  path: "/api/rag-metrics",
298
298
  description: "HyDE invocation + recall-quality telemetry: flags, totals, recent turns, and daily series.",
299
299
  },
300
+ // ─── Per-model compaction thresholds (S52 / v0.16.1) ───────────────
301
+ /** GET /api/model-thresholds — List known models + their thresholds. */
302
+ modelThresholds: {
303
+ method: "GET",
304
+ path: "/api/model-thresholds",
305
+ description: "Every known model (from model_snapshots) with its per-model threshold override (or defaults).",
306
+ },
300
307
  };
@@ -0,0 +1,167 @@
1
+ import { DEFAULT_SAFETY_MARGIN_PCT, DEFAULT_FIRE_POINT_PCT, MAX_SAFETY_MARGIN_PCT, MIN_SAFETY_MARGIN_PCT, MIN_FIRE_POINT_PCT, MAX_FIRE_POINT_PCT, getModelThreshold, listModelThresholds, putModelThreshold, deleteModelThreshold, } from "../../src/store/sqlite/model-thresholds.js";
2
+ import { listModelSnapshots } from "../../src/store/sqlite/model-snapshots.js";
3
+ function send(res, status, body) {
4
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
5
+ res.writeHead(status, { "Content-Type": "application/json" });
6
+ res.end(JSON.stringify(body));
7
+ }
8
+ /** Read + JSON.parse a request body (被骗: spans POST/PUT/DELETE).
9
+ * Capped at 64KB; null-safe. */
10
+ function readBody(req, cb) {
11
+ let body = "";
12
+ let tooBig = false;
13
+ req.on("data", (chunk) => {
14
+ if (body.length > 65536) {
15
+ tooBig = true;
16
+ return;
17
+ }
18
+ body += chunk.toString();
19
+ });
20
+ req.on("end", () => {
21
+ if (tooBig)
22
+ return cb({ ok: false, error: "body_too_large" });
23
+ try {
24
+ const v = body ? JSON.parse(body) : {}; // PREVENT-001: type-checked
25
+ if (typeof v !== "object" || v === null || Array.isArray(v)) {
26
+ return cb({ ok: false, error: "invalid_json" });
27
+ }
28
+ cb({ ok: true, value: v });
29
+ }
30
+ catch {
31
+ cb({ ok: false, error: "invalid_json" });
32
+ }
33
+ });
34
+ req.on("error", () => cb({ ok: false, error: "invalid_json" }));
35
+ }
36
+ /** Read body that also handles 0 bytes. */
37
+ function requireNumber(v, label) {
38
+ const n = Number(v);
39
+ if (!Number.isFinite(n)) {
40
+ throw new Error(`${label} must be a finite number, got ${String(v)}`);
41
+ }
42
+ return n;
43
+ }
44
+ /** List known models from model_snapshots, deduped by model_id (latest wins). */
45
+ function listKnownModels(stateDir) {
46
+ const snaps = listModelSnapshots(stateDir);
47
+ // Dedup by model_id, keep latest (highest capturedAt).
48
+ const byId = new Map();
49
+ for (const s of snaps) {
50
+ const existing = byId.get(s.modelId);
51
+ if (!existing || s.capturedAt > existing.capturedAt)
52
+ byId.set(s.modelId, s);
53
+ }
54
+ return Array.from(byId.values()).map((s) => ({
55
+ modelId: s.modelId,
56
+ provider: s.provider,
57
+ modelName: s.modelName,
58
+ contextWindow: s.contextWindow,
59
+ maxTokens: s.maxTokens,
60
+ hasOverride: getModelThreshold(s.modelId, stateDir) !== null,
61
+ }));
62
+ }
63
+ export function handleModelThresholds(req, res, ctx) {
64
+ if (!req.url?.startsWith("/api/model-thresholds"))
65
+ return false;
66
+ const url = new URL(req.url, "http://x"); // guardrails-allow PREVENT-PI-004: localhost dashboard URL base (loopback-only)
67
+ // GET: list every known model + its threshold.
68
+ if (req.method === "GET" && url.pathname === "/api/model-thresholds") {
69
+ try {
70
+ const known = listKnownModels(ctx.stateDir);
71
+ const overrides = new Map(listModelThresholds(ctx.stateDir).map((t) => [t.modelId, t]));
72
+ const body = {
73
+ defaults: {
74
+ safetyMarginPct: DEFAULT_SAFETY_MARGIN_PCT,
75
+ firePointPct: DEFAULT_FIRE_POINT_PCT,
76
+ safetyMarginRange: [MIN_SAFETY_MARGIN_PCT, MAX_SAFETY_MARGIN_PCT],
77
+ firePointRange: [MIN_FIRE_POINT_PCT, MAX_FIRE_POINT_PCT],
78
+ },
79
+ models: known.map((m) => {
80
+ const ov = overrides.get(m.modelId);
81
+ return {
82
+ ...m,
83
+ threshold: ov
84
+ ? {
85
+ safetyMarginPct: ov.safetyMarginPct,
86
+ firePointPct: ov.firePointPct,
87
+ isOverride: true,
88
+ }
89
+ : {
90
+ safetyMarginPct: DEFAULT_SAFETY_MARGIN_PCT,
91
+ firePointPct: DEFAULT_FIRE_POINT_PCT,
92
+ isOverride: false,
93
+ },
94
+ };
95
+ }),
96
+ };
97
+ send(res, 200, body);
98
+ }
99
+ catch (e) {
100
+ send(res, 500, { error: "internal", detail: String(e) });
101
+ }
102
+ return true;
103
+ }
104
+ // PUT: upsert a per-model override.
105
+ if (req.method === "PUT" && url.pathname === "/api/model-thresholds") {
106
+ readBody(req, (result) => {
107
+ if (!result.ok)
108
+ return send(res, 400, { error: result.error });
109
+ try {
110
+ const v = result.value;
111
+ const modelId = v.modelId;
112
+ if (typeof modelId !== "string" || !modelId.trim()) {
113
+ return send(res, 400, { error: "missing_model_id" });
114
+ }
115
+ let safety;
116
+ let fire;
117
+ try {
118
+ safety = requireNumber(v.safetyMarginPct, "safetyMarginPct");
119
+ fire = requireNumber(v.firePointPct, "firePointPct");
120
+ }
121
+ catch (e) {
122
+ return send(res, 400, {
123
+ error: "invalid_pct",
124
+ detail: e instanceof Error ? e.message : String(e),
125
+ });
126
+ }
127
+ const t = putModelThreshold(modelId, safety, fire, ctx.stateDir);
128
+ const body = { threshold: t };
129
+ send(res, 200, body);
130
+ }
131
+ catch (e) {
132
+ const msg = e instanceof Error ? e.message : String(e);
133
+ if (/must be in \[/.test(msg)) {
134
+ return send(res, 400, { error: "invalid_pct", detail: msg });
135
+ }
136
+ send(res, 500, { error: "internal", detail: msg });
137
+ }
138
+ });
139
+ return true;
140
+ }
141
+ // DELETE: /api/model-thresholds/:modelId
142
+ if (req.method === "DELETE" &&
143
+ url.pathname.startsWith("/api/model-thresholds/")) {
144
+ const modelId = decodeURIComponent(url.pathname.slice("/api/model-thresholds/".length));
145
+ if (!modelId) {
146
+ send(res, 400, { error: "missing_model_id" });
147
+ return true;
148
+ }
149
+ try {
150
+ const deleted = deleteModelThreshold(modelId, ctx.stateDir);
151
+ const body = { deleted };
152
+ send(res, 200, body);
153
+ }
154
+ catch (e) {
155
+ send(res, 500, { error: "internal", detail: String(e) });
156
+ }
157
+ return true;
158
+ }
159
+ // Catch-all: only GET/PUT/DELETE on /api/model-thresholds[*] are supported.
160
+ if (url.pathname === "/api/model-thresholds") {
161
+ send(res, 405, {
162
+ error: "method_not_allowed",
163
+ });
164
+ return true;
165
+ }
166
+ return false;
167
+ }
@@ -13,12 +13,13 @@ export { handleTurns } from "./routes-turns.js";
13
13
  export { handleMaintenance } from "./routes-maintenance.js";
14
14
  export { handleProviderCache } from "./routes-cache.js";
15
15
  export { handleMemoryStatus } from "./routes-memory.js";
16
- export { handleSetupStatus, handleSetupDetect, handleSetupConfigure } from "./routes-setup.js";
16
+ export { handleSetupStatus, handleSetupDetect, handleSetupConfigure, } from "./routes-setup.js";
17
17
  export { handleMemoryMap } from "./routes-memory-map.js";
18
18
  export { handleRaptorTree, handleRaptorBuildHistory } from "./routes-raptor.js";
19
19
  export { handleCacheStripes } from "./routes-cache.js";
20
- export { handleContextHealth, handleCachePoison, handleHealthSettings } from "./routes-health.js";
20
+ export { handleContextHealth, handleCachePoison, handleHealthSettings, } from "./routes-health.js";
21
21
  export { handleEmbedderHealth } from "./routes-embedder-health.js";
22
22
  export { handleRagSettings } from "./routes-rag-settings.js";
23
23
  export { handleRagMetrics } from "./routes-rag-metrics.js";
24
+ export { handleModelThresholds } from "./routes-model-thresholds.js";
24
25
  export { handleWiki } from "./routes-wiki.js";
@@ -13,7 +13,7 @@ import { fileURLToPath } from "node:url";
13
13
  import { createRequire } from "node:module";
14
14
  import { NEW_UI } from "../../src/config.js";
15
15
  import { log, setLogPath, setDashboardServerVersion } from "./state.js";
16
- import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handlePerfSamples, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleCacheStripes, handleMemoryStatus, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleRaptorTree, handleRaptorBuildHistory, handleContextHealth, handleCachePoison, handleHealthSettings, handleEmbedderHealth, handleRagSettings, handleRagMetrics, handleWiki, handleStatic, } from "./routes.js";
16
+ import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handlePerfSamples, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleCacheStripes, handleMemoryStatus, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleRaptorTree, handleRaptorBuildHistory, handleContextHealth, handleCachePoison, handleHealthSettings, handleEmbedderHealth, handleRagSettings, handleRagMetrics, handleModelThresholds, handleWiki, handleStatic, } from "./routes.js";
17
17
  export async function launchDashboardServer(stateDir) {
18
18
  // Our own package version — exposed at /api/version so the launcher can
19
19
  // detect a stale server (started by an older build) and replace it on
@@ -107,7 +107,9 @@ export async function launchDashboardServer(stateDir) {
107
107
  ico: "image/x-icon",
108
108
  map: "application/json",
109
109
  };
110
- const payload = ext === "html" ? injectUiFlag(readFileSync(file, "utf-8")) : readFileSync(file);
110
+ const payload = ext === "html"
111
+ ? injectUiFlag(readFileSync(file, "utf-8"))
112
+ : readFileSync(file);
111
113
  res.writeHead(200, {
112
114
  "Content-Type": types[ext] ?? "application/octet-stream",
113
115
  });
@@ -235,6 +237,8 @@ export async function launchDashboardServer(stateDir) {
235
237
  return;
236
238
  if (handleRagMetrics(req, res, ctx))
237
239
  return;
240
+ if (handleModelThresholds(req, res, ctx))
241
+ return;
238
242
  if (handleWiki(req, res, ctx))
239
243
  return;
240
244
  handleStatic(req, res, ctx);
@@ -1,6 +1,6 @@
1
- import { openStore, } from "../../src/store/sqlite.js";
1
+ import { openStore, resolveModelThreshold, DEFAULT_SAFETY_MARGIN_PCT, DEFAULT_FIRE_POINT_PCT, } from "../../src/store/sqlite.js";
2
2
  import { autoCompactCheck } from "../../src/compact.js";
3
- import { estimateSessionTokens } from "../../src/tokens.js";
3
+ import { estimateSessionTokens, estimateBlockTokens, estimateMessageTokens, } from "../../src/tokens.js";
4
4
  import { runCompact, piCompactWouldNoop } from "../mega-pipeline.js";
5
5
  import { pressureFromPct, pressureRatio, } from "../mega-config.js";
6
6
  import { appendMirrorMessages } from "./mirror-append.js";
@@ -101,11 +101,26 @@ export function registerContextHandler(pi, runtime, config) {
101
101
  runtime.logger.warn("db-mirror-append-fail", { error: String(e) });
102
102
  }
103
103
  }
104
+ // S52 / v0.16.1: per-model threshold override. The user can tune the
105
+ // fire point + safety margin PER MODEL (different providers' models range
106
+ // 8K-1M+ context, so one global tier % is wrong). Falls back to env/default
107
+ // when no override row exists. Computed once here + reused in the tail cap
108
+ // below; the lookup is a single SQLite PK hit (cheap; cached after the
109
+ // first read in a session).
110
+ const _modelIdForThreshold = runtime.currentModel?.modelId ?? null;
111
+ const _perModelThreshold = resolveModelThreshold(_modelIdForThreshold, {
112
+ safetyMarginFallback: DEFAULT_SAFETY_MARGIN_PCT,
113
+ firePointFallback: config.tierPct != null
114
+ ? Math.round(config.tierPct * 100)
115
+ : DEFAULT_FIRE_POINT_PCT,
116
+ stateDir: runtime.currentStateDir,
117
+ });
104
118
  // S29 FAST GATE: drive the auto-trigger off the context % (the number the
105
119
  // menu bar shows), NOT the token count — the model under-reports tokens,
106
120
  // so a token-only gate misses the overshoot that causes max-output-tokens
107
- // truncation. The fire point is the tier's percent threshold (tierPct)
108
- // unless overridden by MEGACOMPACT_AUTO_PCT_TRIGGER. `custom` (absolute
121
+ // truncation. The fire point is the per-model override when present,
122
+ // otherwise the tier's percent threshold (tierPct) unless overridden by
123
+ // MEGACOMPACT_AUTO_PCT_TRIGGER. `custom` (absolute
109
124
  // MEGACOMPACT_THRESHOLD_TOKENS, tierPct null) is an explicit opt-out of
110
125
  // percent scaling — it keeps the token gate. When pct is unavailable
111
126
  // (window unknown / a model that doesn't report percent) a tiered config
@@ -113,7 +128,11 @@ export function registerContextHandler(pi, runtime, config) {
113
128
  // skipping compaction — a percent-only gate would regress that.
114
129
  let gatePassed = false;
115
130
  if (config.tierPct != null && pct != null) {
116
- const firePct = config.autoPctTrigger ?? config.tierPct;
131
+ // Per-model override is a % (10-90); tierPct is a fraction (0.1-1.0).
132
+ // Prefer the override; fall back to autoPctTrigger + tierPct.
133
+ const tierPctFraction = config.autoPctTrigger ?? config.tierPct;
134
+ const perModelFraction = _perModelThreshold.firePointPct / 100;
135
+ const firePct = _modelIdForThreshold != null ? perModelFraction : tierPctFraction;
117
136
  gatePassed = pct / 100 >= firePct;
118
137
  }
119
138
  else {
@@ -160,7 +179,10 @@ export function registerContextHandler(pi, runtime, config) {
160
179
  runtime.snapshot(ctx);
161
180
  // v0.8.7: shallow-copy the cached summary so pi's transformContext can't
162
181
  // mutate the shared reference across replays (audit P3).
163
- const replayView = [{ ...runtime.trimCache.summaryAgentMsg }, ...recent];
182
+ const replayView = [
183
+ { ...runtime.trimCache.summaryAgentMsg },
184
+ ...recent,
185
+ ];
164
186
  return tailResult(replayView) ?? { messages: replayView };
165
187
  }
166
188
  // else: context grew enough → fall through to re-compact (cache is stale)
@@ -281,7 +303,15 @@ export function registerContextHandler(pi, runtime, config) {
281
303
  // model is fed a raw overflow that errors every turn — the
282
304
  // "Already compacted" + overflow death-spiral (2026-08-01 incident).
283
305
  // A thin anchor is recoverable; an overflowed session is not.
284
- criticalOver: (pct ?? 0) >= 90,
306
+ //
307
+ // CRITICAL: pct is null for OpenAI-compatible providers that don't
308
+ // report usage.percent (e.g. neuralwatt). Without the token-pressure
309
+ // fallback the hatch never armed → cut=null → raw overflow → 400
310
+ // "conversation too long even after compaction" (2026-08-03 incident
311
+ // on glm-5.2-short, 200K window). Now also fires on pressure >= 0.9
312
+ // (token-basis) so the hatch arms regardless of whether the provider
313
+ // reports pct.
314
+ criticalOver: (pct ?? 0) >= 90 || pressure >= 0.9,
285
315
  });
286
316
  if (cut === null) {
287
317
  runtime.diagCtxCutNull++;
@@ -308,7 +338,68 @@ export function registerContextHandler(pi, runtime, config) {
308
338
  // on every replay within the same compaction epoch.
309
339
  timestamp: runtime.rt.lastCompactAt ?? Date.now(),
310
340
  };
311
- const recent = messages.slice(cut); // guardrails-allow PREVENT-PI-002: `cut` is the pre-sanitized `compactedFrom` produced by src/boundary.ts computeDropRange, so the preserved run begins on a toolPair-safe index.
341
+ const recentRaw = messages.slice(cut); // guardrails-allow PREVENT-PI-002: `cut` is the pre-sanitized `compactedFrom` produced by src/boundary.ts computeDropRange, so the preserved run begins on a toolPair-safe index.
342
+ // FIX 2 (2026-08-03 incident): TOKEN-BUDGET CAP on the live-trim view.
343
+ // Compaction fires at tier% of the window (140K for a 200K window),
344
+ // but a SINGLE turn can inject a huge tool output (file read, bash) that
345
+ // jumps context from 139K → 199K+ before the next gate fires. When that
346
+ // happens [summary + preserved tail] can STILL exceed the model window,
347
+ // and the provider rejects with 400 "conversation too long even after
348
+ // compaction". The anchor floor (PREVENT-PI-001) keeps ≥N user messages
349
+ // but has NO token cap, so a 2-message tail of two 80K bash outputs sails
350
+ // right past the window.
351
+ //
352
+ // Cap: when the model context window is known, reserve room for the
353
+ // summary + the model's max output tokens + a 10% safety margin, then
354
+ // drop oldest preserved messages from the front of `recentRaw` until the
355
+ // tail fits. Never drops below the FINAL message (always keep the latest
356
+ // turn so the agent can respond). This is a last-resort HARD cap — it
357
+ // only fires when the preserved tail alone is oversized, which is rare.
358
+ const ctxWindow = runtime.lastCtxWindow;
359
+ // Reuse the per-model threshold resolved at the gate (single lookup).
360
+ const modelThreshold = _perModelThreshold;
361
+ // Reserve room for output tokens. Use the model's reported max output
362
+ // when known; fall back to 10% of the window (scales with any model —
363
+ // 20K for a 200K window, 100K for a 1M window) so we never let the
364
+ // preserved tail eat the model's output budget when maxTokens is unknown.
365
+ const maxOutput = runtime.currentModel?.maxTokens && runtime.currentModel.maxTokens > 0
366
+ ? runtime.currentModel.maxTokens
367
+ : Math.ceil(ctxWindow * 0.1);
368
+ let recent = recentRaw;
369
+ if (ctxWindow > 0 && recentRaw.length > 1) {
370
+ const summaryTokens = estimateBlockTokens(summaryMsg.text);
371
+ // Reserve: summary + max output + per-model safety margin (0-20%).
372
+ const safetyMargin = Math.ceil(ctxWindow * (modelThreshold.safetyMarginPct / 100));
373
+ const budget = ctxWindow - maxOutput - safetyMargin - summaryTokens;
374
+ if (budget > 0) {
375
+ // Walk recent from the front, dropping oldest first until the
376
+ // remaining tail fits. Use the AgentMessage→engine-text estimate via
377
+ // messageContentText (already imported) + estimateMessageTokens.
378
+ let tailTokens = 0;
379
+ for (let i = recentRaw.length - 1; i >= 0; i--) {
380
+ const m = recentRaw[i];
381
+ tailTokens += estimateMessageTokens({
382
+ text: messageContentText(m),
383
+ });
384
+ if (tailTokens > budget) {
385
+ // Keep from i+1 onward; but never fewer than the final message.
386
+ const startIdx = Math.min(i + 1, recentRaw.length - 1);
387
+ if (startIdx > 0) {
388
+ recent = recentRaw.slice(startIdx);
389
+ runtime.logger.warn("live-trim-tail-cap", {
390
+ sessionId: runtime.rt.sessionId,
391
+ dropped: startIdx,
392
+ tailTokens,
393
+ safetyMarginPct: modelThreshold.safetyMarginPct,
394
+ budget,
395
+ ctxWindow,
396
+ });
397
+ }
398
+ break;
399
+ }
400
+ }
401
+ }
402
+ }
312
403
  // v0.8.6: cache the trim view so subsequent gated calls in this epoch
313
404
  // replay it verbatim (stabilizing the KV-cache prefix) instead of
314
405
  // regenerating a fresh summary + sentinel every fire.
@@ -346,7 +437,9 @@ export function registerContextHandler(pi, runtime, config) {
346
437
  ctxPct: pct,
347
438
  ctxTokens: usage?.tokens ?? null,
348
439
  });
349
- return tailResult([summaryAgentMsg, ...recent]) ?? { messages: [summaryAgentMsg, ...recent] };
440
+ return (tailResult([summaryAgentMsg, ...recent]) ?? {
441
+ messages: [summaryAgentMsg, ...recent],
442
+ });
350
443
  }
351
444
  catch {
352
445
  runtime.diagCtxThrown++;
@@ -45,3 +45,24 @@ export function latestModelSnapshot(stateDir = getStateDir()) {
45
45
  capturedAt: row.captured_at,
46
46
  };
47
47
  }
48
+ /** All model snapshots (for the dashboard Thresholds table — deduped by
49
+ * caller). Returns NULL columns as-is; callers should default missing
50
+ * context_window/max_tokens to 0. */
51
+ export function listModelSnapshots(stateDir = getStateDir()) {
52
+ const db = openStore(stateDir);
53
+ const rows = db
54
+ .prepare(`SELECT * FROM model_snapshots ORDER BY captured_at DESC`)
55
+ .all();
56
+ return rows.map((row) => ({
57
+ provider: row.provider,
58
+ providerName: row.provider_name,
59
+ modelId: row.model_id,
60
+ modelName: row.model_name,
61
+ inputRate: row.input_rate,
62
+ outputRate: row.output_rate,
63
+ contextWindow: row.context_window,
64
+ maxTokens: row.max_tokens,
65
+ reasoning: row.reasoning === 1,
66
+ capturedAt: row.captured_at,
67
+ }));
68
+ }
@@ -0,0 +1,93 @@
1
+ import { getStateDir } from "../../store.js";
2
+ import { openStore } from "./utils.js";
3
+ /** Default safety margin (%) when no per-model override exists. */
4
+ export const DEFAULT_SAFETY_MARGIN_PCT = 5;
5
+ /** Default fire point (%) when no per-model + no env override exists. */
6
+ export const DEFAULT_FIRE_POINT_PCT = 70;
7
+ export const MIN_SAFETY_MARGIN_PCT = 0;
8
+ export const MAX_SAFETY_MARGIN_PCT = 20;
9
+ export const MIN_FIRE_POINT_PCT = 10;
10
+ export const MAX_FIRE_POINT_PCT = 90;
11
+ function rowToThreshold(r) {
12
+ return {
13
+ modelId: r.model_id,
14
+ safetyMarginPct: r.safety_margin_pct,
15
+ firePointPct: r.fire_point_pct,
16
+ updatedAt: r.updated_at ?? null,
17
+ };
18
+ }
19
+ /** Get the per-model threshold override, or null when none exists. */
20
+ export function getModelThreshold(modelId, stateDir = getStateDir()) {
21
+ const db = openStore(stateDir);
22
+ const row = db
23
+ .prepare("SELECT model_id, safety_margin_pct, fire_point_pct, updated_at FROM model_thresholds WHERE model_id = ?")
24
+ .get(modelId);
25
+ return row ? rowToThreshold(row) : null;
26
+ }
27
+ /** List ALL stored per-model thresholds (for the dashboard table). */
28
+ export function listModelThresholds(stateDir = getStateDir()) {
29
+ const db = openStore(stateDir);
30
+ const rows = db
31
+ .prepare("SELECT model_id, safety_margin_pct, fire_point_pct, updated_at FROM model_thresholds ORDER BY updated_at DESC")
32
+ .all();
33
+ return rows.map(rowToThreshold);
34
+ }
35
+ /** Validate a threshold value range. Throws on out-of-bounds. */
36
+ export function assertValidPct(pct, min, max, label) {
37
+ if (!Number.isFinite(pct)) {
38
+ throw new Error(`${label} must be a finite number, got ${pct}`);
39
+ }
40
+ if (pct < min || pct > max) {
41
+ throw new Error(`${label} must be in [${min}, ${max}], got ${pct}`);
42
+ }
43
+ }
44
+ /** Upsert a per-model threshold. Validates ranges (0-20, 10-90). */
45
+ export function putModelThreshold(modelId, safetyMarginPct, firePointPct, stateDir = getStateDir()) {
46
+ if (!modelId || typeof modelId !== "string") {
47
+ throw new Error("modelId is required");
48
+ }
49
+ assertValidPct(safetyMarginPct, MIN_SAFETY_MARGIN_PCT, MAX_SAFETY_MARGIN_PCT, "safetyMarginPct");
50
+ assertValidPct(firePointPct, MIN_FIRE_POINT_PCT, MAX_FIRE_POINT_PCT, "firePointPct");
51
+ const db = openStore(stateDir);
52
+ const now = Date.now();
53
+ db.prepare(`INSERT INTO model_thresholds (model_id, safety_margin_pct, fire_point_pct, updated_at)
54
+ VALUES (?, ?, ?, ?)
55
+ ON CONFLICT(model_id) DO UPDATE SET
56
+ safety_margin_pct = excluded.safety_margin_pct,
57
+ fire_point_pct = excluded.fire_point_pct,
58
+ updated_at = excluded.updated_at`).run(modelId, safetyMarginPct, firePointPct, now);
59
+ return {
60
+ modelId,
61
+ safetyMarginPct,
62
+ firePointPct,
63
+ updatedAt: now,
64
+ };
65
+ }
66
+ /** Delete a per-model threshold (revert to env/defaults). */
67
+ export function deleteModelThreshold(modelId, stateDir = getStateDir()) {
68
+ const db = openStore(stateDir);
69
+ const r = db
70
+ .prepare("DELETE FROM model_thresholds WHERE model_id = ?")
71
+ .run(modelId);
72
+ return (r.changes ?? 0) > 0;
73
+ }
74
+ /**
75
+ * Resolve the EFFECTIVE threshold value for a model: per-model override if
76
+ * present, otherwise the fallback. Used by context-handler.ts to pick the
77
+ * safety margin + fire point without each call duplicating the lookup.
78
+ */
79
+ export function resolveModelThreshold(modelId, opts) {
80
+ const fallback = {
81
+ safetyMarginPct: opts.safetyMarginFallback,
82
+ firePointPct: opts.firePointFallback,
83
+ };
84
+ if (!modelId)
85
+ return fallback;
86
+ const row = getModelThreshold(modelId, opts.stateDir ?? getStateDir());
87
+ if (!row)
88
+ return fallback;
89
+ return {
90
+ safetyMarginPct: row.safetyMarginPct,
91
+ firePointPct: row.firePointPct,
92
+ };
93
+ }
@@ -158,6 +158,16 @@ export const CORE_DDL = `
158
158
  );
159
159
  CREATE INDEX IF NOT EXISTS idx_model_repo ON model_snapshots(repo_root);
160
160
 
161
+ -- Per-model compaction thresholds (S52 / v0.16.1). User-tunable % values
162
+ -- that override the env tier defaults + the fix-2 safety margin. One row
163
+ -- per model_id; NULL fallback to env/defaults when no row exists.
164
+ CREATE TABLE IF NOT EXISTS model_thresholds (
165
+ model_id TEXT PRIMARY KEY,
166
+ safety_margin_pct REAL NOT NULL, -- 0-20: reserve after summary+maxOutput
167
+ fire_point_pct REAL NOT NULL, -- 10-90: % of window where compaction fires
168
+ updated_at INTEGER
169
+ );
170
+
161
171
  -- Lessons learned (future recall/browse feature seed).
162
172
  CREATE TABLE IF NOT EXISTS lessons (
163
173
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -16,6 +16,7 @@ export * from "./sqlite/checkpoints.js";
16
16
  export * from "./sqlite/session-state.js";
17
17
  export * from "./sqlite/stats.js";
18
18
  export * from "./sqlite/model-snapshots.js";
19
+ export * from "./sqlite/model-thresholds.js";
19
20
  export * from "./sqlite/raptor.js";
20
21
  export * from "./sqlite/raw-transcript.js";
21
22
  export * from "./sqlite/dedup-mirror.js";
@@ -1,2 +1,2 @@
1
- import{b as R,c as W,m as T,i as C,d as F,A as ie,e as B,h as q,j as oe,k as G,l as se,n as le,u as ue,o as K,G as ce,p as J,q as Q,D as fe,g as pe,X as he,Y as de,f as me}from"./generateCategoricalChart-C6hlfMYG.js";import{R as p,A as ee,r as ve}from"./index-DRa2d4TF.js";var ye=["layout","type","stroke","connectNulls","isRange","ref"],be=["key"],te;function M(e){"@babel/helpers - typeof";return M=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},M(e)}function ae(e,t){if(e==null)return{};var r=Ae(e,t),a,n;if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)a=i[n],!(t.indexOf(a)>=0)&&Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}function Ae(e,t){if(e==null)return{};var r={};for(var a in e)if(Object.prototype.hasOwnProperty.call(e,a)){if(t.indexOf(a)>=0)continue;r[a]=e[a]}return r}function D(){return D=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e},D.apply(this,arguments)}function U(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable})),r.push.apply(r,a)}return r}function S(e){for(var t=1;t<arguments.length;t++){var r=arguments[t]!=null?arguments[t]:{};t%2?U(Object(r),!0).forEach(function(a){E(e,a,r[a])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):U(Object(r)).forEach(function(a){Object.defineProperty(e,a,Object.getOwnPropertyDescriptor(r,a))})}return e}function ge(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Z(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,ne(a.key),a)}}function xe(e,t,r){return t&&Z(e.prototype,t),r&&Z(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function Pe(e,t,r){return t=$(t),Oe(e,re()?Reflect.construct(t,r||[],$(e).constructor):t.apply(e,r))}function Oe(e,t){if(t&&(M(t)==="object"||typeof t=="function"))return t;if(t!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return Ee(e)}function Ee(e){if(e===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function re(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(re=function(){return!!e})()}function $(e){return $=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(r){return r.__proto__||Object.getPrototypeOf(r)},$(e)}function we(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&X(e,t)}function X(e,t){return X=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(a,n){return a.__proto__=n,a},X(e,t)}function E(e,t,r){return t=ne(t),t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function ne(e){var t=_e(e,"string");return M(t)=="symbol"?t:t+""}function _e(e,t){if(M(e)!="object"||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var a=r.call(e,t);if(M(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}var L=function(e){function t(){var r;ge(this,t);for(var a=arguments.length,n=new Array(a),i=0;i<a;i++)n[i]=arguments[i];return r=Pe(this,t,[].concat(n)),E(r,"state",{isAnimationFinished:!0}),E(r,"id",ue("recharts-area-")),E(r,"handleAnimationEnd",function(){var o=r.props.onAnimationEnd;r.setState({isAnimationFinished:!0}),K(o)&&o()}),E(r,"handleAnimationStart",function(){var o=r.props.onAnimationStart;r.setState({isAnimationFinished:!1}),K(o)&&o()}),r}return we(t,e),xe(t,[{key:"renderDots",value:function(a,n,i){var o=this.props.isAnimationActive,s=this.state.isAnimationFinished;if(o&&!s)return null;var u=this.props,l=u.dot,f=u.points,c=u.dataKey,h=R(this.props,!1),A=R(l,!0),g=f.map(function(v,O){var y=S(S(S({key:"dot-".concat(O),r:3},h),A),{},{index:O,cx:v.x,cy:v.y,dataKey:c,value:v.value,payload:v.payload,points:f});return t.renderDotItem(l,y)}),x={clipPath:a?"url(#clipPath-".concat(n?"":"dots-").concat(i,")"):null};return p.createElement(W,D({className:"recharts-area-dots"},x),g)}},{key:"renderHorizontalRect",value:function(a){var n=this.props,i=n.baseLine,o=n.points,s=n.strokeWidth,u=o[0].x,l=o[o.length-1].x,f=a*Math.abs(u-l),c=T(o.map(function(h){return h.y||0}));return C(i)&&typeof i=="number"?c=Math.max(i,c):i&&Array.isArray(i)&&i.length&&(c=Math.max(T(i.map(function(h){return h.y||0})),c)),C(c)?p.createElement("rect",{x:u<l?u:u-f,y:0,width:f,height:Math.floor(c+(s?parseInt("".concat(s),10):1))}):null}},{key:"renderVerticalRect",value:function(a){var n=this.props,i=n.baseLine,o=n.points,s=n.strokeWidth,u=o[0].y,l=o[o.length-1].y,f=a*Math.abs(u-l),c=T(o.map(function(h){return h.x||0}));return C(i)&&typeof i=="number"?c=Math.max(i,c):i&&Array.isArray(i)&&i.length&&(c=Math.max(T(i.map(function(h){return h.x||0})),c)),C(c)?p.createElement("rect",{x:0,y:u<l?u:u-f,width:c+(s?parseInt("".concat(s),10):1),height:Math.floor(f)}):null}},{key:"renderClipRect",value:function(a){var n=this.props.layout;return n==="vertical"?this.renderVerticalRect(a):this.renderHorizontalRect(a)}},{key:"renderAreaStatically",value:function(a,n,i,o){var s=this.props,u=s.layout,l=s.type,f=s.stroke,c=s.connectNulls,h=s.isRange;s.ref;var A=ae(s,ye);return p.createElement(W,{clipPath:i?"url(#clipPath-".concat(o,")"):null},p.createElement(F,D({},R(A,!0),{points:a,connectNulls:c,type:l,baseLine:n,layout:u,stroke:"none",className:"recharts-area-area"})),f!=="none"&&p.createElement(F,D({},R(this.props,!1),{className:"recharts-area-curve",layout:u,type:l,connectNulls:c,fill:"none",points:a})),f!=="none"&&h&&p.createElement(F,D({},R(this.props,!1),{className:"recharts-area-curve",layout:u,type:l,connectNulls:c,fill:"none",points:n})))}},{key:"renderAreaWithAnimation",value:function(a,n){var i=this,o=this.props,s=o.points,u=o.baseLine,l=o.isAnimationActive,f=o.animationBegin,c=o.animationDuration,h=o.animationEasing,A=o.animationId,g=this.state,x=g.prevPoints,v=g.prevBaseLine;return p.createElement(ie,{begin:f,duration:c,isActive:l,easing:h,from:{t:0},to:{t:1},key:"area-".concat(A),onAnimationEnd:this.handleAnimationEnd,onAnimationStart:this.handleAnimationStart},function(O){var y=O.t;if(x){var _=x.length/s.length,d=s.map(function(P,N){var k=Math.floor(N*_);if(x[k]){var j=x[k],I=B(j.x,P.x),V=B(j.y,P.y);return S(S({},P),{},{x:I(y),y:V(y)})}return P}),m;if(C(u)&&typeof u=="number"){var b=B(v,u);m=b(y)}else if(q(u)||oe(u)){var w=B(v,0);m=w(y)}else m=u.map(function(P,N){var k=Math.floor(N*_);if(v[k]){var j=v[k],I=B(j.x,P.x),V=B(j.y,P.y);return S(S({},P),{},{x:I(y),y:V(y)})}return P});return i.renderAreaStatically(d,m,a,n)}return p.createElement(W,null,p.createElement("defs",null,p.createElement("clipPath",{id:"animationClipPath-".concat(n)},i.renderClipRect(y))),p.createElement(W,{clipPath:"url(#animationClipPath-".concat(n,")")},i.renderAreaStatically(s,u,a,n)))})}},{key:"renderArea",value:function(a,n){var i=this.props,o=i.points,s=i.baseLine,u=i.isAnimationActive,l=this.state,f=l.prevPoints,c=l.prevBaseLine,h=l.totalLength;return u&&o&&o.length&&(!f&&h>0||!G(f,o)||!G(c,s))?this.renderAreaWithAnimation(a,n):this.renderAreaStatically(o,s,a,n)}},{key:"render",value:function(){var a,n=this.props,i=n.hide,o=n.dot,s=n.points,u=n.className,l=n.top,f=n.left,c=n.xAxis,h=n.yAxis,A=n.width,g=n.height,x=n.isAnimationActive,v=n.id;if(i||!s||!s.length)return null;var O=this.state.isAnimationFinished,y=s.length===1,_=ee("recharts-area",u),d=c&&c.allowDataOverflow,m=h&&h.allowDataOverflow,b=d||m,w=q(v)?this.id:v,P=(a=R(o,!1))!==null&&a!==void 0?a:{r:3,strokeWidth:2},N=P.r,k=N===void 0?3:N,j=P.strokeWidth,I=j===void 0?2:j,V=se(o)?o:{},Y=V.clipDot,H=Y===void 0?!0:Y,z=k*2+I;return p.createElement(W,{className:_},d||m?p.createElement("defs",null,p.createElement("clipPath",{id:"clipPath-".concat(w)},p.createElement("rect",{x:d?f:f-A/2,y:m?l:l-g/2,width:d?A:A*2,height:m?g:g*2})),!H&&p.createElement("clipPath",{id:"clipPath-dots-".concat(w)},p.createElement("rect",{x:f-z/2,y:l-z/2,width:A+z,height:g+z}))):null,y?null:this.renderArea(b,w),(o||y)&&this.renderDots(b,H,w),(!x||O)&&le.renderCallByParent(this.props,s))}}],[{key:"getDerivedStateFromProps",value:function(a,n){return a.animationId!==n.prevAnimationId?{prevAnimationId:a.animationId,curPoints:a.points,curBaseLine:a.baseLine,prevPoints:n.curPoints,prevBaseLine:n.curBaseLine}:a.points!==n.curPoints||a.baseLine!==n.curBaseLine?{curPoints:a.points,curBaseLine:a.baseLine}:null}}])}(ve.PureComponent);te=L;E(L,"displayName","Area");E(L,"defaultProps",{stroke:"#3182bd",fill:"#3182bd",fillOpacity:.6,xAxisId:0,yAxisId:0,legendType:"line",connectNulls:!1,points:[],dot:!1,activeDot:!0,hide:!1,isAnimationActive:!ce.isSsr,animationBegin:0,animationDuration:1500,animationEasing:"ease"});E(L,"getBaseValue",function(e,t,r,a){var n=e.layout,i=e.baseValue,o=t.props.baseValue,s=o??i;if(C(s)&&typeof s=="number")return s;var u=n==="horizontal"?a:r,l=u.scale.domain();if(u.type==="number"){var f=Math.max(l[0],l[1]),c=Math.min(l[0],l[1]);return s==="dataMin"?c:s==="dataMax"||f<0?f:Math.max(Math.min(l[0],l[1]),0)}return s==="dataMin"?l[0]:s==="dataMax"?l[1]:l[0]});E(L,"getComposedData",function(e){var t=e.props,r=e.item,a=e.xAxis,n=e.yAxis,i=e.xAxisTicks,o=e.yAxisTicks,s=e.bandSize,u=e.dataKey,l=e.stackedData,f=e.dataStartIndex,c=e.displayedData,h=e.offset,A=t.layout,g=l&&l.length,x=te.getBaseValue(t,r,a,n),v=A==="horizontal",O=!1,y=c.map(function(d,m){var b;g?b=l[f+m]:(b=J(d,u),Array.isArray(b)?O=!0:b=[x,b]);var w=b[1]==null||g&&J(d,u)==null;return v?{x:Q({axis:a,ticks:i,bandSize:s,entry:d,index:m}),y:w?null:n.scale(b[1]),value:b,payload:d}:{x:w?null:a.scale(b[1]),y:Q({axis:n,ticks:o,bandSize:s,entry:d,index:m}),value:b,payload:d}}),_;return g||O?_=y.map(function(d){var m=Array.isArray(d.value)?d.value[0]:null;return v?{x:d.x,y:m!=null&&d.y!=null?n.scale(m):null}:{x:m!=null?a.scale(m):null,y:d.y}}):_=v?n.scale(x):a.scale(x),S({points:y,baseLine:_,layout:A,isRange:O},h)});E(L,"renderDotItem",function(e,t){var r;if(p.isValidElement(e))r=p.cloneElement(e,t);else if(K(e))r=e(t);else{var a=ee("recharts-area-dot",typeof e!="boolean"?e.className:""),n=t.key,i=ae(t,be);r=p.createElement(fe,D({},i,{key:n,className:a}))}return r});var ke=pe({chartName:"AreaChart",GraphicalChild:L,axisComponents:[{axisType:"xAxis",AxisComp:he},{axisType:"yAxis",AxisComp:de}],formatAxisMap:me});export{ke as A,L as a};
2
- //# sourceMappingURL=AreaChart-CrmTcE7w.js.map
1
+ import{b as R,c as W,m as T,i as C,d as F,A as ie,e as B,h as q,j as oe,k as G,l as se,n as le,u as ue,o as K,G as ce,p as J,q as Q,D as fe,g as pe,X as he,Y as de,f as me}from"./generateCategoricalChart-BsIxUDyw.js";import{R as p,F as ee,r as ve}from"./index-B93cewiX.js";var ye=["layout","type","stroke","connectNulls","isRange","ref"],be=["key"],te;function M(e){"@babel/helpers - typeof";return M=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},M(e)}function ae(e,t){if(e==null)return{};var r=Ae(e,t),a,n;if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)a=i[n],!(t.indexOf(a)>=0)&&Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}function Ae(e,t){if(e==null)return{};var r={};for(var a in e)if(Object.prototype.hasOwnProperty.call(e,a)){if(t.indexOf(a)>=0)continue;r[a]=e[a]}return r}function D(){return D=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e},D.apply(this,arguments)}function U(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable})),r.push.apply(r,a)}return r}function S(e){for(var t=1;t<arguments.length;t++){var r=arguments[t]!=null?arguments[t]:{};t%2?U(Object(r),!0).forEach(function(a){E(e,a,r[a])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):U(Object(r)).forEach(function(a){Object.defineProperty(e,a,Object.getOwnPropertyDescriptor(r,a))})}return e}function ge(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Z(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,ne(a.key),a)}}function xe(e,t,r){return t&&Z(e.prototype,t),r&&Z(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function Pe(e,t,r){return t=$(t),Oe(e,re()?Reflect.construct(t,r||[],$(e).constructor):t.apply(e,r))}function Oe(e,t){if(t&&(M(t)==="object"||typeof t=="function"))return t;if(t!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return Ee(e)}function Ee(e){if(e===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function re(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(re=function(){return!!e})()}function $(e){return $=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(r){return r.__proto__||Object.getPrototypeOf(r)},$(e)}function we(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&X(e,t)}function X(e,t){return X=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(a,n){return a.__proto__=n,a},X(e,t)}function E(e,t,r){return t=ne(t),t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function ne(e){var t=_e(e,"string");return M(t)=="symbol"?t:t+""}function _e(e,t){if(M(e)!="object"||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var a=r.call(e,t);if(M(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}var L=function(e){function t(){var r;ge(this,t);for(var a=arguments.length,n=new Array(a),i=0;i<a;i++)n[i]=arguments[i];return r=Pe(this,t,[].concat(n)),E(r,"state",{isAnimationFinished:!0}),E(r,"id",ue("recharts-area-")),E(r,"handleAnimationEnd",function(){var o=r.props.onAnimationEnd;r.setState({isAnimationFinished:!0}),K(o)&&o()}),E(r,"handleAnimationStart",function(){var o=r.props.onAnimationStart;r.setState({isAnimationFinished:!1}),K(o)&&o()}),r}return we(t,e),xe(t,[{key:"renderDots",value:function(a,n,i){var o=this.props.isAnimationActive,s=this.state.isAnimationFinished;if(o&&!s)return null;var u=this.props,l=u.dot,f=u.points,c=u.dataKey,h=R(this.props,!1),A=R(l,!0),g=f.map(function(v,O){var y=S(S(S({key:"dot-".concat(O),r:3},h),A),{},{index:O,cx:v.x,cy:v.y,dataKey:c,value:v.value,payload:v.payload,points:f});return t.renderDotItem(l,y)}),x={clipPath:a?"url(#clipPath-".concat(n?"":"dots-").concat(i,")"):null};return p.createElement(W,D({className:"recharts-area-dots"},x),g)}},{key:"renderHorizontalRect",value:function(a){var n=this.props,i=n.baseLine,o=n.points,s=n.strokeWidth,u=o[0].x,l=o[o.length-1].x,f=a*Math.abs(u-l),c=T(o.map(function(h){return h.y||0}));return C(i)&&typeof i=="number"?c=Math.max(i,c):i&&Array.isArray(i)&&i.length&&(c=Math.max(T(i.map(function(h){return h.y||0})),c)),C(c)?p.createElement("rect",{x:u<l?u:u-f,y:0,width:f,height:Math.floor(c+(s?parseInt("".concat(s),10):1))}):null}},{key:"renderVerticalRect",value:function(a){var n=this.props,i=n.baseLine,o=n.points,s=n.strokeWidth,u=o[0].y,l=o[o.length-1].y,f=a*Math.abs(u-l),c=T(o.map(function(h){return h.x||0}));return C(i)&&typeof i=="number"?c=Math.max(i,c):i&&Array.isArray(i)&&i.length&&(c=Math.max(T(i.map(function(h){return h.x||0})),c)),C(c)?p.createElement("rect",{x:0,y:u<l?u:u-f,width:c+(s?parseInt("".concat(s),10):1),height:Math.floor(f)}):null}},{key:"renderClipRect",value:function(a){var n=this.props.layout;return n==="vertical"?this.renderVerticalRect(a):this.renderHorizontalRect(a)}},{key:"renderAreaStatically",value:function(a,n,i,o){var s=this.props,u=s.layout,l=s.type,f=s.stroke,c=s.connectNulls,h=s.isRange;s.ref;var A=ae(s,ye);return p.createElement(W,{clipPath:i?"url(#clipPath-".concat(o,")"):null},p.createElement(F,D({},R(A,!0),{points:a,connectNulls:c,type:l,baseLine:n,layout:u,stroke:"none",className:"recharts-area-area"})),f!=="none"&&p.createElement(F,D({},R(this.props,!1),{className:"recharts-area-curve",layout:u,type:l,connectNulls:c,fill:"none",points:a})),f!=="none"&&h&&p.createElement(F,D({},R(this.props,!1),{className:"recharts-area-curve",layout:u,type:l,connectNulls:c,fill:"none",points:n})))}},{key:"renderAreaWithAnimation",value:function(a,n){var i=this,o=this.props,s=o.points,u=o.baseLine,l=o.isAnimationActive,f=o.animationBegin,c=o.animationDuration,h=o.animationEasing,A=o.animationId,g=this.state,x=g.prevPoints,v=g.prevBaseLine;return p.createElement(ie,{begin:f,duration:c,isActive:l,easing:h,from:{t:0},to:{t:1},key:"area-".concat(A),onAnimationEnd:this.handleAnimationEnd,onAnimationStart:this.handleAnimationStart},function(O){var y=O.t;if(x){var _=x.length/s.length,d=s.map(function(P,N){var k=Math.floor(N*_);if(x[k]){var j=x[k],I=B(j.x,P.x),V=B(j.y,P.y);return S(S({},P),{},{x:I(y),y:V(y)})}return P}),m;if(C(u)&&typeof u=="number"){var b=B(v,u);m=b(y)}else if(q(u)||oe(u)){var w=B(v,0);m=w(y)}else m=u.map(function(P,N){var k=Math.floor(N*_);if(v[k]){var j=v[k],I=B(j.x,P.x),V=B(j.y,P.y);return S(S({},P),{},{x:I(y),y:V(y)})}return P});return i.renderAreaStatically(d,m,a,n)}return p.createElement(W,null,p.createElement("defs",null,p.createElement("clipPath",{id:"animationClipPath-".concat(n)},i.renderClipRect(y))),p.createElement(W,{clipPath:"url(#animationClipPath-".concat(n,")")},i.renderAreaStatically(s,u,a,n)))})}},{key:"renderArea",value:function(a,n){var i=this.props,o=i.points,s=i.baseLine,u=i.isAnimationActive,l=this.state,f=l.prevPoints,c=l.prevBaseLine,h=l.totalLength;return u&&o&&o.length&&(!f&&h>0||!G(f,o)||!G(c,s))?this.renderAreaWithAnimation(a,n):this.renderAreaStatically(o,s,a,n)}},{key:"render",value:function(){var a,n=this.props,i=n.hide,o=n.dot,s=n.points,u=n.className,l=n.top,f=n.left,c=n.xAxis,h=n.yAxis,A=n.width,g=n.height,x=n.isAnimationActive,v=n.id;if(i||!s||!s.length)return null;var O=this.state.isAnimationFinished,y=s.length===1,_=ee("recharts-area",u),d=c&&c.allowDataOverflow,m=h&&h.allowDataOverflow,b=d||m,w=q(v)?this.id:v,P=(a=R(o,!1))!==null&&a!==void 0?a:{r:3,strokeWidth:2},N=P.r,k=N===void 0?3:N,j=P.strokeWidth,I=j===void 0?2:j,V=se(o)?o:{},Y=V.clipDot,H=Y===void 0?!0:Y,z=k*2+I;return p.createElement(W,{className:_},d||m?p.createElement("defs",null,p.createElement("clipPath",{id:"clipPath-".concat(w)},p.createElement("rect",{x:d?f:f-A/2,y:m?l:l-g/2,width:d?A:A*2,height:m?g:g*2})),!H&&p.createElement("clipPath",{id:"clipPath-dots-".concat(w)},p.createElement("rect",{x:f-z/2,y:l-z/2,width:A+z,height:g+z}))):null,y?null:this.renderArea(b,w),(o||y)&&this.renderDots(b,H,w),(!x||O)&&le.renderCallByParent(this.props,s))}}],[{key:"getDerivedStateFromProps",value:function(a,n){return a.animationId!==n.prevAnimationId?{prevAnimationId:a.animationId,curPoints:a.points,curBaseLine:a.baseLine,prevPoints:n.curPoints,prevBaseLine:n.curBaseLine}:a.points!==n.curPoints||a.baseLine!==n.curBaseLine?{curPoints:a.points,curBaseLine:a.baseLine}:null}}])}(ve.PureComponent);te=L;E(L,"displayName","Area");E(L,"defaultProps",{stroke:"#3182bd",fill:"#3182bd",fillOpacity:.6,xAxisId:0,yAxisId:0,legendType:"line",connectNulls:!1,points:[],dot:!1,activeDot:!0,hide:!1,isAnimationActive:!ce.isSsr,animationBegin:0,animationDuration:1500,animationEasing:"ease"});E(L,"getBaseValue",function(e,t,r,a){var n=e.layout,i=e.baseValue,o=t.props.baseValue,s=o??i;if(C(s)&&typeof s=="number")return s;var u=n==="horizontal"?a:r,l=u.scale.domain();if(u.type==="number"){var f=Math.max(l[0],l[1]),c=Math.min(l[0],l[1]);return s==="dataMin"?c:s==="dataMax"||f<0?f:Math.max(Math.min(l[0],l[1]),0)}return s==="dataMin"?l[0]:s==="dataMax"?l[1]:l[0]});E(L,"getComposedData",function(e){var t=e.props,r=e.item,a=e.xAxis,n=e.yAxis,i=e.xAxisTicks,o=e.yAxisTicks,s=e.bandSize,u=e.dataKey,l=e.stackedData,f=e.dataStartIndex,c=e.displayedData,h=e.offset,A=t.layout,g=l&&l.length,x=te.getBaseValue(t,r,a,n),v=A==="horizontal",O=!1,y=c.map(function(d,m){var b;g?b=l[f+m]:(b=J(d,u),Array.isArray(b)?O=!0:b=[x,b]);var w=b[1]==null||g&&J(d,u)==null;return v?{x:Q({axis:a,ticks:i,bandSize:s,entry:d,index:m}),y:w?null:n.scale(b[1]),value:b,payload:d}:{x:w?null:a.scale(b[1]),y:Q({axis:n,ticks:o,bandSize:s,entry:d,index:m}),value:b,payload:d}}),_;return g||O?_=y.map(function(d){var m=Array.isArray(d.value)?d.value[0]:null;return v?{x:d.x,y:m!=null&&d.y!=null?n.scale(m):null}:{x:m!=null?a.scale(m):null,y:d.y}}):_=v?n.scale(x):a.scale(x),S({points:y,baseLine:_,layout:A,isRange:O},h)});E(L,"renderDotItem",function(e,t){var r;if(p.isValidElement(e))r=p.cloneElement(e,t);else if(K(e))r=e(t);else{var a=ee("recharts-area-dot",typeof e!="boolean"?e.className:""),n=t.key,i=ae(t,be);r=p.createElement(fe,D({},i,{key:n,className:a}))}return r});var ke=pe({chartName:"AreaChart",GraphicalChild:L,axisComponents:[{axisType:"xAxis",AxisComp:he},{axisType:"yAxis",AxisComp:de}],formatAxisMap:me});export{ke as A,L as a};
2
+ //# sourceMappingURL=AreaChart-Y_X0Bbjd.js.map