@danypops/pi-jittor 0.1.0

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.
@@ -0,0 +1,658 @@
1
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import {
3
+ FOOTER_COMPACTION_RENDER_INTERVAL_MS,
4
+ MAX_DYNAMIC_ROUTES,
5
+ PAPYRUS_CONTEXT_INJECTION_CHANNEL,
6
+ PAPYRUS_TASK_FOCUS_CHANNEL,
7
+ CONTEXT_EVENT_DEDUP_LIMIT,
8
+ CompactionTelemetry,
9
+ papyrusContextMetric,
10
+ validatePapyrusContextInjection,
11
+ applyTaskFocusEvent,
12
+ validateTaskFocusEvent,
13
+ TASK_DOMAINS,
14
+ TASK_TYPES,
15
+ USAGE_PERIODS,
16
+ type ContextAssessment,
17
+ type MetricObservation,
18
+ type ModelCandidate,
19
+ type ModelTaskDomain,
20
+ type ModelTaskType,
21
+ type PolicyDecision,
22
+ type Route,
23
+ type RouterStatus,
24
+ type StoredMetricObservation,
25
+ type UsagePeriod,
26
+ } from "@danypops/jittor";
27
+ import { showBenchmarkPanel } from "./benchmark-tui.ts";
28
+ import { installIntegratedFooter, type CompactionProgress, type IntegratedFooterState } from "./footer.ts";
29
+ import { callJittor } from "./service-client.ts";
30
+ import { persistentEnforcementControl, type CodexRecoveryControl, type EnforcementControl, type UsageBudgetControl } from "./settings.ts";
31
+ import { showSettingsPanel } from "./settings-tui.ts";
32
+ import { buildFooterBudget, formatFooterStatus, providerBudgetMetricQuery, showJittorPanel } from "./tui.ts";
33
+ import { cacheSessionSecret, forgetSessionSecret, sessionSecretField } from "./session-identity.ts";
34
+ import { showUsagePanel } from "./usage.ts";
35
+ import { CodexRecoveryCapability, SYSTEM_RECOVERY_RUNTIME, type CodexRecoveryRuntime } from "./capabilities/codex-recovery.ts";
36
+ import { ProviderResponseTelemetry } from "./capabilities/provider-response-telemetry.ts";
37
+ import { LocalRunTelemetry } from "./capabilities/local-run-telemetry.ts";
38
+
39
+ export { formatFooterStatus } from "./tui.ts";
40
+ export type { CodexRecoveryRuntime } from "./capabilities/codex-recovery.ts";
41
+
42
+ const THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
43
+ const RECOVERY_GUIDANCE = "Run /jittor off to disable blocking, or restart the daemon with: systemctl --user restart jittor.service";
44
+
45
+ export interface JittorExtensionClient {
46
+ call(operation: string, input: unknown): Promise<any>;
47
+ }
48
+
49
+ const daemonClient: JittorExtensionClient = {
50
+ call: (operation, input) => callJittor(operation as Parameters<typeof callJittor>[0], input as never),
51
+ };
52
+
53
+ function usageBudgetControl(enforcement: EnforcementControl): UsageBudgetControl {
54
+ const candidate = enforcement as EnforcementControl & Partial<UsageBudgetControl>;
55
+ return typeof candidate.getUsageTokenBudget === "function" && typeof candidate.setUsageTokenBudget === "function"
56
+ ? {
57
+ getUsageTokenBudget: (period) => candidate.getUsageTokenBudget!(period),
58
+ setUsageTokenBudget: (period, tokens) => candidate.setUsageTokenBudget!(period, tokens),
59
+ }
60
+ : { getUsageTokenBudget: () => undefined, setUsageTokenBudget() {} };
61
+ }
62
+
63
+ function recoveryControl(enforcement: EnforcementControl): CodexRecoveryControl {
64
+ const candidate = enforcement as EnforcementControl & Partial<CodexRecoveryControl>;
65
+ const set = (candidate as Partial<CodexRecoveryControl>).setCodexRecoveryEnabled;
66
+ return typeof candidate.isCodexRecoveryEnabled === "function" && typeof set === "function"
67
+ ? {
68
+ isCodexRecoveryEnabled: () => candidate.isCodexRecoveryEnabled!(),
69
+ setCodexRecoveryEnabled: (enabled) => set.call(candidate, enabled),
70
+ }
71
+ : { isCodexRecoveryEnabled: () => false, setCodexRecoveryEnabled() {} };
72
+ }
73
+
74
+ async function recordMetrics(client: JittorExtensionClient, metrics: MetricObservation[]): Promise<void> {
75
+ if (metrics.length === 0) return;
76
+ // One atomic transaction rather than a per-metric RPC loop: a later observation in the same
77
+ // event failing validation, or the connection dropping mid-loop, must not leave this event's
78
+ // metrics partially persisted.
79
+ await client.call("metrics.record_batch", { observations: metrics });
80
+ }
81
+
82
+ async function refreshFooter(client: JittorExtensionClient, state: IntegratedFooterState, sessionId: string): Promise<void> {
83
+ const status = await client.call("router.status", { session_id: sessionId }) as RouterStatus;
84
+ const query = providerBudgetMetricQuery(status);
85
+ const metrics = query ? await client.call("metrics.query", query) as StoredMetricObservation[] : [];
86
+ state.providerBudget = buildFooterBudget(status, metrics);
87
+ state.requestRender?.();
88
+ }
89
+
90
+ function delay(milliseconds: number, signal?: AbortSignal): Promise<void> {
91
+ if (milliseconds <= 0) return Promise.resolve();
92
+ return new Promise((resolve, reject) => {
93
+ const timer = setTimeout(resolve, milliseconds);
94
+ signal?.addEventListener("abort", () => {
95
+ clearTimeout(timer);
96
+ reject(new Error("Jittor throttle cancelled"));
97
+ }, { once: true });
98
+ });
99
+ }
100
+
101
+ function routeModelAvailable(ctx: ExtensionContext, route: Route): boolean {
102
+ return ctx.modelRegistry.getAvailable().some((model) => model.provider === route.provider && model.id === route.model);
103
+ }
104
+
105
+ async function applyRoute(pi: ExtensionAPI, ctx: ExtensionContext, route: Route): Promise<boolean> {
106
+ if (!routeModelAvailable(ctx, route)) return false;
107
+ const model = ctx.modelRegistry.find(route.provider, route.model);
108
+ if (!model) return false;
109
+ if (!ctx.model || ctx.model.provider !== route.provider || ctx.model.id !== route.model) {
110
+ if (!await pi.setModel(model)) return false;
111
+ }
112
+ if (THINKING_LEVELS.has(route.thinking)) pi.setThinkingLevel(route.thinking as Parameters<ExtensionAPI["setThinkingLevel"]>[0]);
113
+ return true;
114
+ }
115
+
116
+ interface PiRouteModel {
117
+ provider: string;
118
+ id: string;
119
+ reasoning?: boolean;
120
+ thinkingLevelMap?: Partial<Record<string, unknown>>;
121
+ cost?: { input?: number; output?: number };
122
+ }
123
+
124
+ const THINKING_DESCENDING = ["max", "xhigh", "high", "medium", "low", "minimal", "off"] as const;
125
+
126
+ function supportsThinking(model: PiRouteModel, level: string): boolean {
127
+ if (!model.reasoning) return level === "off";
128
+ return model.thinkingLevelMap?.[level] !== null;
129
+ }
130
+
131
+ function modelCost(model: PiRouteModel): number {
132
+ return (model.cost?.input ?? 0) + (model.cost?.output ?? 0);
133
+ }
134
+
135
+ export function benchmarkCandidatesFromPi(models: PiRouteModel[], thinking: string): ModelCandidate[] {
136
+ const candidates: ModelCandidate[] = [];
137
+ for (const model of models) {
138
+ if (!model.provider || !model.id || candidates.some((candidate) => candidate.provider === model.provider && candidate.model === model.id)) continue;
139
+ const level = supportsThinking(model, thinking) ? thinking : "off";
140
+ candidates.push({ provider: model.provider, model: model.id, thinking: level });
141
+ if (candidates.length >= MAX_DYNAMIC_ROUTES) break;
142
+ }
143
+ return candidates;
144
+ }
145
+
146
+ export function routesFromPi(models: PiRouteModel[], current: PiRouteModel, thinking: string): Route[] {
147
+ const catalog = models
148
+ .filter((model) => model.provider.length > 0 && model.id.length > 0)
149
+ .filter((model, index, rows) => rows.findIndex((candidate) => candidate.provider === model.provider && candidate.id === model.id) === index);
150
+ if (!catalog.some((model) => model.provider === current.provider && model.id === current.id)) catalog.push(current);
151
+ const currentLevel = THINKING_DESCENDING.indexOf(thinking as typeof THINKING_DESCENDING[number]);
152
+ const lowerLevels = THINKING_DESCENDING.slice(currentLevel >= 0 ? currentLevel + 1 : 0);
153
+ const routes: Route[] = [];
154
+ const add = (route: Route): void => {
155
+ if (routes.length >= MAX_DYNAMIC_ROUTES || routes.some((candidate) => candidate.provider === route.provider && candidate.model === route.model && candidate.thinking === route.thinking)) return;
156
+ routes.push(route);
157
+ };
158
+ add({ provider: current.provider, model: current.id, thinking });
159
+ for (const level of lowerLevels) {
160
+ if (supportsThinking(current, level)) add({ provider: current.provider, model: current.id, thinking: level });
161
+ }
162
+ const alternatives = catalog
163
+ .filter((model) => model.provider !== current.provider || model.id !== current.id)
164
+ .sort((left, right) => {
165
+ const providerPriority = Number(left.provider !== current.provider) - Number(right.provider !== current.provider);
166
+ return providerPriority || modelCost(left) - modelCost(right) || left.provider.localeCompare(right.provider) || left.id.localeCompare(right.id);
167
+ });
168
+ for (const model of alternatives) {
169
+ const level = [thinking, ...lowerLevels].find((candidate) => supportsThinking(model, candidate)) ?? "off";
170
+ add({ provider: model.provider, model: model.id, thinking: level });
171
+ }
172
+ return routes;
173
+ }
174
+
175
+ async function syncAvailableRoutes(pi: ExtensionAPI, client: JittorExtensionClient, ctx: ExtensionContext): Promise<void> {
176
+ const session_id = ctx.sessionManager.getSessionId();
177
+ const secret = sessionSecretField(session_id);
178
+ if (!ctx.model) { await client.call("router.available_routes", { routes: [], session_id, ...secret }); return; }
179
+ const models = ctx.modelRegistry.getAvailable() as PiRouteModel[];
180
+ const routes = routesFromPi(models, ctx.model as PiRouteModel, pi.getThinkingLevel());
181
+ await client.call("router.available_routes", { routes, session_id, ...secret });
182
+ }
183
+
184
+ async function syncCurrentRoute(
185
+ pi: ExtensionAPI,
186
+ client: JittorExtensionClient,
187
+ ctx: ExtensionContext,
188
+ model = ctx.model,
189
+ thinking = pi.getThinkingLevel(),
190
+ ): Promise<void> {
191
+ if (!model) return;
192
+ const session_id = ctx.sessionManager.getSessionId();
193
+ await client.call("router.current_route", { provider: model.provider, model: model.id, thinking, session_id, ...sessionSecretField(session_id) });
194
+ }
195
+
196
+ function halt(ctx: ExtensionContext, reason: string): false {
197
+ ctx.ui.notify(`${reason}. ${RECOVERY_GUIDANCE}.`, "warning");
198
+ ctx.abort();
199
+ return false;
200
+ }
201
+
202
+ async function applyDecision(
203
+ pi: ExtensionAPI,
204
+ client: JittorExtensionClient,
205
+ ctx: ExtensionContext,
206
+ decision: PolicyDecision,
207
+ allowResync = true,
208
+ ): Promise<boolean> {
209
+ if (decision.action === "halt") return halt(ctx, `Jittor blocked this provider request: ${decision.reason}`);
210
+ if (decision.action === "throttle") await delay(decision.delayMs ?? 0, ctx.signal);
211
+ if (!decision.route || await applyRoute(pi, ctx, decision.route)) return true;
212
+ if (allowResync) {
213
+ await syncAvailableRoutes(pi, client, ctx);
214
+ return applyDecision(pi, client, ctx, await client.call("router.decide", { session_id: ctx.sessionManager.getSessionId() }) as PolicyDecision, false);
215
+ }
216
+ return halt(ctx, `Jittor could not apply any authenticated Pi route after ${decision.route.provider}/${decision.route.model} became unavailable`);
217
+ }
218
+
219
+ /**
220
+ * taskId, when a Papyrus task is focused, tags the metric for cost-per-task correlation. thinking
221
+ * comes from pi.getThinkingLevel() at message_end time, not from the message itself -- AssistantMessage
222
+ * has no thinking field of its own, and the level can't have changed mid-message.
223
+ */
224
+ function assistantUsageMetrics(message: unknown, observedAt: number, taskId: string | null = null, thinking: string | null = null): MetricObservation[] {
225
+ if (typeof message !== "object" || message === null || Array.isArray(message)) return [];
226
+ const value = message as Record<string, unknown>;
227
+ if (value["role"] !== "assistant" || typeof value["usage"] !== "object" || value["usage"] === null) return [];
228
+ const usage = value["usage"] as Record<string, unknown>;
229
+ const provider = typeof value["provider"] === "string" ? value["provider"] : "unknown";
230
+ const model = typeof value["model"] === "string" ? value["model"] : "unknown";
231
+ const scope = `${provider}:${model}`;
232
+ const attributes = { provider, model, ...(taskId === null ? {} : { taskId }), ...(thinking === null || thinking.length === 0 ? {} : { thinking }) };
233
+ const metrics: MetricObservation[] = [];
234
+ for (const [field, metric] of [["input", "input-tokens"], ["output", "output-tokens"], ["cacheRead", "cache-read-tokens"], ["cacheWrite", "cache-write-tokens"]] as const) {
235
+ const amount = usage[field];
236
+ if (typeof amount === "number" && Number.isFinite(amount)) metrics.push({ source: "pi", scope, metric, value: amount, unit: "tokens", observedAt, attributes });
237
+ }
238
+ const cost = typeof usage["cost"] === "object" && usage["cost"] !== null ? (usage["cost"] as Record<string, unknown>)["total"] : undefined;
239
+ if (typeof cost === "number" && Number.isFinite(cost)) metrics.push({ source: "pi", scope, metric: "cost", value: cost, unit: "usd", observedAt, attributes });
240
+ return metrics;
241
+ }
242
+
243
+ export function registerJittorExtension(
244
+ pi: ExtensionAPI,
245
+ client: JittorExtensionClient = daemonClient,
246
+ enforcement: EnforcementControl = persistentEnforcementControl(),
247
+ codexRecovery: CodexRecoveryControl = recoveryControl(enforcement),
248
+ recoveryRuntime: CodexRecoveryRuntime = SYSTEM_RECOVERY_RUNTIME,
249
+ ): void {
250
+ const footerState: IntegratedFooterState = { providerBudget: null };
251
+ const usageBudgets = usageBudgetControl(enforcement);
252
+ let compactionTelemetry = new CompactionTelemetry();
253
+ const localRunTelemetry = new LocalRunTelemetry();
254
+ const providerResponseTelemetry = new ProviderResponseTelemetry();
255
+ const codexRecoveryCapability = new CodexRecoveryCapability(pi, codexRecovery, recoveryRuntime);
256
+ const contextObservations = new Set<string>();
257
+ const stopPapyrusContext = pi.events?.on?.(PAPYRUS_CONTEXT_INJECTION_CHANNEL, (payload) => {
258
+ try {
259
+ const observation = validatePapyrusContextInjection(payload);
260
+ const observationKey = `${observation.producerId}:${observation.sequence}`;
261
+ if (contextObservations.has(observationKey)) return;
262
+ contextObservations.add(observationKey);
263
+ if (contextObservations.size > CONTEXT_EVENT_DEDUP_LIMIT) contextObservations.delete(contextObservations.values().next().value!);
264
+ compactionTelemetry.observeInjection(observation.injected.characters, observation.estimatedTokens);
265
+ void recordMetrics(client, [papyrusContextMetric(observation)]).catch(() => undefined);
266
+ } catch {
267
+ // Reject malformed or stale cross-extension observations without retaining payloads.
268
+ }
269
+ });
270
+ // Real-time cost-per-task correlation: Jittor observes Papyrus's task-focus broadcasts (Papyrus
271
+ // never depends on Jittor) and tags newly recorded token/cost metrics with the currently focused
272
+ // task id. Scoped to this Pi session: a focus change in a different concurrent session must not
273
+ // affect this one's attribution.
274
+ let currentSessionId: string | undefined;
275
+ let focusedTaskId: string | null = null;
276
+ const stopPapyrusTaskFocus = pi.events?.on?.(PAPYRUS_TASK_FOCUS_CHANNEL, (payload) => {
277
+ try {
278
+ const event = validateTaskFocusEvent(payload);
279
+ if (event.sessionId !== undefined && event.sessionId !== currentSessionId) return;
280
+ focusedTaskId = applyTaskFocusEvent(event);
281
+ } catch {
282
+ // Reject malformed or stale cross-extension events without retaining payloads or crashing the extension.
283
+ }
284
+ });
285
+ const cancelRecovery = (resetPolicy: boolean): void => codexRecoveryCapability.cancel(resetPolicy);
286
+ const recoveryStatusText = (): string => codexRecoveryCapability.statusText();
287
+ const scheduleCodexRecovery = (ctx: ExtensionContext): void => codexRecoveryCapability.scheduleIfIdle(ctx);
288
+ let compactionTimer: ReturnType<typeof setInterval> | undefined;
289
+ const finishCompactionUi = (): void => {
290
+ if (compactionTimer) clearInterval(compactionTimer);
291
+ compactionTimer = undefined;
292
+ footerState.compaction = undefined;
293
+ footerState.requestRender?.();
294
+ };
295
+ const beginCompactionUi = (ctx: ExtensionContext, signal: AbortSignal): void => {
296
+ finishCompactionUi();
297
+ const usage = ctx.getContextUsage();
298
+ const compaction: CompactionProgress = {
299
+ startedAt: Date.now(),
300
+ initialFraction: usage?.percent === null || usage?.percent === undefined ? 1 : usage.percent / 100,
301
+ estimatedMs: null,
302
+ confidence: "cold-start",
303
+ };
304
+ footerState.compaction = compaction;
305
+ // Non-blocking: compaction UI starts immediately as cold-start; if a learned estimate resolves
306
+ // before this compaction finishes (and this is still the active compaction, not a later one),
307
+ // upgrade the same progress object in place so the drain bar and status text switch to "learned".
308
+ void client.call("compaction.estimate", {}).then((estimate) => {
309
+ if (footerState.compaction !== compaction || estimate.confidence !== "learned" || estimate.ms === null) return;
310
+ footerState.compaction = { ...compaction, estimatedMs: estimate.ms, confidence: "learned" };
311
+ footerState.requestRender?.();
312
+ }).catch(() => undefined);
313
+ compactionTimer = setInterval(() => footerState.requestRender?.(), FOOTER_COMPACTION_RENDER_INTERVAL_MS);
314
+ signal.addEventListener("abort", finishCompactionUi, { once: true });
315
+ if (signal.aborted) finishCompactionUi();
316
+ else footerState.requestRender?.();
317
+ };
318
+ const showFooter = (ctx: ExtensionContext): void => {
319
+ if (enforcement.isFooterEnabled()) installIntegratedFooter(ctx, footerState, () => pi.getThinkingLevel());
320
+ else ctx.ui.setFooter(undefined);
321
+ };
322
+ const disable = (ctx: ExtensionContext): void => {
323
+ enforcement.setEnabled(false);
324
+ ctx.ui.setStatus("jittor", undefined);
325
+ showFooter(ctx);
326
+ ctx.ui.notify("Jittor enforcement is off (monitor-only); the informational footer remains independent and provider requests will not be blocked.", "warning");
327
+ };
328
+ const enable = async (ctx: ExtensionContext): Promise<void> => {
329
+ try {
330
+ await syncCurrentRoute(pi, client, ctx);
331
+ await syncAvailableRoutes(pi, client, ctx);
332
+ await client.call("telemetry.poll", {});
333
+ const readinessDecision = await client.call("router.decide", { session_id: ctx.sessionManager.getSessionId() }) as PolicyDecision;
334
+ if (readinessDecision.action === "halt") throw new Error(readinessDecision.reason);
335
+ enforcement.setEnabled(true);
336
+ showFooter(ctx);
337
+ await refreshFooter(client, footerState, ctx.sessionManager.getSessionId());
338
+ ctx.ui.notify("Jittor enforcement enabled.", "info");
339
+ } catch (error) {
340
+ enforcement.setEnabled(false);
341
+ showFooter(ctx);
342
+ const reason = error instanceof Error ? error.message : "readiness failed";
343
+ ctx.ui.notify(`Jittor remains monitor-only: ${reason}. ${RECOVERY_GUIDANCE}.`, "error");
344
+ }
345
+ };
346
+
347
+ pi.registerCommand("jittor", {
348
+ description: "Jittor settings, routing status, benchmarks, and Codex recovery controls",
349
+ handler: async (args, ctx) => {
350
+ const action = args.trim().toLowerCase();
351
+ if (action === "" || action === "settings") {
352
+ await showSettingsPanel(ctx, enforcement, codexRecovery, usageBudgets, {
353
+ setEnforcement: async (enabled) => enabled ? enable(ctx) : disable(ctx),
354
+ setFooter: async (enabled) => {
355
+ enforcement.setFooterEnabled(enabled);
356
+ showFooter(ctx);
357
+ if (enabled) await refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => undefined);
358
+ },
359
+ setRecovery: (enabled) => {
360
+ if (!enabled) cancelRecovery(true);
361
+ codexRecovery.setCodexRecoveryEnabled(enabled);
362
+ },
363
+ });
364
+ return;
365
+ }
366
+ if (action === "benchmarks" || action.startsWith("benchmarks ")) {
367
+ if (!ctx.model) {
368
+ ctx.ui.notify("No active Pi model is available for benchmark recommendations.", "warning");
369
+ return;
370
+ }
371
+ // Domain (subject matter, e.g. coding) and type (activity, e.g. research/planning) are
372
+ // independent axes -- each positional word is classified against whichever axis it
373
+ // belongs to, in either order, so "/jittor benchmarks coding research" and
374
+ // "/jittor benchmarks research coding" both work; an unmatched word is a usage error.
375
+ const requested = action.split(/\s+/).slice(1);
376
+ let requestedDomain: ModelTaskDomain | undefined;
377
+ let requestedType: ModelTaskType | undefined;
378
+ let malformed = requested.length > 2;
379
+ for (const word of requested) {
380
+ if (TASK_DOMAINS.includes(word as ModelTaskDomain) && requestedDomain === undefined) requestedDomain = word as ModelTaskDomain;
381
+ else if (TASK_TYPES.includes(word as ModelTaskType) && requestedType === undefined) requestedType = word as ModelTaskType;
382
+ else malformed = true;
383
+ }
384
+ if (malformed) {
385
+ ctx.ui.notify("Usage: /jittor benchmarks [coding|general] [research|planning|general]", "warning");
386
+ return;
387
+ }
388
+ const candidates = benchmarkCandidatesFromPi(ctx.modelRegistry.getAvailable() as PiRouteModel[], pi.getThinkingLevel());
389
+ await showBenchmarkPanel(ctx, client, candidates, `${ctx.model.provider}/${ctx.model.id}`, requestedDomain ?? "general", requestedType ?? "general");
390
+ return;
391
+ }
392
+ if (action === "outcome accepted" || action === "outcome rejected") {
393
+ const explicitOutcome = action.endsWith("accepted") ? "accepted" as const : "rejected" as const;
394
+ const outcomeMetric = localRunTelemetry.explicitOutcomeMetric(explicitOutcome);
395
+ if (!outcomeMetric) {
396
+ ctx.ui.notify("No completed local model run is available for an explicit outcome.", "warning");
397
+ return;
398
+ }
399
+ outcomeMetric.observedAt = Date.now();
400
+ await recordMetrics(client, [outcomeMetric]);
401
+ ctx.ui.notify(`Recorded explicit ${explicitOutcome} outcome for the latest local model run.`, "info");
402
+ return;
403
+ }
404
+ if (action === "recovery" || action === "recovery status") {
405
+ ctx.ui.notify(recoveryStatusText(), "info");
406
+ return;
407
+ }
408
+ if (action === "recovery on" || action === "recovery enable") {
409
+ codexRecovery.setCodexRecoveryEnabled(true);
410
+ ctx.ui.notify("Jittor Codex recovery enabled; bounded retries begin only after transient failures fully settle.", "info");
411
+ return;
412
+ }
413
+ if (action === "recovery off" || action === "recovery disable") {
414
+ cancelRecovery(true);
415
+ codexRecovery.setCodexRecoveryEnabled(false);
416
+ ctx.ui.notify("Jittor Codex recovery disabled and pending recovery cleared.", "info");
417
+ return;
418
+ }
419
+ if (action === "recovery cancel") {
420
+ cancelRecovery(true);
421
+ ctx.ui.notify(`Jittor Codex recovery cooldown and attempt window cleared; recovery remains ${codexRecovery.isCodexRecoveryEnabled() ? "on" : "off"}.`, "info");
422
+ return;
423
+ }
424
+ if (action === "off" || action === "disable") { disable(ctx); return; }
425
+ if (action === "on" || action === "enable") { await enable(ctx); return; }
426
+ if (action === "footer off" || action === "footer disable") {
427
+ enforcement.setFooterEnabled(false);
428
+ ctx.ui.setFooter(undefined);
429
+ ctx.ui.notify("Jittor footer disabled; routing enforcement is unchanged.", "info");
430
+ return;
431
+ }
432
+ if (action === "footer on" || action === "footer enable") {
433
+ enforcement.setFooterEnabled(true);
434
+ showFooter(ctx);
435
+ await refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => undefined);
436
+ ctx.ui.notify("Jittor informational footer enabled; routing enforcement is unchanged.", "info");
437
+ return;
438
+ }
439
+ if (action === "context") {
440
+ const summary = await client.call("context.assess", {}) as ContextAssessment;
441
+ const average = summary.injection.averageCharacters === null ? "unknown" : Math.round(summary.injection.averageCharacters).toLocaleString();
442
+ const p95 = summary.injection.p95Characters === null ? "unknown" : Math.round(summary.injection.p95Characters).toLocaleString();
443
+ ctx.ui.notify([
444
+ `Papyrus injection: ${summary.injection.runs} runs · avg ${average} chars · p95 ${p95} chars · unchanged ${summary.injection.unchangedRate === null ? "unknown" : `${(summary.injection.unchangedRate * 100).toFixed(1)}%`}`,
445
+ `Mix: rules ${summary.injection.ruleCharacters.toLocaleString()} chars · tasks ${summary.injection.taskCharacters.toLocaleString()} chars · estimated ${summary.injection.estimatedTokens.toLocaleString()} tokens`,
446
+ `Compactions: ${summary.compaction.completed} completed · ${summary.compaction.aborted} aborted · ${summary.compaction.perRun === null ? "unknown" : summary.compaction.perRun.toFixed(3)} per agent run · ${summary.compaction.perTurn === null ? "unknown" : summary.compaction.perTurn.toFixed(3)} per turn`,
447
+ `Completeness: ${summary.completeness}`,
448
+ ].join("\n"), "info");
449
+ return;
450
+ }
451
+ // Reached only for the explicit "status" keyword or any other unrecognized text; bare "" is
452
+ // handled above by the settings branch, so this always has a non-empty, non-settings action.
453
+ if (!enforcement.isEnabled()) {
454
+ ctx.ui.notify("Jittor is monitor-only. Run /jittor on to re-enable blocking.", "info");
455
+ return;
456
+ }
457
+ await showJittorPanel(ctx, client);
458
+ },
459
+ });
460
+
461
+ pi.registerCommand("usage", {
462
+ description: "Cumulative token/cost usage graph with hourly/daily/weekly/monthly/quarterly views",
463
+ handler: async (args, ctx) => {
464
+ const action = args.trim().toLowerCase();
465
+ if (action === "budget" || action.startsWith("budget ")) {
466
+ const [, periodText, valueText] = action.split(/\s+/);
467
+ const period = USAGE_PERIODS.some((candidate) => candidate.id === periodText) ? periodText as UsagePeriod : undefined;
468
+ if (!period) {
469
+ const values = USAGE_PERIODS.map(({ id, label }) => `${label}: ${usageBudgets.getUsageTokenBudget(id)?.toLocaleString() ?? "not configured"}`).join(" · ");
470
+ ctx.ui.notify(`Token budgets · ${values}`, "info");
471
+ return;
472
+ }
473
+ if (valueText === undefined) {
474
+ ctx.ui.notify(`${USAGE_PERIODS.find((candidate) => candidate.id === period)!.label} token budget: ${usageBudgets.getUsageTokenBudget(period)?.toLocaleString() ?? "not configured"}`, "info");
475
+ return;
476
+ }
477
+ if (valueText === "off" || valueText === "clear") {
478
+ usageBudgets.setUsageTokenBudget(period, undefined);
479
+ ctx.ui.notify(`${USAGE_PERIODS.find((candidate) => candidate.id === period)!.label} token budget cleared.`, "info");
480
+ return;
481
+ }
482
+ const tokens = Number(valueText.replaceAll(",", ""));
483
+ if (!Number.isFinite(tokens) || tokens <= 0) {
484
+ ctx.ui.notify("Usage: /usage budget <hourly|daily|weekly|monthly|quarterly> <positive-tokens|off>", "warning");
485
+ return;
486
+ }
487
+ usageBudgets.setUsageTokenBudget(period, tokens);
488
+ ctx.ui.notify(`${USAGE_PERIODS.find((candidate) => candidate.id === period)!.label} token budget set to ${tokens.toLocaleString()} tokens.`, "info");
489
+ return;
490
+ }
491
+ if (action !== "" && action !== "cost" && action !== "tokens") {
492
+ ctx.ui.notify("Usage: /usage [cost] | /usage budget <hourly|daily|weekly|monthly|quarterly> <positive-tokens|off>", "warning");
493
+ return;
494
+ }
495
+ await showUsagePanel(ctx, client, usageBudgets, Date.now(), action === "cost" ? "cost" : "tokens");
496
+ },
497
+ });
498
+
499
+ pi.on("session_start", async (_event, ctx) => {
500
+ currentSessionId = ctx.sessionManager.getSessionId();
501
+ focusedTaskId = null;
502
+ finishCompactionUi();
503
+ compactionTelemetry = new CompactionTelemetry();
504
+ localRunTelemetry.reset();
505
+ cancelRecovery(true);
506
+ providerResponseTelemetry.resetTurn();
507
+ ctx.ui.setStatus("jittor", undefined);
508
+ showFooter(ctx);
509
+ // Registered before any router-mutating call could plausibly happen, closing most of the
510
+ // first-touch registration window; best-effort -- a registration failure leaves this session
511
+ // unarmored (opt-in armor), never blocked.
512
+ try {
513
+ const { secret } = await client.call("session.register", { session_id: currentSessionId });
514
+ cacheSessionSecret(currentSessionId, secret);
515
+ } catch {
516
+ // Unarmored for this session; every router.* call still works exactly as before.
517
+ }
518
+ try {
519
+ await syncCurrentRoute(pi, client, ctx);
520
+ await syncAvailableRoutes(pi, client, ctx);
521
+ await client.call("telemetry.poll", {});
522
+ await refreshFooter(client, footerState, ctx.sessionManager.getSessionId());
523
+ } catch {
524
+ footerState.providerBudget = null;
525
+ footerState.requestRender?.();
526
+ }
527
+ });
528
+
529
+ pi.on("session_before_compact", async (event, ctx) => {
530
+ beginCompactionUi(ctx, event.signal);
531
+ const usage = ctx.getContextUsage();
532
+ const metric = compactionTelemetry.begin({
533
+ reason: event.reason,
534
+ willRetry: event.willRetry,
535
+ ...(usage?.percent === null || usage?.percent === undefined ? {} : { contextPercent: usage.percent }),
536
+ ...(usage?.tokens === null || usage?.tokens === undefined ? {} : { contextTokens: usage.tokens }),
537
+ });
538
+ await recordMetrics(client, [metric]).catch(() => undefined);
539
+ });
540
+
541
+ pi.on("session_compact", async (event) => {
542
+ finishCompactionUi();
543
+ await recordMetrics(client, [compactionTelemetry.complete({ reason: event.reason, willRetry: event.willRetry })]).catch(() => undefined);
544
+ });
545
+
546
+ pi.on("agent_settled", async (_event, ctx) => {
547
+ if (footerState.compaction) {
548
+ finishCompactionUi();
549
+ if (compactionTelemetry.hasOpenCompaction()) await recordMetrics(client, [compactionTelemetry.abort(Date.now(), "agent-settled-without-completion")]).catch(() => undefined);
550
+ }
551
+ scheduleCodexRecovery(ctx);
552
+ if (!enforcement.isFooterEnabled()) return;
553
+ try {
554
+ await syncCurrentRoute(pi, client, ctx);
555
+ await syncAvailableRoutes(pi, client, ctx);
556
+ await refreshFooter(client, footerState, ctx.sessionManager.getSessionId());
557
+ } catch {
558
+ footerState.providerBudget = null;
559
+ footerState.requestRender?.();
560
+ }
561
+ });
562
+
563
+ pi.on("input", async (event, ctx) => {
564
+ if (event.source !== "extension") cancelRecovery(true);
565
+ if (event.source === "extension" || !enforcement.isEnabled()) return { action: "continue" as const };
566
+ try {
567
+ const next = await client.call("router.decide", { session_id: ctx.sessionManager.getSessionId() }) as PolicyDecision;
568
+ if (next.action === "halt") {
569
+ ctx.ui.notify(`Jittor blocked input: ${next.reason}. ${RECOVERY_GUIDANCE}.`, "warning");
570
+ return { action: "handled" as const };
571
+ }
572
+ return { action: "continue" as const };
573
+ } catch {
574
+ ctx.ui.notify(`Jittor could not verify budget telemetry, so fail-closed enforcement blocked input. ${RECOVERY_GUIDANCE}.`, "error");
575
+ return { action: "handled" as const };
576
+ }
577
+ });
578
+
579
+ pi.on("model_select", async (event, ctx) => {
580
+ await syncCurrentRoute(pi, client, ctx, event.model).then(() => syncAvailableRoutes(pi, client, ctx)).catch(() => undefined);
581
+ if (enforcement.isFooterEnabled()) await refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => undefined);
582
+ });
583
+
584
+ pi.on("thinking_level_select", async (event, ctx) => {
585
+ await syncCurrentRoute(pi, client, ctx, ctx.model, event.level).catch(() => undefined);
586
+ });
587
+
588
+ pi.on("turn_start", async (event, ctx) => {
589
+ currentSessionId = ctx.sessionManager.getSessionId();
590
+ compactionTelemetry.observeTurn();
591
+ codexRecoveryCapability.resetTurn();
592
+ providerResponseTelemetry.resetTurn();
593
+ localRunTelemetry.beginTurn(event.timestamp);
594
+ if (!enforcement.isEnabled()) return;
595
+ try {
596
+ await syncCurrentRoute(pi, client, ctx);
597
+ await syncAvailableRoutes(pi, client, ctx);
598
+ await applyDecision(pi, client, ctx, await client.call("router.decide", { session_id: ctx.sessionManager.getSessionId() }) as PolicyDecision);
599
+ await refreshFooter(client, footerState, ctx.sessionManager.getSessionId());
600
+ } catch {
601
+ halt(ctx, "Jittor could not verify or apply a safe route");
602
+ }
603
+ });
604
+
605
+ pi.on("message_update", async (event) => {
606
+ localRunTelemetry.onMessageUpdate(event.assistantMessageEvent.type);
607
+ });
608
+
609
+ pi.on("tool_execution_end", async (event) => {
610
+ localRunTelemetry.onToolExecutionEnd(event.toolName, event.isError);
611
+ });
612
+
613
+ pi.on("after_provider_response", async (event, ctx) => {
614
+ localRunTelemetry.onProviderResponse();
615
+ if (ctx.model?.provider === "openai-codex") codexRecoveryCapability.notifyResponse(event.status, event.headers);
616
+ const notifySchemaDrift = (message: string) => { if (enforcement.isEnabled()) ctx.ui.notify(`Jittor detected ${message}. ${RECOVERY_GUIDANCE}.`, "error"); };
617
+ await providerResponseTelemetry.handleProviderResponse(client, ctx.model?.provider, event.status, event.headers, notifySchemaDrift);
618
+ if (enforcement.isFooterEnabled()) await refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => undefined);
619
+ });
620
+
621
+ pi.on("turn_end", async (event) => {
622
+ const metrics = localRunTelemetry.completeTurn(event.message, pi.getThinkingLevel());
623
+ await recordMetrics(client, metrics).catch(() => undefined);
624
+ });
625
+
626
+ pi.on("message_end", async (event, ctx) => {
627
+ if (event.message.role === "assistant") {
628
+ if (event.message.provider === "openai-codex") codexRecoveryCapability.notifyMessageEnd(event.message.stopReason, event.message.errorMessage);
629
+ await providerResponseTelemetry.handleMessageEnd(client, event.message.provider, event.message.stopReason, event.message.errorMessage);
630
+ }
631
+ const metrics = assistantUsageMetrics(event.message, Date.now(), focusedTaskId, pi.getThinkingLevel());
632
+ if (metrics.length > 0) {
633
+ const amount = (name: string): number => metrics.filter((metric) => metric.metric === name && typeof metric.value === "number").reduce((sum, metric) => sum + (metric.value ?? 0), 0);
634
+ compactionTelemetry.observeProviderUsage({ input: amount("input-tokens"), output: amount("output-tokens"), cacheRead: amount("cache-read-tokens"), cacheWrite: amount("cache-write-tokens") });
635
+ await recordMetrics(client, metrics).catch(() => undefined);
636
+ }
637
+ if (enforcement.isFooterEnabled()) await refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => undefined);
638
+ });
639
+
640
+ pi.on("session_shutdown", async (_event, ctx) => {
641
+ finishCompactionUi();
642
+ if (compactionTelemetry.hasOpenCompaction()) await recordMetrics(client, [compactionTelemetry.abort(Date.now(), "session-shutdown")]).catch(() => undefined);
643
+ stopPapyrusContext?.();
644
+ stopPapyrusTaskFocus?.();
645
+ cancelRecovery(true);
646
+ localRunTelemetry.reset();
647
+ const session_id = ctx.sessionManager.getSessionId();
648
+ const secret = sessionSecretField(session_id);
649
+ if (secret.session_secret) await client.call("session.release", { session_id, ...secret }).catch(() => undefined);
650
+ forgetSessionSecret(session_id);
651
+ ctx.ui.setStatus("jittor", undefined);
652
+ ctx.ui.setFooter(undefined);
653
+ });
654
+ }
655
+
656
+ export default function jittorExtension(pi: ExtensionAPI): void {
657
+ registerJittorExtension(pi);
658
+ }