pi-mega-compact 0.8.23 → 0.8.24
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/api-contracts/endpoints.js +8 -0
- package/dist/extensions/dashboard-server/api-contracts/game-types.js +7 -0
- package/dist/extensions/mega-runtime/append-event.js +24 -0
- package/dist/extensions/mega-runtime/bind-repo.js +65 -0
- package/dist/extensions/mega-runtime/capture-model.js +87 -0
- package/dist/extensions/mega-runtime/dashboard-snapshot.js +118 -0
- package/dist/extensions/mega-runtime/effects.js +86 -0
- package/dist/extensions/mega-runtime/engine-view.js +11 -0
- package/dist/extensions/mega-runtime/game-state.js +116 -0
- package/dist/extensions/mega-runtime/get-state-dir.js +10 -0
- package/dist/extensions/mega-runtime/perf.js +49 -0
- package/dist/extensions/mega-runtime/pressure-getters.js +64 -0
- package/dist/extensions/mega-runtime/render-widget.js +17 -0
- package/dist/extensions/mega-runtime/reset-runtime.js +50 -0
- package/dist/extensions/mega-runtime/runtime-helpers.js +73 -0
- package/dist/extensions/mega-runtime/runtime-snapshot.js +204 -0
- package/dist/extensions/mega-runtime/runtime.js +352 -0
- package/dist/extensions/mega-runtime/snapshot.js +142 -0
- package/dist/extensions/mega-runtime/state.js +5 -1151
- package/dist/extensions/mega-runtime/status.js +11 -0
- package/dist/extensions/mega-runtime/widget-ansi.js +207 -0
- package/dist/extensions/mega-runtime/widget-types.js +8 -0
- package/dist/extensions/mega-runtime/widget.js +15 -204
- package/dist/extensions/openclaw-mega-compact.js +291 -0
- package/dist/src/minilm.js +92 -0
- package/dist/src/wordpiece.js +129 -0
- package/extensions/dashboard-client/dist/assets/index-D_WtU2TV.js.map +1 -1
- package/extensions/dashboard-server/api-contracts/endpoints.ts +30 -155
- package/extensions/dashboard-server/api-contracts/game-types.ts +172 -0
- package/extensions/mega-runtime/DECOMPOSITION.md +180 -0
- package/extensions/mega-runtime/README.md +38 -0
- package/extensions/mega-runtime/append-event.ts +40 -0
- package/extensions/mega-runtime/bind-repo.ts +81 -0
- package/extensions/mega-runtime/capture-model.ts +101 -0
- package/extensions/mega-runtime/dashboard-snapshot.ts +173 -0
- package/extensions/mega-runtime/effects.ts +129 -0
- package/extensions/mega-runtime/engine-view.ts +17 -0
- package/extensions/mega-runtime/game-state.ts +149 -0
- package/extensions/mega-runtime/get-state-dir.ts +19 -0
- package/extensions/mega-runtime/perf.ts +60 -0
- package/extensions/mega-runtime/pressure-getters.ts +96 -0
- package/extensions/mega-runtime/render-widget.ts +41 -0
- package/extensions/mega-runtime/reset-runtime.ts +80 -0
- package/extensions/mega-runtime/runtime-helpers.ts +119 -0
- package/extensions/mega-runtime/runtime-snapshot.ts +289 -0
- package/extensions/mega-runtime/runtime.ts +437 -0
- package/extensions/mega-runtime/snapshot.ts +230 -0
- package/extensions/mega-runtime/state.ts +5 -1268
- package/extensions/mega-runtime/status.ts +26 -0
- package/extensions/mega-runtime/widget-ansi.ts +217 -0
- package/extensions/mega-runtime/widget-types.ts +80 -0
- package/extensions/mega-runtime/widget.ts +34 -285
- package/package.json +1 -1
- package/dist/extensions/dashboard-client/src/hooks/useApi.js +0 -51
- package/dist/extensions/dashboard-client/src/hooks/useSSE.js +0 -63
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render-widget.ts — extracted `MegaRuntime.renderWidget()`: the width-aware
|
|
3
|
+
* above-editor widget factory registration. Same thin-delegate pattern as the
|
|
4
|
+
* other runtime.ts extractions.
|
|
5
|
+
*/
|
|
6
|
+
import { WIDGET_KEY } from "./helpers.js";
|
|
7
|
+
import { buildWidgetLines } from "./widget.js";
|
|
8
|
+
// -------------------------------------------------------------- renderWidget
|
|
9
|
+
/** Register the above-editor widget as a width-aware factory so pi re-renders
|
|
10
|
+
* it at the REAL terminal width every frame (auto-fit wide/narrow). The
|
|
11
|
+
* factory returns a minimal Component whose render() reads self.widgetData. */
|
|
12
|
+
export function renderWidgetImpl(self, ctx) {
|
|
13
|
+
ctx.ui.setWidget(WIDGET_KEY, (_tui, _theme) => ({
|
|
14
|
+
render: (width) => buildWidgetLines(self.widgetData, width > 0 ? width : 200, self.activeAgents),
|
|
15
|
+
invalidate: () => { },
|
|
16
|
+
}), { placement: "aboveEditor" });
|
|
17
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* reset-runtime.ts — extracted `MegaRuntime.resetRuntime()`: the per-session
|
|
3
|
+
* state reset used by the session_start / session_tree handlers. The class
|
|
4
|
+
* keeps a thin `resetRuntimeImpl(this, sessionId)` delegate so every call
|
|
5
|
+
* site is unchanged.
|
|
6
|
+
*
|
|
7
|
+
* Follows the same context-interface + free-function + thin-delegate pattern as
|
|
8
|
+
* effects.ts / game-state.ts / capture-model.ts / bind-repo.ts / perf.ts /
|
|
9
|
+
* runtime-helpers.ts.
|
|
10
|
+
*/
|
|
11
|
+
import { normalizeSessionId } from "../../src/store.js";
|
|
12
|
+
// --------------------------------------------------------------- resetRuntime
|
|
13
|
+
export function resetRuntimeImpl(self, sessionId) {
|
|
14
|
+
const sid = normalizeSessionId(sessionId);
|
|
15
|
+
if (self.rt.sessionId === sid && self.rt.persistedThisSession)
|
|
16
|
+
return; // same session, keep checkpoint memory
|
|
17
|
+
self.rt = {
|
|
18
|
+
sessionId: sid,
|
|
19
|
+
persistedThisSession: false,
|
|
20
|
+
lastCheckpointId: undefined,
|
|
21
|
+
lastCompactedFrom: 0,
|
|
22
|
+
lastCompactedTokens: 0,
|
|
23
|
+
dedupSkips: 0,
|
|
24
|
+
dedupAttempts: 0,
|
|
25
|
+
tokensSaved: 0,
|
|
26
|
+
lastCompactAt: null,
|
|
27
|
+
lastNativeCompactAt: null,
|
|
28
|
+
compactCount: 0,
|
|
29
|
+
recallInjections: 0,
|
|
30
|
+
cacheHitTokens: 0,
|
|
31
|
+
lengthStopPending: false,
|
|
32
|
+
errorRetryCount: 0,
|
|
33
|
+
errorRetryUntil: 0,
|
|
34
|
+
consecutiveErrors: 0,
|
|
35
|
+
};
|
|
36
|
+
self.trimCache = null; // v0.8.6: never replay a stale trim into a new session
|
|
37
|
+
self.statusKey = undefined;
|
|
38
|
+
self.activeAgents = 0;
|
|
39
|
+
self.currentTurn = 0;
|
|
40
|
+
self.lastActivityAt = 0;
|
|
41
|
+
self.tierTrace = undefined;
|
|
42
|
+
self.ticker.length = 0;
|
|
43
|
+
self.pulsing = false;
|
|
44
|
+
self.savedGoal = 50_000;
|
|
45
|
+
self.lastWhy = undefined;
|
|
46
|
+
// S31 audit P2: symmetry with bindRepo — a reset can coincide with a context
|
|
47
|
+
// that re-binds the repo, so drop the memo too. Cheap; the next
|
|
48
|
+
// getCachedGameState() re-queries lazily.
|
|
49
|
+
self.cachedGameState = undefined;
|
|
50
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runtime-helpers.ts — extracted private helpers from the `MegaRuntime` class
|
|
3
|
+
* (runtime.ts) so the class body shrinks and the pure/instance logic is
|
|
4
|
+
* independently testable.
|
|
5
|
+
*
|
|
6
|
+
* Follows the same context-interface + free-function + thin-delegate pattern as
|
|
7
|
+
* effects.ts / game-state.ts / capture-model.ts / bind-repo.ts / perf.ts.
|
|
8
|
+
*/
|
|
9
|
+
import { detectCrossRepoDrift } from "../../src/driftDetection.js";
|
|
10
|
+
import { turnLevel } from "../../src/game/scoring.js";
|
|
11
|
+
// -------------------------------------------------------------- materialSig
|
|
12
|
+
/** v0.8.5: cheap material-change signature over live runtime fields (no
|
|
13
|
+
* SQLite). Two snapshots with the same signature produce identical
|
|
14
|
+
* dashboard.json + widgetData, so the 6 synchronous SQLite opens + the
|
|
15
|
+
* writeFileSync(dashboard.json) can be skipped. Built from in-memory state
|
|
16
|
+
* only; `gameStateBump` covers cross-process game_state edits (fs.watch) +
|
|
17
|
+
* in-process /mega-game writes (bumpGameState) + repo switches (bindRepo).
|
|
18
|
+
* The transient flare flags are included so a one-shot flare forces the
|
|
19
|
+
* recompute that renders (then clears) it for exactly one cycle. */
|
|
20
|
+
export function materialSigImpl(ctx) {
|
|
21
|
+
const rt = ctx.rt;
|
|
22
|
+
const ae = ctx.activeEffect;
|
|
23
|
+
return JSON.stringify([
|
|
24
|
+
ctx.lastCtxTokens, ctx.lastCtxPercent, ctx.lastCtxWindow,
|
|
25
|
+
ctx.activeAgents, ctx.currentTurn,
|
|
26
|
+
rt.compactCount, rt.tokensSaved, rt.dedupSkips, rt.dedupAttempts,
|
|
27
|
+
rt.recallInjections, rt.cacheHitTokens, rt.persistedThisSession,
|
|
28
|
+
rt.lastCheckpointId ?? null, rt.lastCompactedFrom, rt.lastCompactedTokens,
|
|
29
|
+
ctx.statusKey ?? null,
|
|
30
|
+
ctx.currentModel?.modelId ?? null, ctx.currentModel?.provider ?? null,
|
|
31
|
+
ae ? `${ae.type}:${ae.role}:${ae.startedAt}` : null,
|
|
32
|
+
ctx.gameStateBump,
|
|
33
|
+
ctx.megaCacheFlare, ctx.megaCacheFlarePct,
|
|
34
|
+
ctx.levelUpFlare, ctx.achievementFlare,
|
|
35
|
+
ctx.achievementFlareTitles.join("|"),
|
|
36
|
+
ctx.tierTrace ?? null, ctx.lastWhy ?? null, ctx.pulsing,
|
|
37
|
+
ctx.ticker.length,
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
// -------------------------------------------------------------- embedderName
|
|
41
|
+
/** Active embedder name for the memory-store line (Trigram default / MiniLM).
|
|
42
|
+
* Pure — reads only `process.env` (the same flag the embedder factory reads). */
|
|
43
|
+
export function embedderNameImpl() {
|
|
44
|
+
// MINILM_EMBEDDER flag lives in src/config/dedup.ts; read the same env var
|
|
45
|
+
// the embedder factory uses so the label matches what's actually running.
|
|
46
|
+
return process.env.MEGACOMPACT_MINILM === "true" ||
|
|
47
|
+
process.env.MEGACOMPACT_MINILM === "1"
|
|
48
|
+
? "MiniLM"
|
|
49
|
+
: "Trigram";
|
|
50
|
+
}
|
|
51
|
+
// -------------------------------------------------------------- driftStatus
|
|
52
|
+
/** Cross-repo drift status (ok | warn), cached for 30s (opens the registry DB). */
|
|
53
|
+
export function driftStatusImpl(ctx) {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
if (ctx.driftCache && now - ctx.driftCache.at < 30_000)
|
|
56
|
+
return ctx.driftCache.status;
|
|
57
|
+
let status = "ok";
|
|
58
|
+
try {
|
|
59
|
+
const report = detectCrossRepoDrift();
|
|
60
|
+
status = report.totals.warn > 0 ? "warn" : "ok";
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
status = "ok";
|
|
64
|
+
}
|
|
65
|
+
ctx.driftCache = { at: now, status };
|
|
66
|
+
return status;
|
|
67
|
+
}
|
|
68
|
+
// -------------------------------------------------------------- getTurnLevel
|
|
69
|
+
/** S33: player level for game mode — floor(log2(turns+1))+1 (gentle).
|
|
70
|
+
* Defensive: non-finite/negative collapses to 1 (never NaN). */
|
|
71
|
+
export function getTurnLevelImpl(ctx) {
|
|
72
|
+
return turnLevel(ctx.currentTurn);
|
|
73
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runtime-snapshot.ts — extracted `MegaRuntime.snapshot()` orchestration.
|
|
3
|
+
*
|
|
4
|
+
* The snapshot() body (dashboard write + S39 heartbeat + widget-data compute
|
|
5
|
+
* + flare/effect consumption + perf recording + the v0.8.5 material-change
|
|
6
|
+
* gate) is moved here verbatim. `MegaRuntime.snapshot()` becomes a thin
|
|
7
|
+
* delegate (`snapshotImpl(this, ctx)`), so the public method and every call
|
|
8
|
+
* site is unchanged.
|
|
9
|
+
*
|
|
10
|
+
* Follows the same context-interface + free-function + thin-delegate pattern
|
|
11
|
+
* as effects.ts / game-state.ts / capture-model.ts / runtime-helpers.ts.
|
|
12
|
+
* The pure widget-data computation lives in snapshot.ts (`computeMegaSnapshot`);
|
|
13
|
+
* this module is the orchestration that calls it. The pure/instance helpers
|
|
14
|
+
* materialSig/embedderName/driftStatus/getTurnLevel are called directly via
|
|
15
|
+
* their `*Impl` functions (imported from runtime-helpers.ts) — they were only
|
|
16
|
+
* ever called from snapshot(), so the in-class private delegates are removed
|
|
17
|
+
* from runtime.ts (dead code) rather than kept.
|
|
18
|
+
*/
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { vectorStats, vectorRepoStats, vectorDataInvariant, } from "../../src/vectorStore.js";
|
|
21
|
+
import { latestModelSnapshot, recordPerfSample, recordSessionHeartbeat, appendTokenSample, } from "../../src/store/sqlite.js";
|
|
22
|
+
import { resolveRepoRoot } from "../mega-config.js";
|
|
23
|
+
import { computeMegaSnapshot } from "./snapshot.js";
|
|
24
|
+
import { buildDashboardSnapshot } from "./dashboard-snapshot.js";
|
|
25
|
+
import { materialSigImpl, embedderNameImpl, driftStatusImpl, getTurnLevelImpl, } from "./runtime-helpers.js";
|
|
26
|
+
// -------------------------------------------------------------- snapshotImpl
|
|
27
|
+
/** Collect live state and write it to disk (+ paint the above-editor widget).
|
|
28
|
+
* Extracted verbatim from `MegaRuntime.snapshot()` (runtime.ts); the public
|
|
29
|
+
* method there is now `snapshotImpl(this, ctx)`. */
|
|
30
|
+
export function snapshotImpl(self, ctx) {
|
|
31
|
+
if (ctx)
|
|
32
|
+
self.lastWidgetCtx = ctx;
|
|
33
|
+
if (ctx)
|
|
34
|
+
self.bindRepo(ctx.cwd);
|
|
35
|
+
// v0.8.5: gate the expensive body (6 sync SQLite opens +
|
|
36
|
+
// writeFileSync(dashboard.json)) behind a cheap material-change signature.
|
|
37
|
+
// During typing / idle / no-compaction streaming, the 'context' event
|
|
38
|
+
// fires repeatedly with NO material change — skip the recompute + write and
|
|
39
|
+
// just re-register the (live) widget factory, which reads the cached
|
|
40
|
+
// widgetData every frame. This removes the per-event main-thread block
|
|
41
|
+
// WITHOUT changing write timing, so tests that read dashboard.json
|
|
42
|
+
// synchronously after a compaction still see it written (compaction changes
|
|
43
|
+
// compactCount/tokensSaved → the signature changes → the full recompute +
|
|
44
|
+
// write runs).
|
|
45
|
+
const sig = materialSigImpl(self);
|
|
46
|
+
if (ctx && self.widgetData && self.lastSnapshotSig === sig) {
|
|
47
|
+
self.renderWidget(ctx);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const perfT0 = performance.now();
|
|
51
|
+
const st = vectorStats(self.store, self.rt.sessionId);
|
|
52
|
+
const repo = vectorRepoStats(self.store);
|
|
53
|
+
const di = vectorDataInvariant(self.store);
|
|
54
|
+
// Effective threshold + armed/ready status for the dashboard.
|
|
55
|
+
// effectiveThresholdPct: the live fire point as a % of the window (null for
|
|
56
|
+
// `custom`, which has no tierPct). S29: honors MEGACOMPACT_AUTO_PCT_TRIGGER
|
|
57
|
+
// override so the dashboard's armed/ready match the context-handler gate
|
|
58
|
+
// (which fires on this same %). Used by armed/ready + the dashboard.
|
|
59
|
+
const effectiveThresholdPct = self.config.tierPct != null
|
|
60
|
+
? (self.config.autoPctTrigger ?? self.config.tierPct) * 100
|
|
61
|
+
: null;
|
|
62
|
+
// armed lights at/above the REAL fire point: max(effectiveThresholdPct,
|
|
63
|
+
// fastGatePct). fastGatePct already equals tierPct*100 by default, but a
|
|
64
|
+
// MEGACOMPACT_FAST_GATE_PCT override can raise it, so we take the max.
|
|
65
|
+
const armed = self.lastCtxPercent != null &&
|
|
66
|
+
self.lastCtxPercent >=
|
|
67
|
+
Math.max(effectiveThresholdPct ?? 0, self.config.fastGatePct);
|
|
68
|
+
// S29: ready mirrors the context-handler gate's basis — percent for tiered
|
|
69
|
+
// (the gate fires on pct), tokens for custom (the gate fires on tokens).
|
|
70
|
+
// Previously this always required tokens, so the dashboard could show
|
|
71
|
+
// "armed" (percent high) but never "ready" when tokens were under-reported
|
|
72
|
+
// — the same inconsistency the S29 gate fix removes.
|
|
73
|
+
const ready = self.config.tierPct != null
|
|
74
|
+
? armed && (self.lastCtxPercent ?? 0) >= (effectiveThresholdPct ?? 0)
|
|
75
|
+
: armed && (self.lastCtxTokens ?? 0) >= self.effectiveThreshold;
|
|
76
|
+
self.dashboard.snapshot(buildDashboardSnapshot({
|
|
77
|
+
config: self.config,
|
|
78
|
+
rt: self.rt,
|
|
79
|
+
pressureBand: self.pressureBand,
|
|
80
|
+
pressure: self.pressure,
|
|
81
|
+
effectiveThreshold: self.effectiveThreshold,
|
|
82
|
+
statusKey: self.statusKey,
|
|
83
|
+
lastCtxTokens: self.lastCtxTokens,
|
|
84
|
+
lastCtxPercent: self.lastCtxPercent,
|
|
85
|
+
lastCtxWindow: self.lastCtxWindow,
|
|
86
|
+
diagCtxFastGate: self.diagCtxFastGate,
|
|
87
|
+
diagLiveTrimFires: self.diagLiveTrimFires,
|
|
88
|
+
diagLiveTrimReplays: self.diagLiveTrimReplays,
|
|
89
|
+
errorRetryCount: self.rt.errorRetryCount,
|
|
90
|
+
consecutiveErrors: self.rt.consecutiveErrors,
|
|
91
|
+
ERROR_RETRY_MAX_CONSECUTIVE: self.config.maxConsecutiveErrors,
|
|
92
|
+
errorRetryHardStop: self.config.errorRetryHardStop,
|
|
93
|
+
activeAgents: self.activeAgents,
|
|
94
|
+
currentTurn: self.currentTurn,
|
|
95
|
+
currentModel: self.currentModel,
|
|
96
|
+
st,
|
|
97
|
+
repo,
|
|
98
|
+
di,
|
|
99
|
+
}));
|
|
100
|
+
const perfDiskMs = self.dashboard.lastWriteMs;
|
|
101
|
+
// S39: record a session heartbeat + token sample into the shared
|
|
102
|
+
// machine-wide index.sqlite so the dashboard can show a real-time
|
|
103
|
+
// stacked-memory graph across all active pi processes. Behind the
|
|
104
|
+
// material-change gate (this code only runs when sig changed). Non-fatal
|
|
105
|
+
// try/catch mirrors the recordPerfSample pattern below. Skip the token
|
|
106
|
+
// sample when lastCtxTokens is null (no context data yet).
|
|
107
|
+
try {
|
|
108
|
+
const repo = resolveRepoRoot(ctx?.cwd ?? self.currentStateDir) ?? self.currentStateDir;
|
|
109
|
+
recordSessionHeartbeat(process.pid, self.rt.sessionId, repo, self.currentStateDir, self.lastCtxWindow || 0);
|
|
110
|
+
if (self.lastCtxTokens != null) {
|
|
111
|
+
appendTokenSample(self.rt.sessionId, repo, self.lastCtxTokens, self.lastCtxPercent ?? 0, self.lastCtxWindow || 0, join(self.currentStateDir, "events.log"));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
/* non-fatal: S39 monitoring must never block the snapshot path */
|
|
116
|
+
}
|
|
117
|
+
// Live stats widget above the editor
|
|
118
|
+
if (ctx) {
|
|
119
|
+
// S31: game-mode state — fetched before the widget computation so the
|
|
120
|
+
// pure function gets a plain value rather than another callback.
|
|
121
|
+
const gs = self.getCachedGameState();
|
|
122
|
+
// S34: derive the level-up flare from the turn count. This side-effect
|
|
123
|
+
// check must happen BEFORE computeMegaSnapshot so the flare and the
|
|
124
|
+
// ambient effect are armed for the current frame.
|
|
125
|
+
const curLevel = getTurnLevelImpl(self);
|
|
126
|
+
if (curLevel > self.lastLevel) {
|
|
127
|
+
self.levelUpFlare = true;
|
|
128
|
+
// v0.8.3: arm a pulse border effect to celebrate the level-up.
|
|
129
|
+
self.setEffect("pulse", "accent", 1500);
|
|
130
|
+
}
|
|
131
|
+
// ── gather widget data (computed per snapshot, rendered per frame) ────
|
|
132
|
+
const modelSnap = latestModelSnapshot(self.currentStateDir);
|
|
133
|
+
const _snapResult = computeMegaSnapshot({
|
|
134
|
+
lastCtxTokens: self.lastCtxTokens,
|
|
135
|
+
lastCtxWindow: self.lastCtxWindow,
|
|
136
|
+
lastCtxPercent: self.lastCtxPercent,
|
|
137
|
+
activeAgents: self.activeAgents,
|
|
138
|
+
currentTurn: self.currentTurn,
|
|
139
|
+
statusKey: self.statusKey,
|
|
140
|
+
st,
|
|
141
|
+
repo,
|
|
142
|
+
rtTokensSaved: self.rt.tokensSaved,
|
|
143
|
+
lastCompactAt: self.rt.lastCompactAt,
|
|
144
|
+
ticker: self.ticker,
|
|
145
|
+
lastWhy: self.lastWhy,
|
|
146
|
+
tierTrace: self.tierTrace,
|
|
147
|
+
pulsing: self.pulsing,
|
|
148
|
+
getCachedGameState: () => gs,
|
|
149
|
+
getTurnLevel: () => getTurnLevelImpl(self),
|
|
150
|
+
embedderName: () => embedderNameImpl(),
|
|
151
|
+
driftStatus: () => driftStatusImpl(self),
|
|
152
|
+
megaCacheFlare: self.megaCacheFlare,
|
|
153
|
+
megaCacheFlarePct: self.megaCacheFlarePct,
|
|
154
|
+
levelUpFlare: self.levelUpFlare,
|
|
155
|
+
achievementFlare: self.achievementFlare,
|
|
156
|
+
achievementFlareTitles: self.achievementFlareTitles,
|
|
157
|
+
activeEffect: self.activeEffect,
|
|
158
|
+
lastActivityAt: self.lastActivityAt,
|
|
159
|
+
pressureBand: self.pressureBand,
|
|
160
|
+
configTier: self.config.tier,
|
|
161
|
+
ready,
|
|
162
|
+
armed,
|
|
163
|
+
modelSnap,
|
|
164
|
+
});
|
|
165
|
+
self.widgetData = _snapResult.widgetData;
|
|
166
|
+
// S33: consume the flare after copying it into widgetData so it fires
|
|
167
|
+
// for exactly one render cycle (the gag flares once, then clears).
|
|
168
|
+
self.megaCacheFlare = false;
|
|
169
|
+
self.megaCacheFlarePct = 0;
|
|
170
|
+
// S34: consume the level-up flare after one render cycle (mirrors the
|
|
171
|
+
// megaCacheFlare one-shot semantics), and advance lastLevel.
|
|
172
|
+
self.levelUpFlare = false;
|
|
173
|
+
self.lastLevel = curLevel;
|
|
174
|
+
// S35: consume the achievement-unlock flare after one render cycle
|
|
175
|
+
// (mirrors the megaCacheFlare/levelUpFlare one-shot semantics).
|
|
176
|
+
self.achievementFlare = false;
|
|
177
|
+
self.achievementFlareTitles = [];
|
|
178
|
+
// v0.8.3: expire the ambient border effect once its time window has
|
|
179
|
+
// elapsed. SEPARATE from the one-shot flares above (those are per-cycle
|
|
180
|
+
// consumes; activeEffect is time-windowed and cleared when Date.now()
|
|
181
|
+
// crosses startedAt + durationMs). The widget also defends this per-frame
|
|
182
|
+
// (effectBorderSgr returns '' once expired), so this is bookkeeping to
|
|
183
|
+
// free the slot and prevent a stale effect lingering between snapshots.
|
|
184
|
+
if (self.activeEffect &&
|
|
185
|
+
Date.now() - self.activeEffect.startedAt >=
|
|
186
|
+
self.activeEffect.durationMs) {
|
|
187
|
+
self.activeEffect = null;
|
|
188
|
+
}
|
|
189
|
+
// Auto-fit: register a factory so pi re-renders the panel at the REAL
|
|
190
|
+
// terminal width every frame (tui.columns), instead of guessing with
|
|
191
|
+
// process.stdout.columns. buildWidgetLines reads this.widgetData live.
|
|
192
|
+
self.renderWidget(ctx);
|
|
193
|
+
}
|
|
194
|
+
// v0.8.5: record the material-change signature computed at the top so the
|
|
195
|
+
// next snapshot() can skip this whole body when nothing material changed.
|
|
196
|
+
try {
|
|
197
|
+
recordPerfSample(self.currentStateDir, "db_recompute_ms", performance.now() - perfT0);
|
|
198
|
+
recordPerfSample(self.currentStateDir, "disk_write_ms", perfDiskMs);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
/* non-fatal: perf instrumentation never blocks the agent */
|
|
202
|
+
}
|
|
203
|
+
self.lastSnapshotSig = sig;
|
|
204
|
+
}
|