@gonrocca/zero-pi 0.1.39 → 0.1.40
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/extensions/zero-statusline.ts +51 -18
- package/package.json +1 -1
|
@@ -113,10 +113,21 @@ interface PiModel {
|
|
|
113
113
|
name?: string;
|
|
114
114
|
contextWindow?: number;
|
|
115
115
|
}
|
|
116
|
+
interface PiSessionEntry {
|
|
117
|
+
type?: string;
|
|
118
|
+
message?: {
|
|
119
|
+
role?: string;
|
|
120
|
+
usage?: { input?: number; output?: number; cacheRead?: number; cacheWrite?: number };
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
interface PiSessionManager {
|
|
124
|
+
getEntries?(): PiSessionEntry[];
|
|
125
|
+
}
|
|
116
126
|
interface PiCtx {
|
|
117
127
|
ui?: PiUI;
|
|
118
128
|
model?: PiModel;
|
|
119
129
|
cwd?: string;
|
|
130
|
+
sessionManager?: PiSessionManager;
|
|
120
131
|
getContextUsage?(): { tokens?: number } | null | undefined;
|
|
121
132
|
}
|
|
122
133
|
interface PiAPI {
|
|
@@ -127,9 +138,11 @@ const STATUS_ID = "zero-statusline";
|
|
|
127
138
|
const BRAND = "www.ceroclawd.com";
|
|
128
139
|
|
|
129
140
|
// ─── Session-scoped state ──────────────────────────────────────────────────
|
|
141
|
+
// Only git stays in state (it's expensive to re-shell on every render);
|
|
142
|
+
// tokens are computed fresh from sessionManager.getEntries() per render —
|
|
143
|
+
// the same pattern pi's native footer uses, so it can never double-count a
|
|
144
|
+
// streamed message_update.
|
|
130
145
|
|
|
131
|
-
let tokensIn = 0;
|
|
132
|
-
let tokensOut = 0;
|
|
133
146
|
let branch: string | undefined;
|
|
134
147
|
let added = 0;
|
|
135
148
|
let removed = 0;
|
|
@@ -137,16 +150,42 @@ let lastCtx: PiCtx | undefined;
|
|
|
137
150
|
let gitInFlight = false;
|
|
138
151
|
|
|
139
152
|
function resetSessionState(): void {
|
|
140
|
-
tokensIn = 0;
|
|
141
|
-
tokensOut = 0;
|
|
142
153
|
branch = undefined;
|
|
143
154
|
added = 0;
|
|
144
155
|
removed = 0;
|
|
145
156
|
}
|
|
146
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Sum assistant input/output tokens across the full session, matching pi's
|
|
160
|
+
* own footer logic. Defensive: a missing sessionManager or entries iteration
|
|
161
|
+
* failure yields zero, never throws.
|
|
162
|
+
*/
|
|
163
|
+
export function computeSessionTokens(
|
|
164
|
+
sessionManager: PiSessionManager | undefined,
|
|
165
|
+
): { input: number; output: number } {
|
|
166
|
+
if (!sessionManager || typeof sessionManager.getEntries !== "function") {
|
|
167
|
+
return { input: 0, output: 0 };
|
|
168
|
+
}
|
|
169
|
+
let input = 0;
|
|
170
|
+
let output = 0;
|
|
171
|
+
try {
|
|
172
|
+
for (const entry of sessionManager.getEntries()) {
|
|
173
|
+
if (entry?.type !== "message") continue;
|
|
174
|
+
if (entry.message?.role !== "assistant") continue;
|
|
175
|
+
const u = entry.message.usage;
|
|
176
|
+
if (typeof u?.input === "number" && u.input > 0) input += u.input;
|
|
177
|
+
if (typeof u?.output === "number" && u.output > 0) output += u.output;
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
// session iteration failure — return what we summed so far.
|
|
181
|
+
}
|
|
182
|
+
return { input, output };
|
|
183
|
+
}
|
|
184
|
+
|
|
147
185
|
// ─── Git read (best-effort, async, deduped) ────────────────────────────────
|
|
148
186
|
|
|
149
|
-
async function readGit(
|
|
187
|
+
async function readGit(cwdHint: string | undefined): Promise<void> {
|
|
188
|
+
const cwd = cwdHint || process.cwd();
|
|
150
189
|
if (!cwd || gitInFlight) return;
|
|
151
190
|
gitInFlight = true;
|
|
152
191
|
try {
|
|
@@ -187,10 +226,11 @@ function render(ctx: PiCtx): void {
|
|
|
187
226
|
const window = ctx.model?.contextWindow && ctx.model.contextWindow > 0 ? ctx.model.contextWindow : 200_000;
|
|
188
227
|
const used = ctx.getContextUsage?.()?.tokens;
|
|
189
228
|
const ctxPercent = typeof used === "number" && used >= 0 ? Math.min(100, (used / window) * 100) : undefined;
|
|
229
|
+
const tokens = computeSessionTokens(ctx.sessionManager);
|
|
190
230
|
const text = composeStatusline({
|
|
191
231
|
model: shortModel(ctx.model?.id ?? ctx.model?.name),
|
|
192
|
-
tokensIn,
|
|
193
|
-
tokensOut,
|
|
232
|
+
tokensIn: tokens.input,
|
|
233
|
+
tokensOut: tokens.output,
|
|
194
234
|
diffAdded: added,
|
|
195
235
|
diffRemoved: removed,
|
|
196
236
|
ctxPercent,
|
|
@@ -237,19 +277,12 @@ export default function register(pi?: unknown): void {
|
|
|
237
277
|
}
|
|
238
278
|
});
|
|
239
279
|
|
|
240
|
-
api.on("message_update", (
|
|
280
|
+
api.on("message_update", (_event, ctx) => {
|
|
241
281
|
try {
|
|
242
282
|
lastCtx = ctx;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (typeof usage.inputTokens === "number" && usage.inputTokens > 0) {
|
|
247
|
-
tokensIn += usage.inputTokens;
|
|
248
|
-
}
|
|
249
|
-
if (typeof usage.outputTokens === "number" && usage.outputTokens > 0) {
|
|
250
|
-
tokensOut += usage.outputTokens;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
283
|
+
// Tokens are computed from sessionManager.getEntries() in render() —
|
|
284
|
+
// no event-side accumulation, so a streamed message_update repeat
|
|
285
|
+
// can never double-count.
|
|
253
286
|
render(ctx);
|
|
254
287
|
} catch {
|
|
255
288
|
// never break a session
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gonrocca/zero-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"description": "zero-pi — an installable layer for pi (pi.dev): the zero spec-driven development workflow, per-phase model autotune, and skill auto-learning. Adds capability to pi without modifying pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|