@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.
- package/README.md +63 -0
- package/docs/USAGE_PRIOR_ART.md +64 -0
- package/extension/src/benchmark-tui.ts +113 -0
- package/extension/src/capabilities/codex-recovery.ts +130 -0
- package/extension/src/capabilities/http-headers.ts +5 -0
- package/extension/src/capabilities/local-run-telemetry.ts +103 -0
- package/extension/src/capabilities/provider-response-telemetry.ts +101 -0
- package/extension/src/footer.ts +323 -0
- package/extension/src/index.ts +658 -0
- package/extension/src/service-client.ts +28 -0
- package/extension/src/session-identity.ts +20 -0
- package/extension/src/settings-tui.ts +153 -0
- package/extension/src/settings.ts +102 -0
- package/extension/src/tui.ts +284 -0
- package/extension/src/usage.ts +324 -0
- package/package.json +35 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createRetryingClient, type RetryingClient } from "@danypops/daemon-kit/pi-client";
|
|
2
|
+
import { connectJittorClient, type JittorClient, type OperationInputs, type OperationName, type OperationOutputs } from "@danypops/jittor";
|
|
3
|
+
|
|
4
|
+
type JittorConnector = () => Promise<JittorClient>;
|
|
5
|
+
|
|
6
|
+
function defaultConnector(): Promise<JittorClient> {
|
|
7
|
+
return Promise.resolve(connectJittorClient());
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let connector: JittorConnector = defaultConnector;
|
|
11
|
+
let retrying: RetryingClient<JittorClient> = createRetryingClient(() => connector(), { label: "Jittor" });
|
|
12
|
+
|
|
13
|
+
export async function callJittor<Name extends OperationName>(
|
|
14
|
+
operation: Name,
|
|
15
|
+
input: OperationInputs[Name],
|
|
16
|
+
): Promise<OperationOutputs[Name]> {
|
|
17
|
+
return retrying.call((client) => client.call(operation, input));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function setJittorClientConnectorForTests(value: JittorConnector): void {
|
|
21
|
+
connector = value;
|
|
22
|
+
retrying = createRetryingClient(() => connector(), { label: "Jittor" });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function resetJittorClientForTests(): void {
|
|
26
|
+
connector = defaultConnector;
|
|
27
|
+
retrying = createRetryingClient(() => connector(), { label: "Jittor" });
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side cache for this extension's own session_secret, registered with the daemon at
|
|
3
|
+
* session_start and released at session_shutdown (see index.ts). Keyed by sessionId rather
|
|
4
|
+
* than a single "current" variable, matching every router call site's own explicit sessionId.
|
|
5
|
+
*/
|
|
6
|
+
const secretsBySessionId = new Map<string, string>();
|
|
7
|
+
|
|
8
|
+
export function cacheSessionSecret(sessionId: string, secret: string): void {
|
|
9
|
+
secretsBySessionId.set(sessionId, secret);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function forgetSessionSecret(sessionId: string): void {
|
|
13
|
+
secretsBySessionId.delete(sessionId);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Spread into any router-mutating request body alongside session_id -- empty object when no secret is cached for this sessionId (unregistered, or registration hasn't completed yet), matching the daemon's opt-in-armor default. */
|
|
17
|
+
export function sessionSecretField(sessionId: string | undefined): { session_secret?: string } {
|
|
18
|
+
const secret = sessionId ? secretsBySessionId.get(sessionId) : undefined;
|
|
19
|
+
return secret ? { session_secret: secret } : {};
|
|
20
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import { USAGE_PERIODS, type UsagePeriod } from "@danypops/jittor";
|
|
4
|
+
import type { CodexRecoveryControl, EnforcementControl, UsageBudgetControl } from "./settings.ts";
|
|
5
|
+
|
|
6
|
+
export interface SettingsSnapshot {
|
|
7
|
+
enforcementEnabled: boolean;
|
|
8
|
+
footerEnabled: boolean;
|
|
9
|
+
codexRecoveryEnabled: boolean;
|
|
10
|
+
usageTokenBudgets: Partial<Record<UsagePeriod, number>>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SettingsTheme {
|
|
14
|
+
fg(color: "accent" | "success" | "warning" | "error" | "muted" | "dim" | "borderMuted", text: string): string;
|
|
15
|
+
bold(text: string): string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type SettingsKey = "enforcement" | "footer" | "recovery" | `budget-${UsagePeriod}`;
|
|
19
|
+
type SettingsAction = { kind: "activate"; key: SettingsKey } | { kind: "close" };
|
|
20
|
+
|
|
21
|
+
export interface SettingsEffects {
|
|
22
|
+
setEnforcement(enabled: boolean): void | Promise<void>;
|
|
23
|
+
setFooter(enabled: boolean): void | Promise<void>;
|
|
24
|
+
setRecovery(enabled: boolean): void | Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const SETTINGS_KEYS: SettingsKey[] = [
|
|
28
|
+
"enforcement",
|
|
29
|
+
"footer",
|
|
30
|
+
"recovery",
|
|
31
|
+
...USAGE_PERIODS.map(({ id }) => `budget-${id}` as const),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
function state(enabled: boolean, theme: SettingsTheme): string {
|
|
35
|
+
return enabled ? theme.fg("success", "ON") : theme.fg("muted", "OFF");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function budgetLabel(period: UsagePeriod, snapshot: SettingsSnapshot): string {
|
|
39
|
+
const value = snapshot.usageTokenBudgets[period];
|
|
40
|
+
return value === undefined ? "not configured" : `${value.toLocaleString()} tokens`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function rowText(key: SettingsKey, snapshot: SettingsSnapshot, theme: SettingsTheme): string {
|
|
44
|
+
if (key === "enforcement") return `Routing enforcement ${state(snapshot.enforcementEnabled, theme)}`;
|
|
45
|
+
if (key === "footer") return `Informational footer ${state(snapshot.footerEnabled, theme)}`;
|
|
46
|
+
if (key === "recovery") return `Codex recovery ${state(snapshot.codexRecoveryEnabled, theme)}`;
|
|
47
|
+
const period = key.slice("budget-".length) as UsagePeriod;
|
|
48
|
+
return `${USAGE_PERIODS.find((candidate) => candidate.id === period)!.label} token budget ${budgetLabel(period, snapshot)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function settingsSnapshot(
|
|
52
|
+
enforcement: EnforcementControl,
|
|
53
|
+
recovery: CodexRecoveryControl,
|
|
54
|
+
budgets: UsageBudgetControl,
|
|
55
|
+
): SettingsSnapshot {
|
|
56
|
+
return {
|
|
57
|
+
enforcementEnabled: enforcement.isEnabled(),
|
|
58
|
+
footerEnabled: enforcement.isFooterEnabled(),
|
|
59
|
+
codexRecoveryEnabled: recovery.isCodexRecoveryEnabled(),
|
|
60
|
+
usageTokenBudgets: Object.fromEntries(USAGE_PERIODS.map(({ id }) => [id, budgets.getUsageTokenBudget(id)])),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function renderSettingsView(snapshot: SettingsSnapshot, selected: number, width: number, theme: SettingsTheme): string[] {
|
|
65
|
+
const safeWidth = Math.max(20, width);
|
|
66
|
+
const lines = [
|
|
67
|
+
theme.bold("Jittor Settings"),
|
|
68
|
+
theme.fg("dim", "Token budgets are user values; provider quotas remain separate."),
|
|
69
|
+
"",
|
|
70
|
+
];
|
|
71
|
+
for (let index = 0; index < SETTINGS_KEYS.length; index += 1) {
|
|
72
|
+
const selectedRow = index === selected;
|
|
73
|
+
const prefix = selectedRow ? theme.fg("accent", "› ") : " ";
|
|
74
|
+
lines.push(`${prefix}${rowText(SETTINGS_KEYS[index]!, snapshot, theme)}`);
|
|
75
|
+
}
|
|
76
|
+
lines.push("", theme.fg("dim", "↑/↓ select · Enter edit · Esc close"));
|
|
77
|
+
return lines.map((line) => visibleWidth(line) <= safeWidth ? line : truncateToWidth(line, safeWidth, "…"));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function plainTheme(): SettingsTheme {
|
|
81
|
+
return { fg: (_color, text) => text, bold: (text) => text };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function editBudget(ctx: ExtensionCommandContext, budgets: UsageBudgetControl, period: UsagePeriod): Promise<void> {
|
|
85
|
+
const label = USAGE_PERIODS.find((candidate) => candidate.id === period)!.label;
|
|
86
|
+
const current = budgets.getUsageTokenBudget(period);
|
|
87
|
+
const input = await ctx.ui.input(`${label} token budget`, current?.toLocaleString() ?? "positive token count or off");
|
|
88
|
+
if (input === undefined) return;
|
|
89
|
+
const normalized = input.trim().toLowerCase();
|
|
90
|
+
if (normalized === "off" || normalized === "clear") {
|
|
91
|
+
budgets.setUsageTokenBudget(period, undefined);
|
|
92
|
+
ctx.ui.notify(`${label} token budget cleared.`, "info");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const tokens = Number(normalized.replaceAll(",", ""));
|
|
96
|
+
if (!Number.isFinite(tokens) || tokens <= 0) {
|
|
97
|
+
ctx.ui.notify("Enter a positive token count, or `off` to clear this threshold.", "warning");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
budgets.setUsageTokenBudget(period, tokens);
|
|
101
|
+
ctx.ui.notify(`${label} token budget set to ${tokens.toLocaleString()} tokens.`, "info");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function showSettingsPanel(
|
|
105
|
+
ctx: ExtensionCommandContext,
|
|
106
|
+
enforcement: EnforcementControl,
|
|
107
|
+
recovery: CodexRecoveryControl,
|
|
108
|
+
budgets: UsageBudgetControl,
|
|
109
|
+
effects: SettingsEffects = {
|
|
110
|
+
setEnforcement: (enabled) => enforcement.setEnabled(enabled),
|
|
111
|
+
setFooter: (enabled) => enforcement.setFooterEnabled(enabled),
|
|
112
|
+
setRecovery: (enabled) => recovery.setCodexRecoveryEnabled(enabled),
|
|
113
|
+
},
|
|
114
|
+
): Promise<void> {
|
|
115
|
+
if (ctx.mode !== "tui") {
|
|
116
|
+
ctx.ui.notify(renderSettingsView(settingsSnapshot(enforcement, recovery, budgets), -1, 100, plainTheme()).slice(0, -2).join("\n"), "info");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (;;) {
|
|
120
|
+
const snapshot = settingsSnapshot(enforcement, recovery, budgets);
|
|
121
|
+
const action = await ctx.ui.custom<SettingsAction>((tui, theme, _keybindings, done) => {
|
|
122
|
+
let selected = 0;
|
|
123
|
+
return {
|
|
124
|
+
invalidate() {},
|
|
125
|
+
render(width: number): string[] { return renderSettingsView(snapshot, selected, width, theme); },
|
|
126
|
+
handleInput(data: string): void {
|
|
127
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || data === "q") done({ kind: "close" });
|
|
128
|
+
else if (matchesKey(data, "up")) { selected = (selected - 1 + SETTINGS_KEYS.length) % SETTINGS_KEYS.length; tui.requestRender(); }
|
|
129
|
+
else if (matchesKey(data, "down")) { selected = (selected + 1) % SETTINGS_KEYS.length; tui.requestRender(); }
|
|
130
|
+
else if (matchesKey(data, "return") || matchesKey(data, "enter") || matchesKey(data, "space")) done({ kind: "activate", key: SETTINGS_KEYS[selected]! });
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
if (!action || action.kind === "close") return;
|
|
135
|
+
if (action.key === "enforcement") {
|
|
136
|
+
if (enforcement.isEnabled()) {
|
|
137
|
+
if (await ctx.ui.confirm("Disable routing enforcement?", "Jittor will remain monitor-only and will no longer block unsafe provider requests.")) await effects.setEnforcement(false);
|
|
138
|
+
} else await effects.setEnforcement(true);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (action.key === "footer") {
|
|
142
|
+
await effects.setFooter(!enforcement.isFooterEnabled());
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (action.key === "recovery") {
|
|
146
|
+
if (!recovery.isCodexRecoveryEnabled()) {
|
|
147
|
+
if (await ctx.ui.confirm("Enable Codex recovery?", "Jittor may start bounded hidden retries only after transient Codex failures fully settle.")) await effects.setRecovery(true);
|
|
148
|
+
} else await effects.setRecovery(false);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
await editBudget(ctx, budgets, action.key.slice("budget-".length) as UsagePeriod);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { JITTOR_EXTENSION_SETTINGS_FILENAME, JITTOR_STATE_DIRECTORY, USAGE_PERIODS, type UsagePeriod } from "@danypops/jittor";
|
|
4
|
+
|
|
5
|
+
export interface EnforcementControl {
|
|
6
|
+
isEnabled(): boolean;
|
|
7
|
+
setEnabled(enabled: boolean): void;
|
|
8
|
+
isFooterEnabled(): boolean;
|
|
9
|
+
setFooterEnabled(enabled: boolean): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CodexRecoveryControl {
|
|
13
|
+
isCodexRecoveryEnabled(): boolean;
|
|
14
|
+
setCodexRecoveryEnabled(enabled: boolean): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UsageBudgetControl {
|
|
18
|
+
getUsageTokenBudget(period: UsagePeriod): number | undefined;
|
|
19
|
+
setUsageTokenBudget(period: UsagePeriod, tokens: number | undefined): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PersistentExtensionControl extends EnforcementControl, CodexRecoveryControl, UsageBudgetControl {}
|
|
23
|
+
|
|
24
|
+
interface ExtensionSettings {
|
|
25
|
+
enforcementEnabled: boolean;
|
|
26
|
+
footerEnabled: boolean;
|
|
27
|
+
codexRecoveryEnabled: boolean;
|
|
28
|
+
usageTokenBudgets: Partial<Record<UsagePeriod, number>>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function defaultSettings(): ExtensionSettings {
|
|
32
|
+
return { enforcementEnabled: true, footerEnabled: true, codexRecoveryEnabled: false, usageTokenBudgets: {} };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseUsageTokenBudgets(value: unknown): Partial<Record<UsagePeriod, number>> {
|
|
36
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return {};
|
|
37
|
+
const record = value as Record<string, unknown>;
|
|
38
|
+
return Object.fromEntries(USAGE_PERIODS.flatMap(({ id }) => {
|
|
39
|
+
const tokens = record[id];
|
|
40
|
+
return typeof tokens === "number" && Number.isFinite(tokens) && tokens > 0 ? [[id, tokens]] : [];
|
|
41
|
+
})) as Partial<Record<UsagePeriod, number>>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function settingsPath(env: Record<string, string | undefined> = process.env): string {
|
|
45
|
+
const config = env["XDG_CONFIG_HOME"] ?? join(env["HOME"] ?? ".", ".config");
|
|
46
|
+
return join(config, JITTOR_STATE_DIRECTORY, JITTOR_EXTENSION_SETTINGS_FILENAME);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function loadSettings(path: string): ExtensionSettings {
|
|
50
|
+
try {
|
|
51
|
+
const value = JSON.parse(readFileSync(path, "utf8")) as unknown;
|
|
52
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return defaultSettings();
|
|
53
|
+
const record = value as Record<string, unknown>;
|
|
54
|
+
return {
|
|
55
|
+
enforcementEnabled: record["enforcementEnabled"] !== false,
|
|
56
|
+
footerEnabled: record["footerEnabled"] !== false,
|
|
57
|
+
codexRecoveryEnabled: record["codexRecoveryEnabled"] === true,
|
|
58
|
+
usageTokenBudgets: parseUsageTokenBudgets(record["usageTokenBudgets"]),
|
|
59
|
+
};
|
|
60
|
+
} catch {
|
|
61
|
+
return defaultSettings();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function persistSettings(path: string, settings: ExtensionSettings): void {
|
|
66
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
67
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
68
|
+
writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}\n`, { mode: 0o600 });
|
|
69
|
+
chmodSync(temporary, 0o600);
|
|
70
|
+
renameSync(temporary, path);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function persistentEnforcementControl(env: Record<string, string | undefined> = process.env): PersistentExtensionControl {
|
|
74
|
+
const path = settingsPath(env);
|
|
75
|
+
const settings = loadSettings(path);
|
|
76
|
+
return {
|
|
77
|
+
isEnabled: () => settings.enforcementEnabled,
|
|
78
|
+
setEnabled(value: boolean): void {
|
|
79
|
+
settings.enforcementEnabled = value;
|
|
80
|
+
persistSettings(path, settings);
|
|
81
|
+
},
|
|
82
|
+
isFooterEnabled: () => settings.footerEnabled,
|
|
83
|
+
setFooterEnabled(value: boolean): void {
|
|
84
|
+
settings.footerEnabled = value;
|
|
85
|
+
persistSettings(path, settings);
|
|
86
|
+
},
|
|
87
|
+
isCodexRecoveryEnabled: () => settings.codexRecoveryEnabled,
|
|
88
|
+
setCodexRecoveryEnabled(value: boolean): void {
|
|
89
|
+
settings.codexRecoveryEnabled = value;
|
|
90
|
+
persistSettings(path, settings);
|
|
91
|
+
},
|
|
92
|
+
getUsageTokenBudget(period): number | undefined {
|
|
93
|
+
return settings.usageTokenBudgets[period];
|
|
94
|
+
},
|
|
95
|
+
setUsageTokenBudget(period, tokens): void {
|
|
96
|
+
if (tokens !== undefined && (!Number.isFinite(tokens) || tokens <= 0)) throw new Error("usage token budget must be a positive finite number");
|
|
97
|
+
if (tokens === undefined) delete settings.usageTokenBudgets[period];
|
|
98
|
+
else settings.usageTokenBudgets[period] = tokens;
|
|
99
|
+
persistSettings(path, settings);
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import {
|
|
4
|
+
HUMAN_STATUS_MAX_SOURCES,
|
|
5
|
+
HUMAN_TEXT_FIELD_MAX_CHARACTERS,
|
|
6
|
+
type MetricQuery,
|
|
7
|
+
type PolicyAction,
|
|
8
|
+
type Route,
|
|
9
|
+
type RouterStatus,
|
|
10
|
+
type StoredMetricObservation,
|
|
11
|
+
} from "@danypops/jittor";
|
|
12
|
+
import type { ProviderBudget } from "./footer.ts";
|
|
13
|
+
import { sessionSecretField } from "./session-identity.ts";
|
|
14
|
+
|
|
15
|
+
export interface JittorPanelClient {
|
|
16
|
+
call(operation: string, input: unknown): Promise<any>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function providerBudgetMetricQuery(status: RouterStatus): MetricQuery | null {
|
|
20
|
+
switch (status.currentRoute?.provider) {
|
|
21
|
+
case "openai-codex": return { source: "codex-subscription", metric: "used-fraction", order: "desc", limit: 100 };
|
|
22
|
+
case "openrouter": return { source: "openrouter", order: "desc", limit: 20 };
|
|
23
|
+
case "anthropic": return { source: "anthropic", metric: "used-fraction", order: "desc", limit: 20 };
|
|
24
|
+
case "anthropic-vertex": return { source: "anthropic-vertex", metric: "used-fraction", order: "desc", limit: 20 };
|
|
25
|
+
default: return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type PanelAction = "pause" | "resume" | "refresh" | "override" | "clear-override" | "close";
|
|
30
|
+
|
|
31
|
+
function latest(rows: StoredMetricObservation[], predicate: (row: StoredMetricObservation) => boolean): StoredMetricObservation | undefined {
|
|
32
|
+
return rows.filter(predicate).sort((left, right) => right.observedAt - left.observedAt || right.id - left.id)[0];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function sanitizedText(value: string): string {
|
|
36
|
+
return value.replace(/[\r\n\t]/g, " ").replace(/ +/g, " ").trim().slice(0, HUMAN_TEXT_FIELD_MAX_CHARACTERS);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function routeText(route: Route): string {
|
|
40
|
+
return `${sanitizedText(route.provider)}/${sanitizedText(route.model)} · ${sanitizedText(route.thinking)}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizedIdentity(value: unknown): string {
|
|
44
|
+
return typeof value === "string" ? value.toLowerCase().replace(/[^a-z0-9]+/g, "") : "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function longestWindow(rows: StoredMetricObservation[]): StoredMetricObservation | undefined {
|
|
48
|
+
return [...rows].sort((left, right) =>
|
|
49
|
+
Number(right.attributes["windowSeconds"] ?? 0) - Number(left.attributes["windowSeconds"] ?? 0)
|
|
50
|
+
|| right.observedAt - left.observedAt
|
|
51
|
+
|| right.id - left.id,
|
|
52
|
+
)[0];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function codexWindowForModel(rows: StoredMetricObservation[], model: string): StoredMetricObservation | undefined {
|
|
56
|
+
const codexRows = rows.filter((row) => row.source === "codex-subscription" && row.metric === "used-fraction" && typeof row.value === "number");
|
|
57
|
+
const modelIdentity = normalizedIdentity(model);
|
|
58
|
+
const matchingAdditional = codexRows.filter((row) => {
|
|
59
|
+
const limitId = normalizedIdentity(row.attributes["limitId"]);
|
|
60
|
+
const limitName = normalizedIdentity(row.attributes["limitName"]);
|
|
61
|
+
return limitId !== "codex" && limitName.length > 0 && limitName === modelIdentity;
|
|
62
|
+
});
|
|
63
|
+
if (matchingAdditional.length > 0) return longestWindow(matchingAdditional);
|
|
64
|
+
return longestWindow(codexRows.filter((row) => {
|
|
65
|
+
const limitId = normalizedIdentity(row.attributes["limitId"]);
|
|
66
|
+
return limitId === "codex" || (limitId.length === 0 && row.scope.startsWith("codex:"));
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function compactWindowName(seconds: number): string {
|
|
71
|
+
if (seconds >= 6 * 24 * 60 * 60) return "W";
|
|
72
|
+
if (seconds >= 60 * 60) return `${Math.round(seconds / 3_600)}h`;
|
|
73
|
+
return `${Math.round(seconds / 60)}m`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function windowName(seconds: number): string {
|
|
77
|
+
if (seconds >= 6 * 24 * 60 * 60) return "weekly";
|
|
78
|
+
if (seconds >= 60 * 60) return `${Math.round(seconds / 3_600)}h`;
|
|
79
|
+
return `${Math.round(seconds / 60)}m`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* `null` means "not known yet, but this provider can report a budget once data arrives" -- router
|
|
84
|
+
* not ready, or a supported provider whose telemetry hasn't been observed yet; the footer shows a
|
|
85
|
+
* placeholder that may resolve. `undefined` means "no budget signal is possible for this provider
|
|
86
|
+
* at all" (e.g. google-vertex, which has no documented rate-limit or quota header/endpoint Jittor
|
|
87
|
+
* could ever read -- see google-vertex-contracts.ts); the footer omits the segment entirely rather
|
|
88
|
+
* than showing a `?` that can never resolve.
|
|
89
|
+
*/
|
|
90
|
+
export function buildFooterBudget(status: RouterStatus, metrics: StoredMetricObservation[]): ProviderBudget | null | undefined {
|
|
91
|
+
if (!status.ready || !status.currentRoute) return null;
|
|
92
|
+
if (status.currentRoute.provider === "openai-codex") {
|
|
93
|
+
const codex = codexWindowForModel(metrics, status.currentRoute.model);
|
|
94
|
+
if (!codex || typeof codex.value !== "number") return null;
|
|
95
|
+
const resetsAtSeconds = Number(codex.attributes["resetsAt"]);
|
|
96
|
+
return {
|
|
97
|
+
kind: "bounded",
|
|
98
|
+
label: compactWindowName(Number(codex.attributes["windowSeconds"] ?? 0)),
|
|
99
|
+
remainingFraction: 1 - codex.value,
|
|
100
|
+
observedAt: codex.observedAt,
|
|
101
|
+
...(Number.isFinite(resetsAtSeconds) && resetsAtSeconds > 0 ? { resetsAt: resetsAtSeconds * 1_000 } : {}),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (status.currentRoute.provider === "anthropic") {
|
|
105
|
+
const anthropic = latest(metrics, (row) => row.source === "anthropic" && row.metric === "used-fraction" && row.scope === "tokens" && typeof row.value === "number")
|
|
106
|
+
?? latest(metrics, (row) => row.source === "anthropic" && row.metric === "used-fraction" && row.scope === "requests" && typeof row.value === "number");
|
|
107
|
+
if (!anthropic || typeof anthropic.value !== "number") return null;
|
|
108
|
+
const resetsAt = Number(anthropic.attributes["resetsAt"]);
|
|
109
|
+
return {
|
|
110
|
+
kind: "bounded",
|
|
111
|
+
label: anthropic.scope === "tokens" ? "tok" : "req",
|
|
112
|
+
remainingFraction: 1 - anthropic.value,
|
|
113
|
+
observedAt: anthropic.observedAt,
|
|
114
|
+
...(Number.isFinite(resetsAt) && resetsAt > 0 ? { resetsAt } : {}),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (status.currentRoute.provider === "anthropic-vertex") {
|
|
118
|
+
// Best-effort only (see index.ts): these metrics only exist if Anthropic-style rate-limit
|
|
119
|
+
// headers were actually observed on this passthrough, which is unverified. Labeled distinctly
|
|
120
|
+
// ("vtok"/"vreq") from direct Anthropic's "tok"/"req" since they are a different account/quota
|
|
121
|
+
// pool even if the header shape is identical. If nothing was ever observed, this stays null
|
|
122
|
+
// (may still resolve later), not undefined (never possible) -- unlike google-vertex, this
|
|
123
|
+
// provider's transport has not been shown to structurally lack the signal.
|
|
124
|
+
const anthropicVertex = latest(metrics, (row) => row.source === "anthropic-vertex" && row.metric === "used-fraction" && row.scope === "tokens" && typeof row.value === "number")
|
|
125
|
+
?? latest(metrics, (row) => row.source === "anthropic-vertex" && row.metric === "used-fraction" && row.scope === "requests" && typeof row.value === "number");
|
|
126
|
+
if (!anthropicVertex || typeof anthropicVertex.value !== "number") return null;
|
|
127
|
+
const resetsAt = Number(anthropicVertex.attributes["resetsAt"]);
|
|
128
|
+
return {
|
|
129
|
+
kind: "bounded",
|
|
130
|
+
label: anthropicVertex.scope === "tokens" ? "vtok" : "vreq",
|
|
131
|
+
remainingFraction: 1 - anthropicVertex.value,
|
|
132
|
+
observedAt: anthropicVertex.observedAt,
|
|
133
|
+
...(Number.isFinite(resetsAt) && resetsAt > 0 ? { resetsAt } : {}),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (status.currentRoute.provider === "openrouter") {
|
|
137
|
+
const openRouter = latest(metrics, (row) => row.source === "openrouter" && row.metric === "usage" && typeof row.value === "number");
|
|
138
|
+
const remaining = latest(metrics, (row) => row.source === "openrouter" && row.metric === "remaining-fraction" && typeof row.value === "number");
|
|
139
|
+
if (remaining && typeof remaining.value === "number" && (!openRouter || remaining.observedAt >= openRouter.observedAt)) {
|
|
140
|
+
const reset = typeof remaining.attributes["reset"] === "string" ? sanitizedText(remaining.attributes["reset"]) : undefined;
|
|
141
|
+
return {
|
|
142
|
+
kind: "bounded",
|
|
143
|
+
label: "OR",
|
|
144
|
+
remainingFraction: remaining.value,
|
|
145
|
+
observedAt: remaining.observedAt,
|
|
146
|
+
...(reset ? { resetText: `${reset} reset` } : {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (!openRouter || typeof openRouter.value !== "number") return null;
|
|
150
|
+
return { kind: "unbounded", label: "spend", valueText: `$${openRouter.value.toFixed(3)}`, observedAt: openRouter.observedAt };
|
|
151
|
+
}
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function formatFooterStatus(status: RouterStatus, metrics: StoredMetricObservation[]): string {
|
|
156
|
+
const budget = buildFooterBudget(status, metrics);
|
|
157
|
+
if (!budget) return "";
|
|
158
|
+
return budget.kind === "unbounded" ? budget.valueText : `${budget.label} ${(budget.remainingFraction * 100).toFixed(1)}% left`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function nextAction(action: PolicyAction | undefined): string {
|
|
162
|
+
switch (action) {
|
|
163
|
+
case "continue": return "throttle";
|
|
164
|
+
case "throttle": return "lower thinking";
|
|
165
|
+
case "lower-thinking": return "switch model";
|
|
166
|
+
case "switch-model": return "switch provider";
|
|
167
|
+
case "switch-provider": return "halt";
|
|
168
|
+
case "halt": return "halted";
|
|
169
|
+
default: return "waiting for decision";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function burnLine(rows: StoredMetricObservation[], current: StoredMetricObservation, now: number): string {
|
|
174
|
+
const previous = rows
|
|
175
|
+
.filter((row) => row.source === current.source && row.scope === current.scope && row.metric === current.metric && row.id !== current.id && row.observedAt < current.observedAt)
|
|
176
|
+
.sort((left, right) => right.observedAt - left.observedAt)[0];
|
|
177
|
+
const resetsAt = Number(current.attributes["resetsAt"] ?? 0) * 1_000;
|
|
178
|
+
const remainingSeconds = (resetsAt - now) / 1_000;
|
|
179
|
+
const sustainable = typeof current.value === "number" && remainingSeconds > 0 ? (1 - current.value) / remainingSeconds : null;
|
|
180
|
+
const observed = previous && typeof previous.value === "number" && typeof current.value === "number" && current.observedAt > previous.observedAt
|
|
181
|
+
? (current.value - previous.value) / ((current.observedAt - previous.observedAt) / 1_000)
|
|
182
|
+
: null;
|
|
183
|
+
const perHour = (rate: number | null) => rate === null ? "n/a" : `${(rate * 3_600 * 100).toFixed(2)}%/h`;
|
|
184
|
+
return `Burn: observed ${perHour(observed)} · sustainable ${perHour(sustainable)}`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function buildStatusView(status: RouterStatus, metrics: StoredMetricObservation[], now = Date.now()): string[] {
|
|
188
|
+
const lines = [status.ready ? "Ready" : "Not ready"];
|
|
189
|
+
const codex = status.currentRoute?.provider === "openai-codex" ? codexWindowForModel(metrics, status.currentRoute.model) : undefined;
|
|
190
|
+
if (codex && typeof codex.value === "number") {
|
|
191
|
+
const seconds = Number(codex.attributes["windowSeconds"] ?? 0);
|
|
192
|
+
lines.push(`Codex ${windowName(seconds)}: ${((1 - codex.value) * 100).toFixed(1)}% left`);
|
|
193
|
+
lines.push(burnLine(metrics, codex, now));
|
|
194
|
+
}
|
|
195
|
+
const openRouter = status.currentRoute?.provider === "openrouter"
|
|
196
|
+
? latest(metrics, (row) => row.source === "openrouter" && row.metric === "usage" && typeof row.value === "number")
|
|
197
|
+
: undefined;
|
|
198
|
+
if (openRouter && typeof openRouter.value === "number") lines.push(`OpenRouter spend: $${openRouter.value.toFixed(3)}`);
|
|
199
|
+
const anthropic = status.currentRoute?.provider === "anthropic"
|
|
200
|
+
? latest(metrics, (row) => row.source === "anthropic" && row.metric === "used-fraction" && row.scope === "tokens" && typeof row.value === "number")
|
|
201
|
+
?? latest(metrics, (row) => row.source === "anthropic" && row.metric === "used-fraction" && row.scope === "requests" && typeof row.value === "number")
|
|
202
|
+
: undefined;
|
|
203
|
+
if (anthropic && typeof anthropic.value === "number") lines.push(`Anthropic ${anthropic.scope}: ${((1 - anthropic.value) * 100).toFixed(1)}% left`);
|
|
204
|
+
if (status.currentRoute) lines.push(`Route: ${routeText(status.currentRoute)}`);
|
|
205
|
+
if (status.lastDecision) lines.push(`Pressure: ${Number.isFinite(status.lastDecision.pressure) ? status.lastDecision.pressure.toFixed(3) : "∞"} · ${status.lastDecision.action}`);
|
|
206
|
+
lines.push(`Next: ${nextAction(status.lastDecision?.action)}`);
|
|
207
|
+
lines.push("Telemetry:");
|
|
208
|
+
const providerSources = status.sources.filter((source) => source.provider === status.currentRoute?.provider);
|
|
209
|
+
for (const source of providerSources.slice(0, HUMAN_STATUS_MAX_SOURCES)) {
|
|
210
|
+
const freshness = !source.ok ? "failed" : source.observedAt !== undefined && now - source.observedAt > 120_000 ? "stale" : "fresh";
|
|
211
|
+
lines.push(` ${sanitizedText(source.id)}: ${freshness} · ${source.metrics} metrics`);
|
|
212
|
+
}
|
|
213
|
+
if (providerSources.length > HUMAN_STATUS_MAX_SOURCES) lines.push(` … ${providerSources.length - HUMAN_STATUS_MAX_SOURCES} more telemetry sources omitted`);
|
|
214
|
+
if (status.override) lines.push(`Override: ${routeText(status.override.route)}`);
|
|
215
|
+
if (status.paused) lines.push("Emergency halt is active");
|
|
216
|
+
return lines;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function snapshot(client: JittorPanelClient, sessionId: string): Promise<{ status: RouterStatus; metrics: StoredMetricObservation[] }> {
|
|
220
|
+
const status = await client.call("router.status", { session_id: sessionId }) as RouterStatus;
|
|
221
|
+
const query = providerBudgetMetricQuery(status);
|
|
222
|
+
const metrics = query ? await client.call("metrics.query", query) as StoredMetricObservation[] : [];
|
|
223
|
+
return { status, metrics };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function chooseOverride(ctx: ExtensionCommandContext, routes: Route[]): Promise<Route | undefined> {
|
|
227
|
+
if (routes.length === 0) { ctx.ui.notify("Pi reports no authenticated routes for the current provider.", "warning"); return undefined; }
|
|
228
|
+
const labels = routes.map(routeText);
|
|
229
|
+
const selected = await ctx.ui.select("Override route", labels);
|
|
230
|
+
const index = selected ? labels.indexOf(selected) : -1;
|
|
231
|
+
return index >= 0 ? routes[index] : undefined;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export async function showJittorPanel(ctx: ExtensionCommandContext, client: JittorPanelClient): Promise<void> {
|
|
235
|
+
const session_id = ctx.sessionManager.getSessionId();
|
|
236
|
+
for (;;) {
|
|
237
|
+
const current = await snapshot(client, session_id);
|
|
238
|
+
if (ctx.mode !== "tui") {
|
|
239
|
+
ctx.ui.notify(buildStatusView(current.status, current.metrics).join("\n"), "info");
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const action = await ctx.ui.custom<PanelAction>((_tui, theme, _keybindings, done) => ({
|
|
243
|
+
invalidate() {},
|
|
244
|
+
render(width: number): string[] {
|
|
245
|
+
const border = theme.fg("borderMuted", "─".repeat(Math.max(1, width)));
|
|
246
|
+
const controls = current.status.paused
|
|
247
|
+
? "r refresh · p release emergency halt · o override · c clear override · Esc close"
|
|
248
|
+
: "r refresh · p emergency halt · o override · c clear override · Esc close";
|
|
249
|
+
return [
|
|
250
|
+
border,
|
|
251
|
+
truncateToWidth(theme.bold("Jittor"), width, ""),
|
|
252
|
+
border,
|
|
253
|
+
...buildStatusView(current.status, current.metrics).map((line) => truncateToWidth(` ${line}`, width, "…")),
|
|
254
|
+
border,
|
|
255
|
+
truncateToWidth(theme.fg("dim", controls), width, "…"),
|
|
256
|
+
border,
|
|
257
|
+
];
|
|
258
|
+
},
|
|
259
|
+
handleInput(data: string): void {
|
|
260
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) done("close");
|
|
261
|
+
else if (data === "r") done("refresh");
|
|
262
|
+
else if (data === "p") done(current.status.paused ? "resume" : "pause");
|
|
263
|
+
else if (data === "o") done("override");
|
|
264
|
+
else if (data === "c") done("clear-override");
|
|
265
|
+
},
|
|
266
|
+
}));
|
|
267
|
+
if (!action || action === "close") return;
|
|
268
|
+
if (action === "refresh") { await client.call("telemetry.poll", {}); continue; }
|
|
269
|
+
if (action === "pause" || action === "resume") {
|
|
270
|
+
if (await ctx.ui.confirm(action === "pause" ? "Emergency-halt provider requests?" : "Release emergency halt?", "This changes provider-request enforcement. Use /jittor off to disable blocking entirely.")) {
|
|
271
|
+
await client.call(action === "pause" ? "router.pause" : "router.resume", { session_id, ...sessionSecretField(session_id) });
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (action === "clear-override") {
|
|
276
|
+
if (await ctx.ui.confirm("Clear route override?", "Policy-controlled routing will resume.")) await client.call("router.clear_override", { session_id, ...sessionSecretField(session_id) });
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const route = await chooseOverride(ctx, current.status.availableRoutes);
|
|
280
|
+
if (route && await ctx.ui.confirm("Apply route override?", `${routeText(route)} for one hour`)) {
|
|
281
|
+
await client.call("router.override", { route, expiresAt: Date.now() + 60 * 60 * 1_000, session_id, ...sessionSecretField(session_id) });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|