pi-mega-compact 0.13.6 → 0.13.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/dashboard-server/index-reader.js +3 -1
- package/dist/extensions/dashboard-server/resolve-snapshot.js +81 -0
- package/dist/extensions/dashboard-server/routes-core.js +8 -3
- package/dist/extensions/dashboard-server/routes-repo.js +5 -1
- package/dist/extensions/dashboard-server/routes-sessions.js +6 -3
- package/dist/extensions/mega-runtime/capture-model.js +5 -2
- package/dist/src/pricing.js +8 -0
- package/extensions/dashboard-client/dist/assets/{AreaChart-KAmp_6bL.js → AreaChart-j2VOSRp6.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-KAmp_6bL.js.map → AreaChart-j2VOSRp6.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-Bk-gm-R0.js → CacheTab-Dr1HeMfc.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-Bk-gm-R0.js.map → CacheTab-Dr1HeMfc.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/EventsTab-DRG2k7FS.js +2 -0
- package/extensions/dashboard-client/dist/assets/EventsTab-DRG2k7FS.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{HealthTab-D-IzmV29.js → HealthTab-DGE9PpW3.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-D-IzmV29.js.map → HealthTab-DGE9PpW3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-CrlkCFVj.js → MaintenanceTab-DeYRhF4q.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-CrlkCFVj.js.map → MaintenanceTab-DeYRhF4q.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DHUdZ7RR.js → MemoryMapTab-DSnSO3nG.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DHUdZ7RR.js.map → MemoryMapTab-DSnSO3nG.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-B7I4HWVg.js → MetricsTab-RIzNc1Uj.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-B7I4HWVg.js.map → MetricsTab-RIzNc1Uj.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-K6j4ZI7u.js → OverviewTab-_1BJ4Bmh.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-K6j4ZI7u.js.map → OverviewTab-_1BJ4Bmh.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-BSRWhn56.js → ReposTab-DkU7ywZB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-BSRWhn56.js.map → ReposTab-DkU7ywZB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BXdizHN1.js → SessionsTab-Bq_F_aOk.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BXdizHN1.js.map → SessionsTab-Bq_F_aOk.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-CAgyfqY3.js → SetupTab-BkBD2Y97.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-CAgyfqY3.js.map → SetupTab-BkBD2Y97.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DwgCzPD9.js → TimeSavedCard-BXzFKU7C.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DwgCzPD9.js.map → TimeSavedCard-BXzFKU7C.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CVKcStBp.js → TurnsTab-Bjpp3vBH.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CVKcStBp.js.map → TurnsTab-Bjpp3vBH.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{WikiTab-BNohxTbs.js → WikiTab-dBN2EA5G.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-BNohxTbs.js.map → WikiTab-dBN2EA5G.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-B4A-_dCA.js → button-Bt0b5QVp.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-B4A-_dCA.js.map → button-Bt0b5QVp.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-Cjn7Ifoe.js → card-D5CafXtg.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-Cjn7Ifoe.js.map → card-D5CafXtg.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-CQjCLvv7.js → index-GPHRnEN7.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-CQjCLvv7.js.map → index-GPHRnEN7.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/pricing-DkYp8-vi.js +2 -0
- package/extensions/dashboard-client/dist/assets/pricing-DkYp8-vi.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{switch-CcdXX0Zm.js → switch-CY5Gwj1o.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-CcdXX0Zm.js.map → switch-CY5Gwj1o.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-KdBnFLu3.js → toggle-C9HpkIfH.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-KdBnFLu3.js.map → toggle-C9HpkIfH.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-B6t44vzc.js → useSSE-2JFrLDlE.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-B6t44vzc.js.map → useSSE-2JFrLDlE.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/components/EventStream.tsx +1 -1
- package/extensions/dashboard-server/index-reader.ts +3 -1
- package/extensions/dashboard-server/resolve-snapshot.ts +98 -0
- package/extensions/dashboard-server/routes-core.ts +8 -3
- package/extensions/dashboard-server/routes-repo.ts +5 -1
- package/extensions/dashboard-server/routes-sessions.ts +6 -3
- package/extensions/mega-runtime/capture-model.ts +5 -2
- package/package.json +1 -1
- package/src/pricing.ts +8 -0
- package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js +0 -2
- package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js +0 -2
- package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js.map +0 -1
|
@@ -7,6 +7,7 @@ import { join } from "node:path";
|
|
|
7
7
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
8
8
|
|
|
9
9
|
import { readSnapshot } from "./snapshot.js";
|
|
10
|
+
import { resolveSnapshot } from "./resolve-snapshot.js";
|
|
10
11
|
import { dashboardHtml } from "./html.js";
|
|
11
12
|
import { readIndex, getIndexDir } from "./index-reader.js";
|
|
12
13
|
import { ACTIVE_WINDOW_SEC } from "./types.js";
|
|
@@ -41,7 +42,10 @@ export function handleIndex(
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
if (req.url === "/api/snapshot") {
|
|
44
|
-
|
|
45
|
+
// Dynamically resolve the most recently active session's dashboard.json
|
|
46
|
+
// so the Overview shows live data from the repo pi is running in — not
|
|
47
|
+
// the stale launcher dir the server was started with.
|
|
48
|
+
const { snapshot: snap } = resolveSnapshot(snapshotPath, ctx.stateDir);
|
|
45
49
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
46
50
|
res.end(JSON.stringify(snap));
|
|
47
51
|
return true;
|
|
@@ -28,10 +28,13 @@ export function handleEvents(
|
|
|
28
28
|
Connection: "keep-alive",
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
// Drain existing events so the client starts with history
|
|
31
|
+
// Drain existing events so the client starts with history.
|
|
32
|
+
// Only stream events with a "type" field — events.log also contains
|
|
33
|
+
// internal monitoring rows (e.g. captureModel:recorded) that use "event"
|
|
34
|
+
// instead of "type" and aren't part of the SseEvent contract.
|
|
32
35
|
const { data: existing, offset: initialOffset } = readFrom(eventsPath, 0);
|
|
33
36
|
eventOffsetRef.value = initialOffset;
|
|
34
|
-
const lines = existing.split("\n").filter((l: string) => l.trim());
|
|
37
|
+
const lines = existing.split("\n").filter((l: string) => l.trim() && l.includes('"type"'));
|
|
35
38
|
for (const line of lines) {
|
|
36
39
|
res.write(`data: ${line}\n\n`);
|
|
37
40
|
}
|
|
@@ -44,7 +47,7 @@ export function handleEvents(
|
|
|
44
47
|
watchTimer = null;
|
|
45
48
|
const { data, offset } = readFrom(eventsPath, eventOffsetRef.value);
|
|
46
49
|
eventOffsetRef.value = offset;
|
|
47
|
-
const newLines = data.split("\n").filter((l: string) => l.trim());
|
|
50
|
+
const newLines = data.split("\n").filter((l: string) => l.trim() && l.includes('"type"'));
|
|
48
51
|
for (const line of newLines) {
|
|
49
52
|
res.write(`data: ${line}\n\n`);
|
|
50
53
|
}
|
|
@@ -10,6 +10,7 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
|
10
10
|
import { resolveRepoRoot } from "../mega-config.js";
|
|
11
11
|
import { recordModelSnapshot, recordRepoModel } from "../../src/store/sqlite.js";
|
|
12
12
|
import type { ModelSnapshot } from "../../src/store/sqlite.js";
|
|
13
|
+
import { lookupModelInputRate } from "../../src/pricing.js";
|
|
13
14
|
|
|
14
15
|
// ---------------------------------------------------------------------- types
|
|
15
16
|
|
|
@@ -42,12 +43,14 @@ export function captureModelImpl(ctx: CaptureModelContext, ectx: ExtensionContex
|
|
|
42
43
|
} catch {
|
|
43
44
|
/* optional */
|
|
44
45
|
}
|
|
46
|
+
const modelId = m.id;
|
|
47
|
+
const fallbackInput = lookupModelInputRate(modelId);
|
|
45
48
|
const snap: Omit<ModelSnapshot, "capturedAt"> = {
|
|
46
49
|
provider: m.provider,
|
|
47
50
|
providerName,
|
|
48
|
-
modelId
|
|
51
|
+
modelId,
|
|
49
52
|
modelName: m.name ?? null,
|
|
50
|
-
inputRate: m.cost?.input
|
|
53
|
+
inputRate: m.cost?.input || fallbackInput || 0,
|
|
51
54
|
outputRate: m.cost?.output ?? 0,
|
|
52
55
|
contextWindow: m.contextWindow ?? 0,
|
|
53
56
|
maxTokens: m.maxTokens ?? 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.7",
|
|
4
4
|
"description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|
package/src/pricing.ts
CHANGED
|
@@ -30,6 +30,8 @@ export const MODEL_INPUT_RATES: Record<string, number> = {
|
|
|
30
30
|
"claude-3-opus": 15e-6,
|
|
31
31
|
"claude-3-haiku": 0.25e-6,
|
|
32
32
|
"claude-3.5-haiku": 0.8e-6,
|
|
33
|
+
"claude-mythos-5": 15e-6, // Opus-class custom alias
|
|
34
|
+
"claude-fable": 5e-6, // Claude 5 mid-tier
|
|
33
35
|
"gpt-4o": 2.5e-6,
|
|
34
36
|
"gpt-4o-mini": 0.15e-6,
|
|
35
37
|
"gpt-4-turbo": 10e-6,
|
|
@@ -39,11 +41,17 @@ export const MODEL_INPUT_RATES: Record<string, number> = {
|
|
|
39
41
|
"gemini-2.0-flash": 0.1e-6,
|
|
40
42
|
"deepseek-chat": 0.14e-6,
|
|
41
43
|
"deepseek-reasoner": 0.55e-6,
|
|
44
|
+
"deepseek-v4-flash": 0.14e-6, // DeepSeek v4 flash-tier
|
|
42
45
|
codestral: 0.3e-6,
|
|
43
46
|
"mistral-large": 2e-6,
|
|
44
47
|
"llama-3.1-405b": 1.33e-6,
|
|
45
48
|
"llama-3.1-70b": 0.8e-6,
|
|
46
49
|
"llama-3.1-8b": 0.05e-6,
|
|
50
|
+
// GLM models (zai-org) — approximate open-weights pricing
|
|
51
|
+
"zai-org/GLM-5.1-FP8": 0.15e-6,
|
|
52
|
+
"zai-org/GLM-4.7-Flash": 0.1e-6,
|
|
53
|
+
"hf:zai-org/GLM-4.7-Flash": 0.1e-6,
|
|
54
|
+
"ozore/mimo-v2.5-pro": 0.5e-6,
|
|
47
55
|
};
|
|
48
56
|
|
|
49
57
|
/**
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as o,j as r,B as g}from"./index-CQjCLvv7.js";import{u as m}from"./useSSE-B6t44vzc.js";import{T as u}from"./toggle-KdBnFLu3.js";const p={compact_start:"ev-compact",compact_end:"ev-compact",compact_trigger:"ev-trigger",compact_skip:"ev-trigger",tier_changed:"ev-tier",model_changed:"ev-tier",pressure_lifted:"ev-tier",checkpoint_persisted:"ev-checkpoint",recall_inject:"ev-recall",anchors_updated:"ev-recall",config_updated:"ev-config",config_preset:"ev-config",crew_presence_changed:"ev-crew",crew_turn_changed:"ev-crew",crew_bandit_chosen:"ev-crew",session_sample:"ev-recall",game_ritual_start:"ev-game",game_ritual_stage:"ev-game",game_ritual_end:"ev-game",game_mode_changed:"ev-game",game_render:"ev-game"},_=["all","compact_start","compact_end","recall_inject","checkpoint_persisted"];function h(e){switch(e.type){case"compact_start":return`trigger=${e.trigger} session=${e.sessionId.slice(0,8)}`;case"compact_end":return`freed=${e.tokensFreed} ok=${e.success} cp=${e.checkpointId.slice(0,8)}`;case"compact_trigger":return`pressure=${e.pressure}% threshold=${e.threshold}% armed=${e.armed}`;case"compact_skip":return`reason=${e.reason}`;case"tier_changed":return`${e.from} → ${e.to} ctx=${e.contextPct}%`;case"model_changed":return`${e.providerName}/${e.model}`;case"pressure_lifted":return`${e.beforePct}% → ${e.afterPct}%`;case"checkpoint_persisted":return`cp=${e.checkpointId.slice(0,8)} sessionTok=${e.sessionTokens}`;case"recall_inject":return`q="${e.query.slice(0,40)}" chunks=${e.chunks} tok=${e.tokens}`;case"anchors_updated":return`count=${e.count} pinned=${e.pinned}`;case"config_updated":return`key=${e.key}`;case"config_preset":return`preset=${e.preset}`;case"crew_presence_changed":return`agents=${e.activeAgents} turn=${e.currentTurn}`;case"crew_turn_changed":return`turn=${e.turnIndex} agent=${e.agentName}`;case"crew_bandit_chosen":return`agent=${e.chosenAgent} score=${e.score} regret=${e.regret}`;case"session_sample":return`sess=${e.sessionId.slice(0,8)} tok=${e.tokens.toLocaleString()} ctx=${Math.round(e.percent)}%`;case"game_ritual_start":case"game_ritual_stage":case"game_ritual_end":case"game_mode_changed":case"game_render":return`stage=${e.type}`;default:return`event=${e.type}`}}function f(e){try{return new Date(e).toLocaleTimeString()}catch{return e}}function x({events:e}){const[t,n]=o.useState("all"),[a,i]=o.useState(null),l=o.useMemo(()=>(t==="all"?e:e.filter(c=>c.type===t)).slice(-200).reverse(),[e,t]);return r.jsxs("div",{className:"flex flex-col gap-3",children:[r.jsxs("div",{className:"flex flex-wrap items-center gap-2",role:"toolbar",children:[_.map(s=>r.jsx(u,{pressed:t===s,onClick:()=>n(s),children:s==="all"?"all":s.replace(/_/g," ")},s)),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[e.length," buffered"]})]}),r.jsxs("ul",{className:"flex flex-col gap-1 font-mono text-xs",children:[l.length===0&&r.jsx("li",{className:"text-muted-foreground",children:"No events yet."}),l.map((s,c)=>{const d=p[s.type]??"ev-default";return r.jsxs("li",{className:`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${d}`,onClick:()=>i(a===c?null:c),children:[r.jsx("span",{className:"ev-time",children:f(s.ts)})," ",r.jsx("span",{className:"ev-type",children:s.type})," ",r.jsx("span",{className:"ev-summary",children:h(s)}),a===c&&r.jsx("pre",{className:"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs",children:JSON.stringify(s,null,2)})]},`${s.ts}-${c}`)})]})]})}const $=["all","compact","recall","config","crew","game"];function j({active:e,onFilter:t}){return r.jsx("div",{className:"flex flex-wrap gap-2",role:"toolbar","aria-label":"Event category filter",children:$.map(n=>r.jsx(u,{pressed:e===n,onClick:()=>t(n),children:n},n))})}const w=new Set(["compact_start","compact_end","compact_trigger","compact_skip"]),k=new Set(["config_updated","config_preset","tier_changed","model_changed","pressure_lifted","anchors_updated","checkpoint_persisted"]),y=new Set(["recall_inject","hyde_executed","recall_metrics"]),S=new Set(["crew_presence_changed","crew_turn_changed","crew_bandit_chosen"]),E=new Set(["game_ritual_start","game_ritual_stage","game_ritual_end","game_mode_changed","game_render"]);function N(e,t){if(t==="all")return!0;const n=e.type;return t==="compact"?w.has(n):t==="recall"?y.has(n):t==="config"?k.has(n):t==="crew"?S.has(n):E.has(n)}function P(){const{events:e,status:t,eventCount:n}=m(),[a,i]=o.useState("all"),l=o.useMemo(()=>e.filter(d=>N(d,a)),[e,a]),s=t==="connected"?"success":t==="error"?"danger":t==="connecting"?"warning":"outline",c=t==="connected"?"connected":t==="connecting"?"connecting…":t==="error"?"error":"disconnected";return r.jsxs("div",{className:"flex flex-col gap-4",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsxs(g,{variant:s,role:"status",children:[r.jsx("span",{className:"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current","aria-hidden":"true"}),c]}),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[n," events received"]})]}),r.jsx(j,{active:a,onFilter:i}),r.jsx(x,{events:l})]})}export{P as default};
|
|
2
|
-
//# sourceMappingURL=EventsTab-Cyylo0of.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EventsTab-Cyylo0of.js","sources":["../../src/components/EventStream.tsx","../../src/components/EventCategoryFilter.tsx","../../src/tabs/EventsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/EventStream.tsx — SSE event list with filters.\n *\n * Type badge colored by event type. Timestamp + expandable detail row.\n * Type filter chips (all + per-type). Virtualization: manual windowing for\n * 500+ events (render last N visible).\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\nimport { Toggle } from \"../components/ui/toggle\";\n\nexport interface EventStreamProps {\n\tevents: SseEvent[];\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n\tcompact_start: \"ev-compact\",\n\tcompact_end: \"ev-compact\",\n\tcompact_trigger: \"ev-trigger\",\n\tcompact_skip: \"ev-trigger\",\n\ttier_changed: \"ev-tier\",\n\tmodel_changed: \"ev-tier\",\n\tpressure_lifted: \"ev-tier\",\n\tcheckpoint_persisted: \"ev-checkpoint\",\n\trecall_inject: \"ev-recall\",\n\tanchors_updated: \"ev-recall\",\n\tconfig_updated: \"ev-config\",\n\tconfig_preset: \"ev-config\",\n\tcrew_presence_changed: \"ev-crew\",\n\tcrew_turn_changed: \"ev-crew\",\n\tcrew_bandit_chosen: \"ev-crew\",\n\tsession_sample: \"ev-recall\",\n\tgame_ritual_start: \"ev-game\",\n\tgame_ritual_stage: \"ev-game\",\n\tgame_ritual_end: \"ev-game\",\n\tgame_mode_changed: \"ev-game\",\n\tgame_render: \"ev-game\",\n};\n\n// Filterable event types (spec: compact_start/end, recall_inject, checkpoint_persisted + all).\nconst FILTER_TYPES = [\n\t\"all\",\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"recall_inject\",\n\t\"checkpoint_persisted\",\n] as const;\ntype FilterType = (typeof FILTER_TYPES)[number];\n\nconst RENDER_WINDOW = 200; // render last 200 to keep DOM light under 500+ events\n\nfunction summarize(ev: SseEvent): string {\n\tswitch (ev.type) {\n\t\tcase \"compact_start\":\n\t\t\treturn `trigger=${ev.trigger} session=${ev.sessionId.slice(0, 8)}`;\n\t\tcase \"compact_end\":\n\t\t\treturn `freed=${ev.tokensFreed} ok=${ev.success} cp=${ev.checkpointId.slice(0, 8)}`;\n\t\tcase \"compact_trigger\":\n\t\t\treturn `pressure=${ev.pressure}% threshold=${ev.threshold}% armed=${ev.armed}`;\n\t\tcase \"compact_skip\":\n\t\t\treturn `reason=${ev.reason}`;\n\t\tcase \"tier_changed\":\n\t\t\treturn `${ev.from} → ${ev.to} ctx=${ev.contextPct}%`;\n\t\tcase \"model_changed\":\n\t\t\treturn `${ev.providerName}/${ev.model}`;\n\t\tcase \"pressure_lifted\":\n\t\t\treturn `${ev.beforePct}% → ${ev.afterPct}%`;\n\t\tcase \"checkpoint_persisted\":\n\t\t\treturn `cp=${ev.checkpointId.slice(0, 8)} sessionTok=${ev.sessionTokens}`;\n\t\tcase \"recall_inject\":\n\t\t\treturn `q=\"${ev.query.slice(0, 40)}\" chunks=${ev.chunks} tok=${ev.tokens}`;\n\t\tcase \"anchors_updated\":\n\t\t\treturn `count=${ev.count} pinned=${ev.pinned}`;\n\t\tcase \"config_updated\":\n\t\t\treturn `key=${ev.key}`;\n\t\tcase \"config_preset\":\n\t\t\treturn `preset=${ev.preset}`;\n\t\tcase \"crew_presence_changed\":\n\t\t\treturn `agents=${ev.activeAgents} turn=${ev.currentTurn}`;\n\t\tcase \"crew_turn_changed\":\n\t\t\treturn `turn=${ev.turnIndex} agent=${ev.agentName}`;\n\t\tcase \"crew_bandit_chosen\":\n\t\t\treturn `agent=${ev.chosenAgent} score=${ev.score} regret=${ev.regret}`;\n\t\tcase \"session_sample\":\n\t\t\treturn `sess=${ev.sessionId.slice(0, 8)} tok=${ev.tokens.toLocaleString()} ctx=${Math.round(ev.percent)}%`;\n\t\tcase \"game_ritual_start\":\n\t\tcase \"game_ritual_stage\":\n\t\tcase \"game_ritual_end\":\n\t\tcase \"game_mode_changed\":\n\t\tcase \"game_render\":\n\t\t\treturn `stage=${ev.type}`;\n\t\tdefault:\n\t\t\treturn `event=${ev.type}`;\n\t}\n}\n\nfunction formatTs(ts: string): string {\n\ttry {\n\t\tconst d = new Date(ts);\n\t\treturn d.toLocaleTimeString();\n\t} catch {\n\t\treturn ts;\n\t}\n}\n\nexport function EventStream({ events }: EventStreamProps): React.ReactElement {\n\tconst [filter, setFilter] = useState<FilterType>(\"all\");\n\tconst [expanded, setExpanded] = useState<number | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tconst list =\n\t\t\tfilter === \"all\" ? events : events.filter((e) => e.type === filter);\n\t\t// Newest first; render only the last RENDER_WINDOW to keep DOM light.\n\t\treturn list.slice(-RENDER_WINDOW).reverse();\n\t}, [events, filter]);\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t<div className=\"flex flex-wrap items-center gap-2\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<Toggle\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\tpressed={filter === t}\n\t\t\t\t\t\tonClick={() => setFilter(t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t === \"all\" ? \"all\" : t.replace(/_/g, \" \")}\n\t\t\t\t\t</Toggle>\n\t\t\t\t))}\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"flex flex-col gap-1 font-mono text-xs\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"text-muted-foreground\">No events yet.</li>\n\t\t\t\t)}\n\t\t\t\t{filtered.map((ev, idx) => {\n\t\t\t\t\tconst cls = TYPE_COLORS[ev.type] ?? \"ev-default\";\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tkey={`${ev.ts}-${idx}`}\n\t\t\t\t\t\t\tclassName={`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${cls}`}\n\t\t\t\t\t\t\tonClick={() => setExpanded(expanded === idx ? null : idx)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"ev-time\">{formatTs(ev.ts)}</span>{\" \"}\n\t\t\t\t\t\t\t<span className=\"ev-type\">{ev.type}</span>{\" \"}\n\t\t\t\t\t\t\t<span className=\"ev-summary\">{summarize(ev)}</span>\n\t\t\t\t\t\t\t{expanded === idx && (\n\t\t\t\t\t\t\t\t<pre className=\"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs\">\n\t\t\t\t\t\t\t\t\t{JSON.stringify(ev, null, 2)}\n\t\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/EventCategoryFilter.tsx\n *\n * Category-level filter chips for the Events tab. Filters SSE events by\n * category: all / compact / recall / config / crew / game.\n */\n\nimport type React from \"react\";\nimport { Toggle } from \"../components/ui/toggle\";\n\nexport type EventCategory =\n\t| \"all\"\n\t| \"compact\"\n\t| \"recall\"\n\t| \"config\"\n\t| \"crew\"\n\t| \"game\";\n\nconst CATEGORIES: readonly EventCategory[] = [\n\t\"all\",\n\t\"compact\",\n\t\"recall\",\n\t\"config\",\n\t\"crew\",\n\t\"game\",\n] as const;\n\nexport interface EventCategoryFilterProps {\n\t/** Currently active category. */\n\tactive: EventCategory;\n\t/** Called when the user selects a category. */\n\tonFilter: (category: EventCategory) => void;\n}\n\nexport function EventCategoryFilter({\n\tactive,\n\tonFilter,\n}: EventCategoryFilterProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"flex flex-wrap gap-2\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<Toggle\n\t\t\t\t\tkey={cat}\n\t\t\t\t\tpressed={active === cat}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</Toggle>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/EventsTab.tsx — Events tab (C1).\n *\n * Live SSE event stream via useSSE + EventStream with category filter chips.\n * Shows connection status + event count. Category filter uses the existing\n * EventCategoryFilter component to filter by all/compact/recall/config/crew/game.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\nimport { useSSE } from \"../hooks/useSSE\";\nimport { EventStream } from \"../components/EventStream\";\nimport {\n\tEventCategoryFilter,\n\ttype EventCategory,\n} from \"../components/EventCategoryFilter\";\nimport { Badge } from \"../components/ui/badge\";\n\nconst COMPACT_TYPES = new Set([\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"compact_trigger\",\n\t\"compact_skip\",\n]);\nconst CONFIG_TYPES = new Set([\n\t\"config_updated\",\n\t\"config_preset\",\n\t\"tier_changed\",\n\t\"model_changed\",\n\t\"pressure_lifted\",\n\t\"anchors_updated\",\n\t\"checkpoint_persisted\",\n]);\nconst RECALL_TYPES = new Set([\n\t\"recall_inject\",\n\t\"hyde_executed\",\n\t\"recall_metrics\",\n]);\nconst CREW_TYPES = new Set([\n\t\"crew_presence_changed\",\n\t\"crew_turn_changed\",\n\t\"crew_bandit_chosen\",\n]);\nconst GAME_TYPES = new Set([\n\t\"game_ritual_start\",\n\t\"game_ritual_stage\",\n\t\"game_ritual_end\",\n\t\"game_mode_changed\",\n\t\"game_render\",\n]);\n\nfunction matchesCategory(ev: SseEvent, cat: EventCategory): boolean {\n\tif (cat === \"all\") return true;\n\tconst t = ev.type;\n\tif (cat === \"compact\") return COMPACT_TYPES.has(t);\n\tif (cat === \"recall\") return RECALL_TYPES.has(t);\n\tif (cat === \"config\") return CONFIG_TYPES.has(t);\n\tif (cat === \"crew\") return CREW_TYPES.has(t);\n\treturn GAME_TYPES.has(t); // cat === \"game\"\n}\n\nexport default function EventsTab(): React.ReactElement {\n\tconst { events, status, eventCount } = useSSE();\n\tconst [category, setCategory] = useState<EventCategory>(\"all\");\n\n\tconst filtered = useMemo(\n\t\t() => events.filter((e) => matchesCategory(e, category)),\n\t\t[events, category],\n\t);\n\n\tconst statusVariant =\n\t\tstatus === \"connected\"\n\t\t\t? (\"success\" as const)\n\t\t\t: status === \"error\"\n\t\t\t\t? (\"danger\" as const)\n\t\t\t\t: status === \"connecting\"\n\t\t\t\t\t? (\"warning\" as const)\n\t\t\t\t\t: (\"outline\" as const);\n\tconst statusLabel =\n\t\tstatus === \"connected\"\n\t\t\t? \"connected\"\n\t\t\t: status === \"connecting\"\n\t\t\t\t? \"connecting…\"\n\t\t\t\t: status === \"error\"\n\t\t\t\t\t? \"error\"\n\t\t\t\t\t: \"disconnected\";\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<Badge variant={statusVariant} role=\"status\">\n\t\t\t\t\t<span className=\"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</Badge>\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t{eventCount} events received\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<EventCategoryFilter active={category} onFilter={setCategory} />\n\t\t\t<EventStream events={filtered} />\n\t\t</div>\n\t);\n}\n"],"names":["TYPE_COLORS","FILTER_TYPES","summarize","ev","formatTs","ts","EventStream","events","filter","setFilter","useState","expanded","setExpanded","filtered","useMemo","e","jsxs","t","jsx","Toggle","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","RECALL_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusVariant","statusLabel","Badge"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const n={"claude-sonnet-4":3e-6,"claude-3.5-sonnet":3e-6,"claude-3-opus":15e-6,"claude-3-haiku":25e-8,"claude-3.5-haiku":8e-7,"gpt-4o":25e-7,"gpt-4o-mini":15e-8,"gpt-4-turbo":1e-5,"gpt-3.5-turbo":5e-7,"gemini-2.5-pro":125e-8,"gemini-2.5-flash":15e-8,"gemini-2.0-flash":1e-7,"deepseek-chat":14e-8,"deepseek-reasoner":55e-8,codestral:3e-7,"mistral-large":2e-6,"llama-3.1-405b":133e-8,"llama-3.1-70b":8e-7,"llama-3.1-8b":5e-8};function r(e){if(n[e]!=null)return n[e];for(const[a,t]of Object.entries(n))if(e.startsWith(a))return t}function s(e,a,t){if(t<=0)return{cacheReadSaved:0,cacheWriteCost:0,netSaved:0};const c=e*t*.9,o=a*t*.25;return{cacheReadSaved:c,cacheWriteCost:o,netSaved:c-o}}export{s as c,r as l};
|
|
2
|
-
//# sourceMappingURL=pricing-CvE4NllQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pricing-CvE4NllQ.js","sources":["../../../../src/pricing.ts"],"sourcesContent":["/**\n * pricing.ts — Reusable pricing constants and cache-savings computation.\n *\n * Single source of truth for provider prompt-cache pricing multipliers and\n * known model input rates. Extracted from routes-cache.ts inline arithmetic.\n *\n * PREVENT-PI-004: compute-only, zero network. No SQL, no IO.\n */\n\n// ─── Cache Pricing Multipliers ──────────────────────────────────────────────\n\n/**\n * Fraction of input cost saved per cache-read token.\n * Cache reads cost 10% of full input → you save 90% of the input cost.\n */\nexport const CACHE_READ_MULTIPLIER = 0.9;\n\n/**\n * Fraction of input cost incurred as a premium per cache-write token.\n * Cache writes cost 125% of full input → a 25% premium over a normal input token.\n */\nexport const CACHE_WRITE_MULTIPLIER = 0.25;\n\n// ─── Known Model Input Rates ─────────────────────────────────────────────────\n// USD per input token. Exact-match first, then prefix-match.\n\nexport const MODEL_INPUT_RATES: Record<string, number> = {\n\t\"claude-sonnet-4\": 3e-6, // $3/Mtok\n\t\"claude-3.5-sonnet\": 3e-6,\n\t\"claude-3-opus\": 15e-6,\n\t\"claude-3-haiku\": 0.25e-6,\n\t\"claude-3.5-haiku\": 0.8e-6,\n\t\"gpt-4o\": 2.5e-6,\n\t\"gpt-4o-mini\": 0.15e-6,\n\t\"gpt-4-turbo\": 10e-6,\n\t\"gpt-3.5-turbo\": 0.5e-6,\n\t\"gemini-2.5-pro\": 1.25e-6,\n\t\"gemini-2.5-flash\": 0.15e-6,\n\t\"gemini-2.0-flash\": 0.1e-6,\n\t\"deepseek-chat\": 0.14e-6,\n\t\"deepseek-reasoner\": 0.55e-6,\n\tcodestral: 0.3e-6,\n\t\"mistral-large\": 2e-6,\n\t\"llama-3.1-405b\": 1.33e-6,\n\t\"llama-3.1-70b\": 0.8e-6,\n\t\"llama-3.1-8b\": 0.05e-6,\n};\n\n/**\n * Look up the input-token rate (USD) for a model string.\n * Exact match first, then prefix match (e.g. \"claude-sonnet-4-20250514\" → 3e-6).\n */\nexport function lookupModelInputRate(model: string): number | undefined {\n\t// exact match first\n\tif (MODEL_INPUT_RATES[model] != null) return MODEL_INPUT_RATES[model];\n\t// prefix match\n\tfor (const [key, rate] of Object.entries(MODEL_INPUT_RATES)) {\n\t\tif (model.startsWith(key)) return rate;\n\t}\n\treturn undefined;\n}\n\n// ─── Cache Savings Computation ──────────────────────────────────────────────\n\nexport interface CacheSavings {\n\treadonly cacheReadSaved: number;\n\treadonly cacheWriteCost: number;\n\treadonly netSaved: number;\n}\n\n/**\n * Compute dollar savings from provider prompt cache read/write token counts.\n *\n * @param totalCacheRead — total cache-read tokens over the lifetime.\n * @param totalCacheWrite — total cache-write tokens over the lifetime.\n * @param inputRate — the model's input-token rate in USD (e.g. 3e-6 for Sonnet).\n * @returns `{ cacheReadSaved, cacheWriteCost, netSaved }` — or all zeros when\n * `inputRate` is zero or negative.\n */\nexport function computeCacheSavings(\n\ttotalCacheRead: number,\n\ttotalCacheWrite: number,\n\tinputRate: number,\n): CacheSavings {\n\tif (inputRate <= 0) {\n\t\treturn { cacheReadSaved: 0, cacheWriteCost: 0, netSaved: 0 };\n\t}\n\tconst cacheReadSaved = totalCacheRead * inputRate * CACHE_READ_MULTIPLIER;\n\tconst cacheWriteCost = totalCacheWrite * inputRate * CACHE_WRITE_MULTIPLIER;\n\treturn {\n\t\tcacheReadSaved,\n\t\tcacheWriteCost,\n\t\tnetSaved: cacheReadSaved - cacheWriteCost,\n\t};\n}\n"],"names":["MODEL_INPUT_RATES","lookupModelInputRate","model","key","rate","computeCacheSavings","totalCacheRead","totalCacheWrite","inputRate","cacheReadSaved","cacheWriteCost"],"mappings":"AA0BO,MAAMA,EAA4C,CACxD,kBAAmB,KACnB,oBAAqB,KACrB,gBAAiB,MACjB,iBAAkB,MAClB,mBAAoB,KACpB,SAAU,MACV,cAAe,MACf,cAAe,KACf,gBAAiB,KACjB,iBAAkB,OAClB,mBAAoB,MACpB,mBAAoB,KACpB,gBAAiB,MACjB,oBAAqB,MACrB,UAAW,KACX,gBAAiB,KACjB,iBAAkB,OAClB,gBAAiB,KACjB,eAAgB,IACjB,EAMO,SAASC,EAAqBC,EAAmC,CAEvE,GAAIF,EAAkBE,CAAK,GAAK,KAAM,OAAOF,EAAkBE,CAAK,EAEpE,SAAW,CAACC,EAAKC,CAAI,IAAK,OAAO,QAAQJ,CAAiB,EACzD,GAAIE,EAAM,WAAWC,CAAG,EAAG,OAAOC,CAGpC,CAmBO,SAASC,EACfC,EACAC,EACAC,EACe,CACf,GAAIA,GAAa,EAChB,MAAO,CAAE,eAAgB,EAAG,eAAgB,EAAG,SAAU,CAAA,EAE1D,MAAMC,EAAiBH,EAAiBE,EAAY,GAC9CE,EAAiBH,EAAkBC,EAAY,IACrD,MAAO,CACN,eAAAC,EACA,eAAAC,EACA,SAAUD,EAAiBC,CAAA,CAE7B"}
|