@mrciphersmith/keryx 0.2.61 → 0.2.63
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/cli.js +716 -262
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10173,7 +10173,90 @@ async function resolveModelsForPicker(fetchFn, provider, env = process.env, opts
|
|
|
10173
10173
|
const apiKey = typeof raw === "string" && raw.length > 0 ? raw : undefined;
|
|
10174
10174
|
return fetchOpenAiCompatModelsDetailed(fetchFn, { ...compat, ...provider.baseUrl !== undefined ? { baseUrl: provider.baseUrl } : {} }, apiKey, opts);
|
|
10175
10175
|
}
|
|
10176
|
-
|
|
10176
|
+
function balanceCapableProvider(name) {
|
|
10177
|
+
const provider = providerByName(name);
|
|
10178
|
+
if (provider === undefined || provider.balancePath === undefined || provider.balanceKind === undefined) {
|
|
10179
|
+
return;
|
|
10180
|
+
}
|
|
10181
|
+
return provider;
|
|
10182
|
+
}
|
|
10183
|
+
function parseDeepSeekBalance(body) {
|
|
10184
|
+
if (typeof body !== "object" || body === null) {
|
|
10185
|
+
return;
|
|
10186
|
+
}
|
|
10187
|
+
const infos = body.balance_infos;
|
|
10188
|
+
if (!Array.isArray(infos)) {
|
|
10189
|
+
return;
|
|
10190
|
+
}
|
|
10191
|
+
for (const info of infos) {
|
|
10192
|
+
if (typeof info !== "object" || info === null) {
|
|
10193
|
+
continue;
|
|
10194
|
+
}
|
|
10195
|
+
const total = Number(info.total_balance);
|
|
10196
|
+
if (Number.isFinite(total)) {
|
|
10197
|
+
const currency = String(info.currency ?? "USD");
|
|
10198
|
+
return { currency, total, exact: true };
|
|
10199
|
+
}
|
|
10200
|
+
}
|
|
10201
|
+
return;
|
|
10202
|
+
}
|
|
10203
|
+
function parseOpenRouterBalance(body) {
|
|
10204
|
+
if (typeof body !== "object" || body === null) {
|
|
10205
|
+
return;
|
|
10206
|
+
}
|
|
10207
|
+
const credits = body.credits;
|
|
10208
|
+
if (typeof credits !== "object" || credits === null) {
|
|
10209
|
+
return;
|
|
10210
|
+
}
|
|
10211
|
+
const total = Number(credits.total);
|
|
10212
|
+
const used = Number(credits.used);
|
|
10213
|
+
if (!Number.isFinite(total)) {
|
|
10214
|
+
return;
|
|
10215
|
+
}
|
|
10216
|
+
const usedField = Number.isFinite(used) ? { used } : {};
|
|
10217
|
+
const remaining = Number.isFinite(used) ? total - used : undefined;
|
|
10218
|
+
return {
|
|
10219
|
+
currency: String(credits.currency ?? "USD"),
|
|
10220
|
+
total,
|
|
10221
|
+
...usedField,
|
|
10222
|
+
...remaining !== undefined ? { remaining } : {},
|
|
10223
|
+
exact: true
|
|
10224
|
+
};
|
|
10225
|
+
}
|
|
10226
|
+
async function fetchProviderBalance(fetchFn, provider, apiKey, opts) {
|
|
10227
|
+
if (provider.balancePath === undefined || provider.balanceKind === undefined) {
|
|
10228
|
+
return;
|
|
10229
|
+
}
|
|
10230
|
+
const url = `${provider.baseUrl.replace(/\/+$/, "")}${provider.balancePath}`;
|
|
10231
|
+
const timeoutMs = opts?.timeoutMs ?? BALANCE_FETCH_TIMEOUT_MS;
|
|
10232
|
+
const controller = new AbortController;
|
|
10233
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
10234
|
+
try {
|
|
10235
|
+
const init = { signal: controller.signal };
|
|
10236
|
+
if (apiKey !== undefined && apiKey.length > 0) {
|
|
10237
|
+
init.headers = { authorization: `Bearer ${apiKey}` };
|
|
10238
|
+
}
|
|
10239
|
+
const res = await fetchFn(url, init);
|
|
10240
|
+
if (!res.ok) {
|
|
10241
|
+
return;
|
|
10242
|
+
}
|
|
10243
|
+
const body = await res.json();
|
|
10244
|
+
return provider.balanceKind === "deepseek" ? parseDeepSeekBalance(body) : parseOpenRouterBalance(body);
|
|
10245
|
+
} catch {
|
|
10246
|
+
return;
|
|
10247
|
+
} finally {
|
|
10248
|
+
clearTimeout(timer);
|
|
10249
|
+
}
|
|
10250
|
+
}
|
|
10251
|
+
function providerApiKey(provider, env = process.env) {
|
|
10252
|
+
const envKey = provider.envKey;
|
|
10253
|
+
if (envKey === undefined) {
|
|
10254
|
+
return;
|
|
10255
|
+
}
|
|
10256
|
+
const raw = env[envKey];
|
|
10257
|
+
return typeof raw === "string" && raw.length > 0 ? raw : undefined;
|
|
10258
|
+
}
|
|
10259
|
+
var DEFAULT_MODELS_PATH = "/v1/models", OPENAI_COMPAT_PROVIDERS, MODELS_FETCH_TIMEOUT_MS = 1e4, BALANCE_FETCH_TIMEOUT_MS = 8000;
|
|
10177
10260
|
var init_providers = __esm(() => {
|
|
10178
10261
|
OPENAI_COMPAT_PROVIDERS = [
|
|
10179
10262
|
{
|
|
@@ -10182,7 +10265,9 @@ var init_providers = __esm(() => {
|
|
|
10182
10265
|
baseUrl: "https://openrouter.ai/api",
|
|
10183
10266
|
envKey: "OPENROUTER_API_KEY",
|
|
10184
10267
|
models: ["openai/gpt-4o-mini", "google/gemini-2.0-flash-001", "qwen/qwen-2.5-7b-instruct", "meta-llama/llama-3.1-8b-instruct"],
|
|
10185
|
-
note: "hosted \xB7 400+ models"
|
|
10268
|
+
note: "hosted \xB7 400+ models",
|
|
10269
|
+
balancePath: "/api/v1/credits",
|
|
10270
|
+
balanceKind: "openrouter"
|
|
10186
10271
|
},
|
|
10187
10272
|
{
|
|
10188
10273
|
name: "deepseek",
|
|
@@ -10190,7 +10275,9 @@ var init_providers = __esm(() => {
|
|
|
10190
10275
|
baseUrl: "https://api.deepseek.com",
|
|
10191
10276
|
envKey: "DEEPSEEK_API_KEY",
|
|
10192
10277
|
models: ["deepseek-chat", "deepseek-reasoner"],
|
|
10193
|
-
note: "cheap per-token"
|
|
10278
|
+
note: "cheap per-token",
|
|
10279
|
+
balancePath: "/user/balance",
|
|
10280
|
+
balanceKind: "deepseek"
|
|
10194
10281
|
},
|
|
10195
10282
|
{
|
|
10196
10283
|
name: "zai",
|
|
@@ -13121,14 +13208,29 @@ async function runModelTurn(input2) {
|
|
|
13121
13208
|
};
|
|
13122
13209
|
let text = "";
|
|
13123
13210
|
let error;
|
|
13211
|
+
let usage;
|
|
13212
|
+
let reasoning = false;
|
|
13213
|
+
const startedAt = performance.now();
|
|
13214
|
+
let firstByteAt;
|
|
13124
13215
|
for await (const event of port.stream(request, { attemptId: request.requestId })) {
|
|
13216
|
+
if (firstByteAt === undefined) {
|
|
13217
|
+
firstByteAt = performance.now();
|
|
13218
|
+
}
|
|
13125
13219
|
if (event.kind === "text_delta" && event.text) {
|
|
13126
13220
|
text += event.text;
|
|
13127
13221
|
} else if (event.kind === "provider_error" && event.error) {
|
|
13128
13222
|
error = event.error;
|
|
13223
|
+
} else if (event.kind === "reasoning_delta") {
|
|
13224
|
+
reasoning = true;
|
|
13225
|
+
} else if (event.kind === "usage_update" && event.usage) {
|
|
13226
|
+
usage = event.usage;
|
|
13129
13227
|
}
|
|
13130
13228
|
}
|
|
13131
|
-
|
|
13229
|
+
const latencyMs = firstByteAt !== undefined ? Math.max(0, Math.round(firstByteAt - startedAt)) : undefined;
|
|
13230
|
+
const usageField = usage !== undefined ? { usage } : {};
|
|
13231
|
+
const latencyField = latencyMs !== undefined ? { latencyMs } : {};
|
|
13232
|
+
const reasoningField = reasoning ? { reasoning } : {};
|
|
13233
|
+
return error ? { provider, model, credentialAvailable, text, error, ...usageField, ...latencyField, ...reasoningField } : { provider, model, credentialAvailable, text, ...usageField, ...latencyField, ...reasoningField };
|
|
13132
13234
|
}
|
|
13133
13235
|
var DEFAULT_PROVIDER = "anthropic", DEFAULT_MODELS;
|
|
13134
13236
|
var init_single_turn = __esm(() => {
|
|
@@ -53454,7 +53556,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
53454
53556
|
// package.json
|
|
53455
53557
|
var package_default = {
|
|
53456
53558
|
name: "@mrciphersmith/keryx",
|
|
53457
|
-
version: "0.2.
|
|
53559
|
+
version: "0.2.63",
|
|
53458
53560
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
53459
53561
|
private: false,
|
|
53460
53562
|
publishConfig: {
|
|
@@ -57032,7 +57134,7 @@ var AGENT_SLASH_COMMANDS = [
|
|
|
57032
57134
|
{ name: "/theme", description: "Open the theme picker \u2014 /theme [name] applies immediately", modes: BOTH },
|
|
57033
57135
|
{
|
|
57034
57136
|
name: "/game",
|
|
57035
|
-
description: "
|
|
57137
|
+
description: "Games against the model \u2014 /game [seconds] raises the model-turn deadline; add games via src/tui/games/",
|
|
57036
57138
|
modes: AGENT_ONLY
|
|
57037
57139
|
},
|
|
57038
57140
|
{
|
|
@@ -57413,8 +57515,75 @@ function openThemePicker(otui, chrome, options) {
|
|
|
57413
57515
|
return presentThemePicker((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
57414
57516
|
}
|
|
57415
57517
|
|
|
57416
|
-
// src/tui/
|
|
57417
|
-
|
|
57518
|
+
// src/tui/games/registry.ts
|
|
57519
|
+
function createRegistry(games) {
|
|
57520
|
+
return {
|
|
57521
|
+
games,
|
|
57522
|
+
get(id) {
|
|
57523
|
+
return games.find((game) => game.id === id);
|
|
57524
|
+
}
|
|
57525
|
+
};
|
|
57526
|
+
}
|
|
57527
|
+
|
|
57528
|
+
// src/tui/games/stats.ts
|
|
57529
|
+
function emptyTurnTotals() {
|
|
57530
|
+
return {
|
|
57531
|
+
turns: 0,
|
|
57532
|
+
latencyMs: undefined,
|
|
57533
|
+
totalMs: undefined,
|
|
57534
|
+
inputTokens: undefined,
|
|
57535
|
+
outputTokens: undefined,
|
|
57536
|
+
localFallbacks: 0,
|
|
57537
|
+
errors: 0
|
|
57538
|
+
};
|
|
57539
|
+
}
|
|
57540
|
+
function addMaybe(a, b) {
|
|
57541
|
+
if (a === undefined && b === undefined) {
|
|
57542
|
+
return;
|
|
57543
|
+
}
|
|
57544
|
+
return (a ?? 0) + (b ?? 0);
|
|
57545
|
+
}
|
|
57546
|
+
function addTurn(totals, turn) {
|
|
57547
|
+
return {
|
|
57548
|
+
turns: totals.turns + 1,
|
|
57549
|
+
latencyMs: addMaybe(totals.latencyMs, turn.latencyMs),
|
|
57550
|
+
totalMs: addMaybe(totals.totalMs, turn.totalMs),
|
|
57551
|
+
inputTokens: addMaybe(totals.inputTokens, turn.inputTokens),
|
|
57552
|
+
outputTokens: addMaybe(totals.outputTokens, turn.outputTokens),
|
|
57553
|
+
localFallbacks: totals.localFallbacks + (turn.localFallback ? 1 : 0),
|
|
57554
|
+
errors: totals.errors + (turn.error ? 1 : 0)
|
|
57555
|
+
};
|
|
57556
|
+
}
|
|
57557
|
+
function formatMs(ms) {
|
|
57558
|
+
if (ms === undefined) {
|
|
57559
|
+
return "\u2013";
|
|
57560
|
+
}
|
|
57561
|
+
if (ms < 1000) {
|
|
57562
|
+
return `${ms}ms`;
|
|
57563
|
+
}
|
|
57564
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
57565
|
+
}
|
|
57566
|
+
function formatTokens(n) {
|
|
57567
|
+
if (n === undefined) {
|
|
57568
|
+
return "\u2013";
|
|
57569
|
+
}
|
|
57570
|
+
if (n < 1000) {
|
|
57571
|
+
return `${n}`;
|
|
57572
|
+
}
|
|
57573
|
+
return `${(n / 1000).toFixed(1)}k`;
|
|
57574
|
+
}
|
|
57575
|
+
|
|
57576
|
+
// src/tui/games/constants.ts
|
|
57577
|
+
var GAME_MODEL_TIMEOUT_MS = 60000;
|
|
57578
|
+
var GAMES_FOOTER = [
|
|
57579
|
+
{ key: "arrows", label: "move" },
|
|
57580
|
+
{ key: "enter", label: "place" },
|
|
57581
|
+
{ key: "r", label: "new game" },
|
|
57582
|
+
{ key: "tab", label: "games" },
|
|
57583
|
+
{ key: "esc", label: "minimize" }
|
|
57584
|
+
];
|
|
57585
|
+
|
|
57586
|
+
// src/tui/games/tic-tac-toe/core.ts
|
|
57418
57587
|
var WIN_LINES = [
|
|
57419
57588
|
[0, 1, 2],
|
|
57420
57589
|
[3, 4, 5],
|
|
@@ -57444,6 +57613,29 @@ function checkWinner(board) {
|
|
|
57444
57613
|
function freeCells(board) {
|
|
57445
57614
|
return board.flatMap((cell, index) => cell === null ? [index] : []);
|
|
57446
57615
|
}
|
|
57616
|
+
function placeMark(board, index, mark) {
|
|
57617
|
+
if (index < 0 || index > 8 || board[index] !== null) {
|
|
57618
|
+
return;
|
|
57619
|
+
}
|
|
57620
|
+
const next = board.slice();
|
|
57621
|
+
next[index] = mark;
|
|
57622
|
+
const win = checkWinner(next);
|
|
57623
|
+
const draw = win === null && next.every((cell) => cell !== null);
|
|
57624
|
+
return {
|
|
57625
|
+
board: next,
|
|
57626
|
+
turn: win === null && !draw ? mark === "X" ? "O" : "X" : mark,
|
|
57627
|
+
winner: win?.winner ?? null,
|
|
57628
|
+
winLine: win?.line ?? null,
|
|
57629
|
+
draw,
|
|
57630
|
+
cursor: index
|
|
57631
|
+
};
|
|
57632
|
+
}
|
|
57633
|
+
function freshGame() {
|
|
57634
|
+
return { board: emptyBoard(), turn: "X", winner: null, winLine: null, draw: false, cursor: 4 };
|
|
57635
|
+
}
|
|
57636
|
+
function isGameOver(state) {
|
|
57637
|
+
return state.winner !== null || state.draw;
|
|
57638
|
+
}
|
|
57447
57639
|
function bestLocalMove(board, mark) {
|
|
57448
57640
|
const free = freeCells(board);
|
|
57449
57641
|
if (free.length === 0) {
|
|
@@ -57466,28 +57658,6 @@ function bestLocalMove(board, mark) {
|
|
|
57466
57658
|
}
|
|
57467
57659
|
return free[0];
|
|
57468
57660
|
}
|
|
57469
|
-
function placeMark(board, index, mark) {
|
|
57470
|
-
if (index < 0 || index > 8 || board[index] !== null) {
|
|
57471
|
-
return;
|
|
57472
|
-
}
|
|
57473
|
-
const next = board.slice();
|
|
57474
|
-
next[index] = mark;
|
|
57475
|
-
const win = checkWinner(next);
|
|
57476
|
-
const draw = win === null && next.every((cell) => cell !== null);
|
|
57477
|
-
return {
|
|
57478
|
-
board: next,
|
|
57479
|
-
turn: win === null && !draw ? mark === "X" ? "O" : "X" : mark,
|
|
57480
|
-
winner: win?.winner ?? null,
|
|
57481
|
-
winLine: win?.line ?? null,
|
|
57482
|
-
draw
|
|
57483
|
-
};
|
|
57484
|
-
}
|
|
57485
|
-
function freshGame() {
|
|
57486
|
-
return { board: emptyBoard(), turn: "X", winner: null, winLine: null, draw: false };
|
|
57487
|
-
}
|
|
57488
|
-
function isGameOver(state) {
|
|
57489
|
-
return state.winner !== null || state.draw;
|
|
57490
|
-
}
|
|
57491
57661
|
function parseModelMove(reply, board) {
|
|
57492
57662
|
if (reply.length === 0) {
|
|
57493
57663
|
return;
|
|
@@ -57499,6 +57669,11 @@ function parseModelMove(reply, board) {
|
|
|
57499
57669
|
const index = Number(match[1]);
|
|
57500
57670
|
return board[index] === null ? index : undefined;
|
|
57501
57671
|
}
|
|
57672
|
+
function asTtt(state) {
|
|
57673
|
+
return state;
|
|
57674
|
+
}
|
|
57675
|
+
|
|
57676
|
+
// src/tui/games/tic-tac-toe/prompts.ts
|
|
57502
57677
|
function gameSystemPrompt() {
|
|
57503
57678
|
return [
|
|
57504
57679
|
"You are playing tic-tac-toe as O against a human playing X.",
|
|
@@ -57520,31 +57695,43 @@ ${rows.join(`
|
|
|
57520
57695
|
|
|
57521
57696
|
Make your move. Reply with one digit 0-8.`;
|
|
57522
57697
|
}
|
|
57523
|
-
|
|
57524
|
-
|
|
57525
|
-
|
|
57526
|
-
|
|
57527
|
-
|
|
57528
|
-
|
|
57529
|
-
|
|
57530
|
-
|
|
57531
|
-
|
|
57532
|
-
...opts.env !== undefined ? { env: opts.env } : {}
|
|
57533
|
-
});
|
|
57534
|
-
if (turn.error !== undefined) {
|
|
57535
|
-
return { move: undefined, error: `model error: ${turn.error.message}` };
|
|
57698
|
+
|
|
57699
|
+
// src/tui/games/tic-tac-toe/input.ts
|
|
57700
|
+
function wrapCursor(row, col) {
|
|
57701
|
+
return (row + 3) % 3 * 3 + (col + 3) % 3;
|
|
57702
|
+
}
|
|
57703
|
+
function onKey(state, key) {
|
|
57704
|
+
const name = key.name || key.sequence;
|
|
57705
|
+
if (isGameOver(state)) {
|
|
57706
|
+
return;
|
|
57536
57707
|
}
|
|
57537
|
-
|
|
57538
|
-
|
|
57708
|
+
const row = Math.floor(state.cursor / 3);
|
|
57709
|
+
const col = state.cursor % 3;
|
|
57710
|
+
if (name === "up" || name === "k") {
|
|
57711
|
+
return { ...state, cursor: wrapCursor(row - 1, col) };
|
|
57539
57712
|
}
|
|
57540
|
-
|
|
57713
|
+
if (name === "down" || name === "j") {
|
|
57714
|
+
return { ...state, cursor: wrapCursor(row + 1, col) };
|
|
57715
|
+
}
|
|
57716
|
+
if (name === "left" || name === "h") {
|
|
57717
|
+
return { ...state, cursor: wrapCursor(row, col - 1) };
|
|
57718
|
+
}
|
|
57719
|
+
if (name === "right" || name === "l") {
|
|
57720
|
+
return { ...state, cursor: wrapCursor(row, col + 1) };
|
|
57721
|
+
}
|
|
57722
|
+
if (name === "return" || name === "enter" || name === "space" || name === " ") {
|
|
57723
|
+
if (state.turn !== "X") {
|
|
57724
|
+
return;
|
|
57725
|
+
}
|
|
57726
|
+
return placeMark(state.board, state.cursor, "X");
|
|
57727
|
+
}
|
|
57728
|
+
return;
|
|
57541
57729
|
}
|
|
57542
|
-
|
|
57543
|
-
|
|
57544
|
-
|
|
57545
|
-
|
|
57546
|
-
|
|
57547
|
-
];
|
|
57730
|
+
function markFor(player) {
|
|
57731
|
+
return player === "human" ? "X" : "O";
|
|
57732
|
+
}
|
|
57733
|
+
|
|
57734
|
+
// src/tui/games/tic-tac-toe/layout.ts
|
|
57548
57735
|
var GAME_CELL_GAP = 1;
|
|
57549
57736
|
var GAME_BOARD_CHROME_X = 4;
|
|
57550
57737
|
var GAME_CELL_SIZES = {
|
|
@@ -57558,7 +57745,243 @@ function resolveCellSize(availableWidth) {
|
|
|
57558
57745
|
const large = GAME_CELL_SIZES.large;
|
|
57559
57746
|
return gameBoardWidth(large.width) <= availableWidth ? large : GAME_CELL_SIZES.small;
|
|
57560
57747
|
}
|
|
57561
|
-
|
|
57748
|
+
|
|
57749
|
+
// src/tui/games/tic-tac-toe/render.ts
|
|
57750
|
+
function markColor(mark, theme) {
|
|
57751
|
+
return mark === "X" ? theme.ok ?? "" : theme.error ?? "";
|
|
57752
|
+
}
|
|
57753
|
+
function statusText(state) {
|
|
57754
|
+
if (state.winner !== null) {
|
|
57755
|
+
return `${state.winner} wins! (r \u2014 new game)`;
|
|
57756
|
+
}
|
|
57757
|
+
if (state.draw) {
|
|
57758
|
+
return "Draw! (r \u2014 new game)";
|
|
57759
|
+
}
|
|
57760
|
+
return state.turn === "X" ? "Your turn \u2014 X" : "Agent's turn \u2014 O";
|
|
57761
|
+
}
|
|
57762
|
+
function render2(state, ctx) {
|
|
57763
|
+
const core = ctx.core;
|
|
57764
|
+
const r = ctx.renderer;
|
|
57765
|
+
const theme = ctx.theme;
|
|
57766
|
+
const cellSize = resolveCellSize(ctx.width);
|
|
57767
|
+
const wrap2 = new core.BoxRenderable(r, {
|
|
57768
|
+
id: "game-wrap",
|
|
57769
|
+
width: "100%",
|
|
57770
|
+
flexDirection: "column",
|
|
57771
|
+
alignItems: "center"
|
|
57772
|
+
});
|
|
57773
|
+
const legend = new core.BoxRenderable(r, {
|
|
57774
|
+
id: "game-legend",
|
|
57775
|
+
flexDirection: "row",
|
|
57776
|
+
marginBottom: 1
|
|
57777
|
+
});
|
|
57778
|
+
legend.add(new core.TextRenderable(r, { id: "game-legend-x", content: "X you", fg: theme.ok }));
|
|
57779
|
+
legend.add(new core.TextRenderable(r, { id: "game-legend-sep", content: " \xB7 ", fg: theme.muted }));
|
|
57780
|
+
legend.add(new core.TextRenderable(r, { id: "game-legend-o", content: "O model", fg: theme.error }));
|
|
57781
|
+
wrap2.add(legend);
|
|
57782
|
+
const board = new core.BoxRenderable(r, {
|
|
57783
|
+
id: "game-board",
|
|
57784
|
+
flexDirection: "column",
|
|
57785
|
+
flexShrink: 0,
|
|
57786
|
+
border: true,
|
|
57787
|
+
borderStyle: "rounded",
|
|
57788
|
+
borderColor: theme.border,
|
|
57789
|
+
backgroundColor: theme.panel,
|
|
57790
|
+
paddingLeft: 1,
|
|
57791
|
+
paddingRight: 1
|
|
57792
|
+
});
|
|
57793
|
+
for (let row = 0;row < 3; row++) {
|
|
57794
|
+
const rowBox = new core.BoxRenderable(r, {
|
|
57795
|
+
id: `game-row-${row}`,
|
|
57796
|
+
flexDirection: "row",
|
|
57797
|
+
flexShrink: 0,
|
|
57798
|
+
gap: 1
|
|
57799
|
+
});
|
|
57800
|
+
for (let col = 0;col < 3; col++) {
|
|
57801
|
+
const index = row * 3 + col;
|
|
57802
|
+
const cell = state.board[index] ?? null;
|
|
57803
|
+
const isCursor = index === state.cursor && !isGameOver(state);
|
|
57804
|
+
const won = state.winLine?.includes(index) === true;
|
|
57805
|
+
const cellBox = new core.BoxRenderable(r, {
|
|
57806
|
+
id: `game-cell-${index}`,
|
|
57807
|
+
width: cellSize.width,
|
|
57808
|
+
height: cellSize.height,
|
|
57809
|
+
flexShrink: 0,
|
|
57810
|
+
flexGrow: 0,
|
|
57811
|
+
flexDirection: "row",
|
|
57812
|
+
alignItems: "center",
|
|
57813
|
+
justifyContent: "center",
|
|
57814
|
+
border: true,
|
|
57815
|
+
borderStyle: "rounded",
|
|
57816
|
+
borderColor: won ? markColor(state.winner ?? "X", theme) : isCursor ? theme.focus : theme.border,
|
|
57817
|
+
backgroundColor: isCursor || won ? theme.highlight : undefined
|
|
57818
|
+
});
|
|
57819
|
+
const cellText = new core.TextRenderable(r, {
|
|
57820
|
+
id: `game-cell-text-${index}`,
|
|
57821
|
+
content: cell ?? "\xB7",
|
|
57822
|
+
fg: cell === null ? theme.muted : markColor(cell, theme)
|
|
57823
|
+
});
|
|
57824
|
+
cellBox.add(cellText);
|
|
57825
|
+
rowBox.add(cellBox);
|
|
57826
|
+
}
|
|
57827
|
+
board.add(rowBox);
|
|
57828
|
+
}
|
|
57829
|
+
wrap2.add(board);
|
|
57830
|
+
wrap2.add(new core.TextRenderable(r, { id: "game-status", content: statusText(state), marginTop: 1 }));
|
|
57831
|
+
ctx.parent.add(wrap2);
|
|
57832
|
+
}
|
|
57833
|
+
|
|
57834
|
+
// src/tui/games/tic-tac-toe/game.ts
|
|
57835
|
+
function statusOf2(s) {
|
|
57836
|
+
if (s.winner !== null) {
|
|
57837
|
+
return `${s.winner} wins! (r \u2014 new game)`;
|
|
57838
|
+
}
|
|
57839
|
+
if (s.draw) {
|
|
57840
|
+
return "Draw! (r \u2014 new game)";
|
|
57841
|
+
}
|
|
57842
|
+
return s.turn === "X" ? "Your turn \u2014 X" : "Agent's turn \u2014 O";
|
|
57843
|
+
}
|
|
57844
|
+
var ticTacToeGame = {
|
|
57845
|
+
id: "tic-tac-toe",
|
|
57846
|
+
label: "Tic-tac-toe",
|
|
57847
|
+
fresh: () => freshGame(),
|
|
57848
|
+
turn: (state) => asTtt(state).turn === "X" ? "human" : "model",
|
|
57849
|
+
isOver: (state) => isGameOver(asTtt(state)),
|
|
57850
|
+
outcome: (state) => {
|
|
57851
|
+
const s = asTtt(state);
|
|
57852
|
+
if (s.winner === "X") {
|
|
57853
|
+
return "human";
|
|
57854
|
+
}
|
|
57855
|
+
if (s.winner === "O") {
|
|
57856
|
+
return "model";
|
|
57857
|
+
}
|
|
57858
|
+
return s.draw ? "draw" : null;
|
|
57859
|
+
},
|
|
57860
|
+
status: (state) => statusOf2(asTtt(state)),
|
|
57861
|
+
systemPrompt: () => gameSystemPrompt(),
|
|
57862
|
+
stateForModel: (state) => gameUserPrompt(asTtt(state).board),
|
|
57863
|
+
parseMove: (reply, state) => parseModelMove(reply, asTtt(state).board),
|
|
57864
|
+
applyMove: (state, move, by) => placeMark(asTtt(state).board, move, markFor(by)),
|
|
57865
|
+
localMove: (state, by) => bestLocalMove(asTtt(state).board, markFor(by)),
|
|
57866
|
+
pass: (state) => {
|
|
57867
|
+
const s = asTtt(state);
|
|
57868
|
+
return { ...s, turn: s.turn === "X" ? "O" : "X" };
|
|
57869
|
+
},
|
|
57870
|
+
onKey: (state, key) => onKey(asTtt(state), key),
|
|
57871
|
+
render: (state, ctx) => render2(asTtt(state), ctx)
|
|
57872
|
+
};
|
|
57873
|
+
// src/tui/games/model-turn.ts
|
|
57874
|
+
init_single_turn();
|
|
57875
|
+
async function runGameModelTurn(game, state, opts) {
|
|
57876
|
+
const startedAt = performance.now();
|
|
57877
|
+
const turn = await runModelTurn({
|
|
57878
|
+
system: game.systemPrompt(),
|
|
57879
|
+
user: game.stateForModel(state),
|
|
57880
|
+
...opts.provider !== undefined ? { provider: opts.provider } : {},
|
|
57881
|
+
...opts.model !== undefined ? { model: opts.model } : {},
|
|
57882
|
+
maxOutputTokens: 256,
|
|
57883
|
+
requestId: "keryx-game",
|
|
57884
|
+
...opts.providerFactory !== undefined ? { providerFactory: opts.providerFactory } : {},
|
|
57885
|
+
...opts.env !== undefined ? { env: opts.env } : {}
|
|
57886
|
+
});
|
|
57887
|
+
const totalMs = Math.max(0, Math.round(performance.now() - startedAt));
|
|
57888
|
+
const stats = {
|
|
57889
|
+
provider: turn.provider,
|
|
57890
|
+
model: turn.model,
|
|
57891
|
+
latencyMs: turn.latencyMs,
|
|
57892
|
+
totalMs,
|
|
57893
|
+
inputTokens: turn.usage?.inputTokens,
|
|
57894
|
+
outputTokens: turn.usage?.outputTokens,
|
|
57895
|
+
reasoning: turn.reasoning === true,
|
|
57896
|
+
localFallback: false,
|
|
57897
|
+
error: turn.error !== undefined
|
|
57898
|
+
};
|
|
57899
|
+
if (turn.error !== undefined) {
|
|
57900
|
+
return { move: undefined, error: `model error: ${turn.error.message}`, stats };
|
|
57901
|
+
}
|
|
57902
|
+
if (!turn.credentialAvailable && opts.providerFactory === undefined) {
|
|
57903
|
+
return { move: undefined, error: "no model credential \u2014 configure a provider first (/provider)", stats };
|
|
57904
|
+
}
|
|
57905
|
+
return { move: game.parseMove(turn.text, state), error: undefined, stats };
|
|
57906
|
+
}
|
|
57907
|
+
|
|
57908
|
+
// src/tui/games/agent-panel.ts
|
|
57909
|
+
var MODEL_MAX = 22;
|
|
57910
|
+
function truncate4(value, max) {
|
|
57911
|
+
return value.length > max ? `${value.slice(0, max - 1)}\u2026` : value;
|
|
57912
|
+
}
|
|
57913
|
+
function renderAgentPanel(game, parent, core, renderer, args2) {
|
|
57914
|
+
const theme = getTheme();
|
|
57915
|
+
const box = (opts) => new core.BoxRenderable(renderer, opts);
|
|
57916
|
+
const text = (opts) => new core.TextRenderable(renderer, opts);
|
|
57917
|
+
const card = (id, extra = {}) => box({
|
|
57918
|
+
id,
|
|
57919
|
+
flexDirection: "column",
|
|
57920
|
+
border: true,
|
|
57921
|
+
borderStyle: "rounded",
|
|
57922
|
+
borderColor: theme.border,
|
|
57923
|
+
backgroundColor: theme.panel,
|
|
57924
|
+
paddingLeft: 1,
|
|
57925
|
+
paddingRight: 1,
|
|
57926
|
+
...extra
|
|
57927
|
+
});
|
|
57928
|
+
const statRow = (owner, label, value, id, valueFg = theme.text) => {
|
|
57929
|
+
const rowBox = box({ flexDirection: "row", gap: 1 });
|
|
57930
|
+
rowBox.add(text({ content: label, fg: theme.muted }));
|
|
57931
|
+
rowBox.add(text({ id, content: value, fg: valueFg }));
|
|
57932
|
+
owner.add(rowBox);
|
|
57933
|
+
};
|
|
57934
|
+
const idle = args2.notice === undefined || args2.notice === "";
|
|
57935
|
+
const statusCard = card("game-status-card", { width: "100%", marginTop: 1 });
|
|
57936
|
+
statusCard.add(text({
|
|
57937
|
+
id: "game-notice",
|
|
57938
|
+
content: args2.modelBusy ? "agent is thinking\u2026" : idle ? "waiting for your move" : args2.notice,
|
|
57939
|
+
fg: args2.modelBusy ? theme.focus : idle ? theme.text : theme.error
|
|
57940
|
+
}));
|
|
57941
|
+
parent.add(statusCard);
|
|
57942
|
+
const sysLines = game.systemPrompt().split(`
|
|
57943
|
+
`);
|
|
57944
|
+
const sysShown = sysLines.length > 6 ? [...sysLines.slice(0, 6), `\u2026 (+${sysLines.length - 6} more)`] : sysLines;
|
|
57945
|
+
const sysCard = card("game-system-card", { width: "100%", marginTop: 1 });
|
|
57946
|
+
sysCard.add(text({ id: "game-system-title", content: "system prompt", fg: theme.muted }));
|
|
57947
|
+
sysCard.add(text({ id: "game-system", content: sysShown.join(`
|
|
57948
|
+
`), fg: theme.muted }));
|
|
57949
|
+
parent.add(sysCard);
|
|
57950
|
+
const lt = args2.lastTurn;
|
|
57951
|
+
const tot = args2.totals;
|
|
57952
|
+
const statsRow = box({ id: "game-stats-row", width: "100%", flexDirection: "row", gap: 1, marginTop: 1 });
|
|
57953
|
+
const lastTurnCard = card("game-last-turn", { flexGrow: 1, flexShrink: 0 });
|
|
57954
|
+
lastTurnCard.add(text({ id: "game-last-turn-title", content: "last turn", fg: theme.muted }));
|
|
57955
|
+
if (lt === undefined) {
|
|
57956
|
+
lastTurnCard.add(text({ id: "game-stats-empty", content: "no turns yet", fg: theme.muted }));
|
|
57957
|
+
} else {
|
|
57958
|
+
const model = lt.provider === "\u2013" ? "\u2013" : truncate4(`${lt.provider}/${lt.model}`, MODEL_MAX);
|
|
57959
|
+
statRow(lastTurnCard, "model", model, "game-stats-model");
|
|
57960
|
+
statRow(lastTurnCard, "first byte", formatMs(lt.latencyMs), "game-stats-latency");
|
|
57961
|
+
statRow(lastTurnCard, "total", formatMs(lt.totalMs), "game-stats-total");
|
|
57962
|
+
statRow(lastTurnCard, "tokens", `in ${formatTokens(lt.inputTokens)} \xB7 out ${formatTokens(lt.outputTokens)}`, "game-stats-tokens");
|
|
57963
|
+
if (lt.reasoning) {
|
|
57964
|
+
statRow(lastTurnCard, "reasoning", "yes", "game-stats-reasoning", theme.focus);
|
|
57965
|
+
}
|
|
57966
|
+
if (lt.localFallback) {
|
|
57967
|
+
statRow(lastTurnCard, "fallback", "local", "game-stats-fallback", theme.error);
|
|
57968
|
+
}
|
|
57969
|
+
if (lt.error) {
|
|
57970
|
+
statRow(lastTurnCard, "error", "yes", "game-stats-error", theme.error);
|
|
57971
|
+
}
|
|
57972
|
+
}
|
|
57973
|
+
const sessionCard = card("game-session", { flexGrow: 1, flexShrink: 0 });
|
|
57974
|
+
sessionCard.add(text({ id: "game-session-title", content: "session", fg: theme.muted }));
|
|
57975
|
+
statRow(sessionCard, "turns", String(tot.turns), "game-session-turns");
|
|
57976
|
+
statRow(sessionCard, "fallbacks", String(tot.localFallbacks), "game-session-fallbacks");
|
|
57977
|
+
statRow(sessionCard, "errors", String(tot.errors), "game-session-errors");
|
|
57978
|
+
statRow(sessionCard, "tokens", `in ${formatTokens(tot.inputTokens)} \xB7 out ${formatTokens(tot.outputTokens)}`, "game-session-tokens");
|
|
57979
|
+
statsRow.add(lastTurnCard);
|
|
57980
|
+
statsRow.add(sessionCard);
|
|
57981
|
+
parent.add(statsRow);
|
|
57982
|
+
}
|
|
57983
|
+
|
|
57984
|
+
// src/tui/games/otui.ts
|
|
57562
57985
|
function asOtui2(otui) {
|
|
57563
57986
|
if (otui === undefined || otui === null) {
|
|
57564
57987
|
return;
|
|
@@ -57569,60 +57992,90 @@ function asOtui2(otui) {
|
|
|
57569
57992
|
}
|
|
57570
57993
|
return cand;
|
|
57571
57994
|
}
|
|
57572
|
-
|
|
57573
|
-
|
|
57574
|
-
|
|
57575
|
-
|
|
57576
|
-
|
|
57577
|
-
|
|
57578
|
-
|
|
57579
|
-
|
|
57580
|
-
}
|
|
57581
|
-
function statusText(state) {
|
|
57582
|
-
if (state.winner !== null) {
|
|
57583
|
-
return `${state.winner} wins! (r \u2014 new game)`;
|
|
57584
|
-
}
|
|
57585
|
-
if (state.draw) {
|
|
57586
|
-
return "Draw! (r \u2014 new game)";
|
|
57995
|
+
|
|
57996
|
+
// src/tui/games/modal.ts
|
|
57997
|
+
var DEFAULT_GAMES = [ticTacToeGame];
|
|
57998
|
+
function presentGamesModal(openModalFn, otui, chrome, options = {}, registry = createRegistry(DEFAULT_GAMES)) {
|
|
57999
|
+
const games = registry.games;
|
|
58000
|
+
const first = games[0];
|
|
58001
|
+
if (first === undefined) {
|
|
58002
|
+
return;
|
|
57587
58003
|
}
|
|
57588
|
-
return state.turn === "X" ? "Your turn \u2014 X" : "Agent's turn \u2014 O";
|
|
57589
|
-
}
|
|
57590
|
-
function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
57591
|
-
const renderer = options.renderer;
|
|
57592
58004
|
const core = asOtui2(otui);
|
|
58005
|
+
const renderer = options.renderer;
|
|
57593
58006
|
let handle;
|
|
57594
|
-
let
|
|
57595
|
-
|
|
57596
|
-
|
|
57597
|
-
|
|
58007
|
+
let activeId = first.id;
|
|
58008
|
+
const states = new Map;
|
|
58009
|
+
const totals = new Map;
|
|
58010
|
+
const lastTurn = new Map;
|
|
57598
58011
|
let notice;
|
|
58012
|
+
let modelBusy = false;
|
|
57599
58013
|
let unsubscribeKey;
|
|
58014
|
+
let bodyRef;
|
|
58015
|
+
let bodyWidth = 0;
|
|
58016
|
+
const stateOf = (id) => {
|
|
58017
|
+
const game = registry.get(id);
|
|
58018
|
+
if (game === undefined) {
|
|
58019
|
+
throw new Error(`unknown game: ${id}`);
|
|
58020
|
+
}
|
|
58021
|
+
let state = states.get(id);
|
|
58022
|
+
if (state === undefined) {
|
|
58023
|
+
state = game.fresh();
|
|
58024
|
+
states.set(id, state);
|
|
58025
|
+
}
|
|
58026
|
+
return state;
|
|
58027
|
+
};
|
|
58028
|
+
const totalsOf = (id) => {
|
|
58029
|
+
let t = totals.get(id);
|
|
58030
|
+
if (t === undefined) {
|
|
58031
|
+
t = emptyTurnTotals();
|
|
58032
|
+
totals.set(id, t);
|
|
58033
|
+
}
|
|
58034
|
+
return t;
|
|
58035
|
+
};
|
|
57600
58036
|
const paint = () => {
|
|
57601
|
-
if (
|
|
58037
|
+
if (core === undefined || bodyRef === undefined) {
|
|
57602
58038
|
return;
|
|
57603
58039
|
}
|
|
57604
|
-
|
|
57605
|
-
|
|
57606
|
-
|
|
57607
|
-
|
|
57608
|
-
noticeBox.content = "agent is thinking\u2026";
|
|
57609
|
-
noticeBox.fg = theme.focus;
|
|
57610
|
-
} else {
|
|
57611
|
-
noticeBox.content = notice ?? "";
|
|
57612
|
-
noticeBox.fg = theme.error;
|
|
58040
|
+
clearTranscriptChildren(bodyRef);
|
|
58041
|
+
const game = registry.get(activeId);
|
|
58042
|
+
if (game === undefined) {
|
|
58043
|
+
return;
|
|
57613
58044
|
}
|
|
57614
|
-
|
|
57615
|
-
|
|
57616
|
-
|
|
57617
|
-
|
|
57618
|
-
|
|
57619
|
-
|
|
57620
|
-
|
|
57621
|
-
|
|
58045
|
+
const state = stateOf(activeId);
|
|
58046
|
+
const ctx = {
|
|
58047
|
+
core,
|
|
58048
|
+
renderer,
|
|
58049
|
+
theme: getTheme(),
|
|
58050
|
+
parent: bodyRef,
|
|
58051
|
+
width: bodyWidth
|
|
58052
|
+
};
|
|
58053
|
+
game.render(state, ctx);
|
|
58054
|
+
renderAgentPanel(game, bodyRef, core, renderer, {
|
|
58055
|
+
notice,
|
|
58056
|
+
modelBusy,
|
|
58057
|
+
lastTurn: lastTurn.get(activeId),
|
|
58058
|
+
totals: totalsOf(activeId)
|
|
58059
|
+
});
|
|
58060
|
+
};
|
|
58061
|
+
const restart = () => {
|
|
58062
|
+
const game = registry.get(activeId);
|
|
58063
|
+
if (game === undefined) {
|
|
58064
|
+
return;
|
|
57622
58065
|
}
|
|
58066
|
+
states.set(activeId, game.fresh());
|
|
58067
|
+
totals.set(activeId, emptyTurnTotals());
|
|
58068
|
+
lastTurn.set(activeId, undefined);
|
|
58069
|
+
notice = undefined;
|
|
58070
|
+
paint();
|
|
57623
58071
|
};
|
|
57624
58072
|
const applyModelMove = async () => {
|
|
57625
|
-
|
|
58073
|
+
const game = registry.get(activeId);
|
|
58074
|
+
if (game === undefined) {
|
|
58075
|
+
return;
|
|
58076
|
+
}
|
|
58077
|
+
const state = stateOf(activeId);
|
|
58078
|
+
if (modelBusy || game.isOver(state) || game.turn(state) !== "model") {
|
|
57626
58079
|
return;
|
|
57627
58080
|
}
|
|
57628
58081
|
modelBusy = true;
|
|
@@ -57633,30 +58086,51 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
|
57633
58086
|
const deadline = new Promise((resolve3) => {
|
|
57634
58087
|
timer = setTimeout(() => resolve3("timeout"), timeoutMs);
|
|
57635
58088
|
});
|
|
57636
|
-
const outcome = await Promise.race([
|
|
57637
|
-
modelMove(currentGame.board, {
|
|
57638
|
-
...options.provider !== undefined ? { provider: options.provider } : {},
|
|
57639
|
-
...options.model !== undefined ? { model: options.model } : {},
|
|
57640
|
-
...options.providerFactory !== undefined ? { providerFactory: options.providerFactory } : {},
|
|
57641
|
-
...options.env !== undefined ? { env: options.env } : {}
|
|
57642
|
-
}),
|
|
57643
|
-
deadline
|
|
57644
|
-
]);
|
|
58089
|
+
const outcome = await Promise.race([runGameModelTurn(game, state, options), deadline]);
|
|
57645
58090
|
if (timer !== undefined) {
|
|
57646
58091
|
clearTimeout(timer);
|
|
57647
58092
|
}
|
|
57648
58093
|
modelBusy = false;
|
|
57649
58094
|
const timedOut = outcome === "timeout";
|
|
57650
|
-
const result = timedOut ?
|
|
57651
|
-
let
|
|
58095
|
+
const result = timedOut ? undefined : outcome;
|
|
58096
|
+
let stats;
|
|
58097
|
+
if (timedOut) {
|
|
58098
|
+
stats = {
|
|
58099
|
+
provider: "\u2013",
|
|
58100
|
+
model: "\u2013",
|
|
58101
|
+
latencyMs: undefined,
|
|
58102
|
+
totalMs: timeoutMs,
|
|
58103
|
+
inputTokens: undefined,
|
|
58104
|
+
outputTokens: undefined,
|
|
58105
|
+
reasoning: false,
|
|
58106
|
+
localFallback: true,
|
|
58107
|
+
error: false
|
|
58108
|
+
};
|
|
58109
|
+
} else if (result !== undefined) {
|
|
58110
|
+
stats = result.stats;
|
|
58111
|
+
} else {
|
|
58112
|
+
stats = {
|
|
58113
|
+
provider: "\u2013",
|
|
58114
|
+
model: "\u2013",
|
|
58115
|
+
latencyMs: undefined,
|
|
58116
|
+
totalMs: undefined,
|
|
58117
|
+
inputTokens: undefined,
|
|
58118
|
+
outputTokens: undefined,
|
|
58119
|
+
reasoning: false,
|
|
58120
|
+
localFallback: true,
|
|
58121
|
+
error: true
|
|
58122
|
+
};
|
|
58123
|
+
}
|
|
58124
|
+
let move = result?.move;
|
|
57652
58125
|
let playedLocally = false;
|
|
57653
|
-
if (move === undefined && result
|
|
57654
|
-
move =
|
|
58126
|
+
if (move === undefined && result?.error === undefined) {
|
|
58127
|
+
move = game.localMove(state, "model");
|
|
57655
58128
|
playedLocally = move !== undefined;
|
|
58129
|
+
stats = { ...stats, localFallback: playedLocally };
|
|
57656
58130
|
}
|
|
57657
|
-
const placed = move === undefined ? undefined :
|
|
57658
|
-
|
|
57659
|
-
if (result
|
|
58131
|
+
const placed = move === undefined ? undefined : game.applyMove(state, move, "model");
|
|
58132
|
+
const next = placed ?? game.pass(state);
|
|
58133
|
+
if (result?.error !== undefined) {
|
|
57660
58134
|
notice = `agent: ${result.error}`;
|
|
57661
58135
|
} else if (timedOut && playedLocally) {
|
|
57662
58136
|
notice = `agent timed out after ${Math.round(timeoutMs / 1000)}s \u2014 played a local move`;
|
|
@@ -57665,132 +58139,29 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
|
57665
58139
|
} else {
|
|
57666
58140
|
notice = undefined;
|
|
57667
58141
|
}
|
|
57668
|
-
|
|
57669
|
-
|
|
57670
|
-
|
|
57671
|
-
if (modelBusy || isGameOver(currentGame) || currentGame.turn !== "X") {
|
|
57672
|
-
return;
|
|
57673
|
-
}
|
|
57674
|
-
const placed = placeMark(currentGame.board, cursor, "X");
|
|
57675
|
-
if (placed === undefined) {
|
|
57676
|
-
return;
|
|
57677
|
-
}
|
|
57678
|
-
currentGame = placed;
|
|
57679
|
-
notice = undefined;
|
|
57680
|
-
paint();
|
|
57681
|
-
if (!isGameOver(currentGame) && currentGame.turn === "O") {
|
|
57682
|
-
applyModelMove();
|
|
57683
|
-
}
|
|
57684
|
-
};
|
|
57685
|
-
const moveCursor = (dr, dc) => {
|
|
57686
|
-
if (modelBusy) {
|
|
57687
|
-
return;
|
|
57688
|
-
}
|
|
57689
|
-
const row = Math.floor(cursor / 3);
|
|
57690
|
-
const col = cursor % 3;
|
|
57691
|
-
cursor = (row + dr + 3) % 3 * 3 + (col + dc + 3) % 3;
|
|
57692
|
-
paint();
|
|
57693
|
-
};
|
|
57694
|
-
const restart = () => {
|
|
57695
|
-
resetGame();
|
|
57696
|
-
cursor = 4;
|
|
57697
|
-
notice = undefined;
|
|
58142
|
+
states.set(activeId, next);
|
|
58143
|
+
totals.set(activeId, addTurn(totalsOf(activeId), stats));
|
|
58144
|
+
lastTurn.set(activeId, stats);
|
|
57698
58145
|
paint();
|
|
57699
58146
|
};
|
|
57700
58147
|
handle = openModalFn(otui, chrome, {
|
|
57701
58148
|
title: "/game",
|
|
57702
|
-
tabs:
|
|
57703
|
-
footer:
|
|
57704
|
-
onArrowKeys: (
|
|
57705
|
-
|
|
57706
|
-
|
|
58149
|
+
tabs: games.map((game) => ({ id: game.id, label: game.label })),
|
|
58150
|
+
footer: GAMES_FOOTER,
|
|
58151
|
+
onArrowKeys: (key, _direction) => {
|
|
58152
|
+
const game = registry.get(activeId);
|
|
58153
|
+
if (game === undefined || modelBusy) {
|
|
58154
|
+
return false;
|
|
58155
|
+
}
|
|
58156
|
+
const state = stateOf(activeId);
|
|
58157
|
+
return game.onKey(state, { name: key.name, sequence: key.sequence }) !== undefined;
|
|
57707
58158
|
},
|
|
57708
58159
|
renderTab: (_tabId, body, ctx) => {
|
|
57709
58160
|
if (body === undefined || body === null) {
|
|
57710
58161
|
return;
|
|
57711
58162
|
}
|
|
57712
|
-
|
|
57713
|
-
|
|
57714
|
-
return;
|
|
57715
|
-
}
|
|
57716
|
-
const theme = getTheme();
|
|
57717
|
-
const r = renderer;
|
|
57718
|
-
const cellSize = resolveCellSize(ctx.width);
|
|
57719
|
-
const wrap2 = new core.BoxRenderable(r, {
|
|
57720
|
-
id: "game-wrap",
|
|
57721
|
-
width: "100%",
|
|
57722
|
-
flexDirection: "column",
|
|
57723
|
-
alignItems: "center"
|
|
57724
|
-
});
|
|
57725
|
-
const legend = new core.BoxRenderable(r, {
|
|
57726
|
-
id: "game-legend",
|
|
57727
|
-
flexDirection: "row",
|
|
57728
|
-
marginBottom: 1
|
|
57729
|
-
});
|
|
57730
|
-
legend.add(new core.TextRenderable(r, { id: "game-legend-x", content: "X you", fg: theme.ok }));
|
|
57731
|
-
legend.add(new core.TextRenderable(r, { id: "game-legend-sep", content: " \xB7 ", fg: theme.muted }));
|
|
57732
|
-
legend.add(new core.TextRenderable(r, { id: "game-legend-o", content: "O model", fg: theme.error }));
|
|
57733
|
-
wrap2.add(legend);
|
|
57734
|
-
const board = new core.BoxRenderable(r, {
|
|
57735
|
-
id: "game-board",
|
|
57736
|
-
flexDirection: "column",
|
|
57737
|
-
flexShrink: 0,
|
|
57738
|
-
border: true,
|
|
57739
|
-
borderStyle: "rounded",
|
|
57740
|
-
borderColor: theme.border,
|
|
57741
|
-
backgroundColor: theme.panel,
|
|
57742
|
-
paddingLeft: 1,
|
|
57743
|
-
paddingRight: 1
|
|
57744
|
-
});
|
|
57745
|
-
cells = [];
|
|
57746
|
-
for (let row = 0;row < 3; row++) {
|
|
57747
|
-
const rowBox = new core.BoxRenderable(r, {
|
|
57748
|
-
id: `game-row-${row}`,
|
|
57749
|
-
flexDirection: "row",
|
|
57750
|
-
flexShrink: 0,
|
|
57751
|
-
gap: GAME_CELL_GAP
|
|
57752
|
-
});
|
|
57753
|
-
for (let col = 0;col < 3; col++) {
|
|
57754
|
-
const index = row * 3 + col;
|
|
57755
|
-
const cellBox = new core.BoxRenderable(r, {
|
|
57756
|
-
id: `game-cell-${index}`,
|
|
57757
|
-
width: cellSize.width,
|
|
57758
|
-
height: cellSize.height,
|
|
57759
|
-
flexShrink: 0,
|
|
57760
|
-
flexGrow: 0,
|
|
57761
|
-
flexDirection: "row",
|
|
57762
|
-
alignItems: "center",
|
|
57763
|
-
justifyContent: "center",
|
|
57764
|
-
border: true,
|
|
57765
|
-
borderStyle: "rounded",
|
|
57766
|
-
borderColor: theme.border
|
|
57767
|
-
});
|
|
57768
|
-
const cellText = new core.TextRenderable(r, {
|
|
57769
|
-
id: `game-cell-text-${index}`,
|
|
57770
|
-
content: "\xB7",
|
|
57771
|
-
fg: theme.muted
|
|
57772
|
-
});
|
|
57773
|
-
cellBox.add(cellText);
|
|
57774
|
-
rowBox.add(cellBox);
|
|
57775
|
-
cells.push({ box: cellBox, text: cellText });
|
|
57776
|
-
}
|
|
57777
|
-
board.add(rowBox);
|
|
57778
|
-
}
|
|
57779
|
-
wrap2.add(board);
|
|
57780
|
-
const status = new core.TextRenderable(r, {
|
|
57781
|
-
id: "game-status",
|
|
57782
|
-
content: "",
|
|
57783
|
-
marginTop: 1
|
|
57784
|
-
});
|
|
57785
|
-
statusBox = status;
|
|
57786
|
-
wrap2.add(status);
|
|
57787
|
-
const noticeText = new core.TextRenderable(r, {
|
|
57788
|
-
id: "game-notice",
|
|
57789
|
-
content: ""
|
|
57790
|
-
});
|
|
57791
|
-
noticeBox = noticeText;
|
|
57792
|
-
wrap2.add(noticeText);
|
|
57793
|
-
parent.add(wrap2);
|
|
58163
|
+
bodyRef = body;
|
|
58164
|
+
bodyWidth = ctx.width;
|
|
57794
58165
|
paint();
|
|
57795
58166
|
},
|
|
57796
58167
|
onClose: () => {
|
|
@@ -57803,39 +58174,92 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
|
57803
58174
|
if (options.onKeypress !== undefined) {
|
|
57804
58175
|
unsubscribeKey = options.onKeypress((key) => {
|
|
57805
58176
|
const token = key.name || key.sequence;
|
|
57806
|
-
if (token === "
|
|
57807
|
-
|
|
57808
|
-
return;
|
|
57809
|
-
}
|
|
57810
|
-
if (token === "down" || token === "j") {
|
|
57811
|
-
moveCursor(1, 0);
|
|
57812
|
-
return;
|
|
57813
|
-
}
|
|
57814
|
-
if (token === "h") {
|
|
57815
|
-
moveCursor(0, -1);
|
|
58177
|
+
if (token === "r" || token === "R") {
|
|
58178
|
+
restart();
|
|
57816
58179
|
return;
|
|
57817
58180
|
}
|
|
57818
|
-
|
|
57819
|
-
|
|
58181
|
+
const game = registry.get(activeId);
|
|
58182
|
+
if (game === undefined || modelBusy) {
|
|
57820
58183
|
return;
|
|
57821
58184
|
}
|
|
57822
|
-
|
|
57823
|
-
|
|
58185
|
+
const state = stateOf(activeId);
|
|
58186
|
+
const next = game.onKey(state, key);
|
|
58187
|
+
if (next === undefined) {
|
|
57824
58188
|
return;
|
|
57825
58189
|
}
|
|
57826
|
-
|
|
57827
|
-
|
|
58190
|
+
states.set(activeId, next);
|
|
58191
|
+
paint();
|
|
58192
|
+
if (!game.isOver(next) && game.turn(next) === "model") {
|
|
58193
|
+
applyModelMove();
|
|
57828
58194
|
}
|
|
57829
58195
|
});
|
|
57830
58196
|
}
|
|
57831
58197
|
return {
|
|
57832
58198
|
close: () => handle?.close(),
|
|
57833
58199
|
restart,
|
|
58200
|
+
activeGameId: () => activeId,
|
|
57834
58201
|
modelThinking: () => modelBusy
|
|
57835
58202
|
};
|
|
57836
58203
|
}
|
|
57837
|
-
function
|
|
57838
|
-
return
|
|
58204
|
+
function openGamesModal(otui, chrome, options = {}) {
|
|
58205
|
+
return presentGamesModal((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
58206
|
+
}
|
|
58207
|
+
// src/tui/balance-panel.ts
|
|
58208
|
+
init_providers();
|
|
58209
|
+
init_shell_config();
|
|
58210
|
+
function formatBalance(balance) {
|
|
58211
|
+
if (balance === undefined) {
|
|
58212
|
+
return "\u2014";
|
|
58213
|
+
}
|
|
58214
|
+
const amount = balance.remaining ?? balance.total;
|
|
58215
|
+
const symbol = balance.currency === "USD" ? "$" : balance.currency === "EUR" ? "\u20AC" : balance.currency === "GBP" ? "\xA3" : `${balance.currency} `;
|
|
58216
|
+
return `${symbol}${amount.toFixed(2)}`;
|
|
58217
|
+
}
|
|
58218
|
+
function mountBalancePanel(sidebarTop, otui, renderer, options) {
|
|
58219
|
+
const core = otui;
|
|
58220
|
+
const r = renderer;
|
|
58221
|
+
const env = envWithSavedApiKeys(options.env ?? process.env);
|
|
58222
|
+
let current;
|
|
58223
|
+
let textNode;
|
|
58224
|
+
const label = new core.TextRenderable(r, {
|
|
58225
|
+
id: "sb-balance-k",
|
|
58226
|
+
content: core.t`${core.dim("Balance")}`,
|
|
58227
|
+
marginTop: 1
|
|
58228
|
+
});
|
|
58229
|
+
sidebarTop.add(label);
|
|
58230
|
+
const value = new core.TextRenderable(r, {
|
|
58231
|
+
id: "sb-balance-v",
|
|
58232
|
+
content: core.t`${core.dim("\u2026")}`,
|
|
58233
|
+
onMouseDown: () => {
|
|
58234
|
+
refresh();
|
|
58235
|
+
}
|
|
58236
|
+
});
|
|
58237
|
+
textNode = value;
|
|
58238
|
+
sidebarTop.add(value);
|
|
58239
|
+
const paint = (balance) => {
|
|
58240
|
+
current = balance;
|
|
58241
|
+
if (textNode !== undefined) {
|
|
58242
|
+
textNode.content = core.t`${core.dim(formatBalance(balance))}`;
|
|
58243
|
+
}
|
|
58244
|
+
};
|
|
58245
|
+
const refresh = async () => {
|
|
58246
|
+
const provider = balanceCapableProvider(options.provider);
|
|
58247
|
+
if (provider === undefined) {
|
|
58248
|
+
paint(undefined);
|
|
58249
|
+
return;
|
|
58250
|
+
}
|
|
58251
|
+
const apiKey = providerApiKey(provider, env);
|
|
58252
|
+
if (apiKey === undefined) {
|
|
58253
|
+
paint(undefined);
|
|
58254
|
+
return;
|
|
58255
|
+
}
|
|
58256
|
+
const base = resolveProviderBaseUrl(provider, env);
|
|
58257
|
+
const withBase = { ...provider, baseUrl: base };
|
|
58258
|
+
const balance = await fetchProviderBalance(options.fetch ?? globalThis.fetch, withBase, apiKey, { ...options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {} });
|
|
58259
|
+
paint(balance);
|
|
58260
|
+
};
|
|
58261
|
+
refresh();
|
|
58262
|
+
return { refresh, current: () => current };
|
|
57839
58263
|
}
|
|
57840
58264
|
|
|
57841
58265
|
// src/tui/tui-shell.ts
|
|
@@ -58175,7 +58599,7 @@ function showComposerChoice(otui, r, dock, request) {
|
|
|
58175
58599
|
} catch {}
|
|
58176
58600
|
dock.visible = false;
|
|
58177
58601
|
}
|
|
58178
|
-
const
|
|
58602
|
+
const onKey2 = (key) => {
|
|
58179
58603
|
if (key.name === "escape") {
|
|
58180
58604
|
finish(request.cancelId);
|
|
58181
58605
|
key.preventDefault();
|
|
@@ -58222,7 +58646,7 @@ function showComposerChoice(otui, r, dock, request) {
|
|
|
58222
58646
|
key.stopPropagation();
|
|
58223
58647
|
}
|
|
58224
58648
|
};
|
|
58225
|
-
const unsub = onKeypress2(r,
|
|
58649
|
+
const unsub = onKeypress2(r, onKey2);
|
|
58226
58650
|
optionsScroll.focus();
|
|
58227
58651
|
});
|
|
58228
58652
|
}
|
|
@@ -58663,6 +59087,18 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
58663
59087
|
});
|
|
58664
59088
|
main.add(scroll);
|
|
58665
59089
|
const transcript = scroll.content;
|
|
59090
|
+
let liveStatus;
|
|
59091
|
+
const originalTranscriptAdd = transcript.add.bind(transcript);
|
|
59092
|
+
const originalTranscriptRemove = transcript.remove.bind(transcript);
|
|
59093
|
+
transcript.add = (child, index) => {
|
|
59094
|
+
if (liveStatus !== undefined && child !== liveStatus) {
|
|
59095
|
+
originalTranscriptRemove(liveStatus);
|
|
59096
|
+
const added = originalTranscriptAdd(child, index);
|
|
59097
|
+
originalTranscriptAdd(liveStatus);
|
|
59098
|
+
return added;
|
|
59099
|
+
}
|
|
59100
|
+
return originalTranscriptAdd(child, index);
|
|
59101
|
+
};
|
|
58666
59102
|
const queueDock = new otui.BoxRenderable(r, {
|
|
58667
59103
|
id: "queue-dock",
|
|
58668
59104
|
flexShrink: 0,
|
|
@@ -58837,7 +59273,6 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
58837
59273
|
const footerRight = new otui.TextRenderable(r, { id: "footer-right", content: otui.t`${otui.dim(opts.status)}` });
|
|
58838
59274
|
footer.add(footerRight);
|
|
58839
59275
|
main.add(footer);
|
|
58840
|
-
let liveStatus;
|
|
58841
59276
|
let busyPhase = "waiting for model";
|
|
58842
59277
|
let busyStartedAt = 0;
|
|
58843
59278
|
let spinIdx = 0;
|
|
@@ -58852,12 +59287,15 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
58852
59287
|
}
|
|
58853
59288
|
if (!busy) {
|
|
58854
59289
|
footerLeft.content = otui.t`${otui.dim(opts.footerHint)}`;
|
|
59290
|
+
footerRight.content = otui.t`${otui.dim(opts.status)}`;
|
|
58855
59291
|
return;
|
|
58856
59292
|
}
|
|
58857
59293
|
const frame = SPINNER[spinIdx % SPINNER.length] ?? "\u280B";
|
|
58858
59294
|
const secs = ((Date.now() - busyStartedAt) / 1000).toFixed(1);
|
|
58859
59295
|
const line = `${frame} ${busyPhase} \xB7 ${secs}s`;
|
|
58860
59296
|
footerLeft.content = otui.t`${otui.yellow(line)}`;
|
|
59297
|
+
const perm = opts.permissionMode?.();
|
|
59298
|
+
footerRight.content = otui.t`${otui.dim(perm !== undefined && perm.length > 0 ? `mode ${perm}` : opts.status)}`;
|
|
58861
59299
|
if (liveStatus !== undefined) {
|
|
58862
59300
|
liveStatus.content = otui.t`${otui.dim(line)}`;
|
|
58863
59301
|
}
|
|
@@ -61339,6 +61777,7 @@ function attachUsageIo(io, chrome) {
|
|
|
61339
61777
|
totalOut += usage.outputTokens ?? 0;
|
|
61340
61778
|
chrome.setHeaderMeta(`\u2191${fmtTokens(totalIn)} \u2193${fmtTokens(totalOut)}`);
|
|
61341
61779
|
chrome.setContextTotal(totalIn + totalOut);
|
|
61780
|
+
chrome.setUsage?.(totalIn, totalOut);
|
|
61342
61781
|
};
|
|
61343
61782
|
return Object.assign(io, {
|
|
61344
61783
|
resetUsage() {
|
|
@@ -61835,7 +62274,7 @@ function promptApiKeyStep(otui, r, opts) {
|
|
|
61835
62274
|
const keyInput = new otui.InputRenderable(r, { id: "kp-input", placeholder: opts.placeholder ?? "sk-...", marginTop: 1 });
|
|
61836
62275
|
box.add(keyInput);
|
|
61837
62276
|
keyInput.focus();
|
|
61838
|
-
const
|
|
62277
|
+
const onKey2 = (key) => {
|
|
61839
62278
|
if (key.name === "escape") {
|
|
61840
62279
|
cleanup();
|
|
61841
62280
|
resolve3({ kind: "back" });
|
|
@@ -61843,7 +62282,7 @@ function promptApiKeyStep(otui, r, opts) {
|
|
|
61843
62282
|
key.stopPropagation();
|
|
61844
62283
|
}
|
|
61845
62284
|
};
|
|
61846
|
-
const unsub = onKeypress4(r,
|
|
62285
|
+
const unsub = onKeypress4(r, onKey2);
|
|
61847
62286
|
const cleanup = () => {
|
|
61848
62287
|
unsub();
|
|
61849
62288
|
r.root.remove(box);
|
|
@@ -61875,7 +62314,7 @@ function pickProviderStep(otui, r, detected) {
|
|
|
61875
62314
|
});
|
|
61876
62315
|
box.add(provSelect);
|
|
61877
62316
|
provSelect.focus();
|
|
61878
|
-
const
|
|
62317
|
+
const onKey2 = (key) => {
|
|
61879
62318
|
if (key.name === "escape") {
|
|
61880
62319
|
cleanup();
|
|
61881
62320
|
resolve3(undefined);
|
|
@@ -61883,7 +62322,7 @@ function pickProviderStep(otui, r, detected) {
|
|
|
61883
62322
|
key.stopPropagation();
|
|
61884
62323
|
}
|
|
61885
62324
|
};
|
|
61886
|
-
const unsub = onKeypress4(r,
|
|
62325
|
+
const unsub = onKeypress4(r, onKey2);
|
|
61887
62326
|
const cleanup = () => {
|
|
61888
62327
|
unsub();
|
|
61889
62328
|
r.root.remove(box);
|
|
@@ -61985,7 +62424,7 @@ function pickModelInTui(otui, r, models) {
|
|
|
61985
62424
|
sel.options = matches2.length > 0 ? matches2.map((m) => ({ name: m, description: "" })) : [{ name: NO_MATCH, description: "" }];
|
|
61986
62425
|
filterLine.content = otui.t`${otui.dim(q.length > 0 ? `filter: ${filter} (${matches2.length}/${all.length})` : "type to filter \xB7 \u2191/\u2193 Enter \xB7 Esc to go back")}`;
|
|
61987
62426
|
};
|
|
61988
|
-
const
|
|
62427
|
+
const onKey2 = (key) => {
|
|
61989
62428
|
if (key.name === "escape") {
|
|
61990
62429
|
cleanup();
|
|
61991
62430
|
resolve3(undefined);
|
|
@@ -62008,7 +62447,7 @@ function pickModelInTui(otui, r, models) {
|
|
|
62008
62447
|
key.stopPropagation();
|
|
62009
62448
|
}
|
|
62010
62449
|
};
|
|
62011
|
-
const unsub = onKeypress4(r,
|
|
62450
|
+
const unsub = onKeypress4(r, onKey2);
|
|
62012
62451
|
const cleanup = () => {
|
|
62013
62452
|
unsub();
|
|
62014
62453
|
r.root.remove(box);
|
|
@@ -62077,7 +62516,7 @@ function pickSessionInTui(otui, r, sessions) {
|
|
|
62077
62516
|
sel.selectedIndex = 0;
|
|
62078
62517
|
};
|
|
62079
62518
|
apply();
|
|
62080
|
-
const
|
|
62519
|
+
const onKey2 = (key) => {
|
|
62081
62520
|
if (key.name === "escape") {
|
|
62082
62521
|
cleanup();
|
|
62083
62522
|
resolve3(undefined);
|
|
@@ -62100,7 +62539,7 @@ function pickSessionInTui(otui, r, sessions) {
|
|
|
62100
62539
|
key.stopPropagation();
|
|
62101
62540
|
}
|
|
62102
62541
|
};
|
|
62103
|
-
const unsub = onKeypress4(r,
|
|
62542
|
+
const unsub = onKeypress4(r, onKey2);
|
|
62104
62543
|
const cleanup = () => {
|
|
62105
62544
|
unsub();
|
|
62106
62545
|
r.root.remove(box);
|
|
@@ -62193,6 +62632,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
62193
62632
|
placeholder: "type a task or / for commands \xB7 Enter send \xB7 Shift+Enter newline",
|
|
62194
62633
|
commands: commandsForMode("agent"),
|
|
62195
62634
|
headerMeta: "\u21910 \u21930",
|
|
62635
|
+
permissionMode: () => permissionMode,
|
|
62196
62636
|
filterCommands: (query) => filterCommands(query, "agent"),
|
|
62197
62637
|
...opts.versionCheck !== undefined ? { versionCheck: opts.versionCheck } : {}
|
|
62198
62638
|
});
|
|
@@ -62220,6 +62660,12 @@ async function launchTuiAgentShell(opts) {
|
|
|
62220
62660
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-model-k", content: otui.t`${otui.dim("Model")}`, marginTop: 1 }));
|
|
62221
62661
|
const sbModelV = new otui.TextRenderable(r, { id: "sb-model-v", content: otui.t`${otui.dim(`${sel.provider}/${sel.model}`)}` });
|
|
62222
62662
|
sidebar.add(sbModelV);
|
|
62663
|
+
sidebar.add(new otui.TextRenderable(r, { id: "sb-usage-k", content: otui.t`${otui.dim("Usage")}`, marginTop: 1 }));
|
|
62664
|
+
const sbUsageV = new otui.TextRenderable(r, { id: "sb-usage-v", content: otui.t`${otui.dim("\u21910 \u21930")}` });
|
|
62665
|
+
sidebar.add(sbUsageV);
|
|
62666
|
+
const balancePanel = mountBalancePanel(sidebar, otui, r, {
|
|
62667
|
+
provider: sel.provider
|
|
62668
|
+
});
|
|
62223
62669
|
mountCwdPanel(otui, r, sidebar, opts.session?.cwd ?? process.cwd());
|
|
62224
62670
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-workspace-k", content: otui.t`${otui.dim("Workspace")}`, marginTop: 1 }));
|
|
62225
62671
|
const sbWorkspaceV = new otui.TextRenderable(r, {
|
|
@@ -62461,6 +62907,9 @@ async function launchTuiAgentShell(opts) {
|
|
|
62461
62907
|
},
|
|
62462
62908
|
onExactUsage: () => {
|
|
62463
62909
|
hasExactUsage = true;
|
|
62910
|
+
},
|
|
62911
|
+
setUsage: (input3, output2) => {
|
|
62912
|
+
sbUsageV.content = otui.t`${otui.dim(`\u2191${fmtTokens(input3)} \u2193${fmtTokens(output2)}`)}`;
|
|
62464
62913
|
}
|
|
62465
62914
|
});
|
|
62466
62915
|
let lastUsage;
|
|
@@ -63015,10 +63464,12 @@ Staying in the current session.
|
|
|
63015
63464
|
});
|
|
63016
63465
|
})();
|
|
63017
63466
|
};
|
|
63018
|
-
const showGame = () => {
|
|
63019
|
-
|
|
63467
|
+
const showGame = (line) => {
|
|
63468
|
+
const timeoutMatch = /\/game\s+(\d+)/.exec(line);
|
|
63469
|
+
openGamesModal(otui, chrome, {
|
|
63020
63470
|
renderer: r,
|
|
63021
|
-
...inspectorKeys
|
|
63471
|
+
...inspectorKeys,
|
|
63472
|
+
...timeoutMatch !== null ? { timeoutMs: Math.max(1, Number(timeoutMatch[1])) * 1000 } : {}
|
|
63022
63473
|
});
|
|
63023
63474
|
};
|
|
63024
63475
|
const showWorkspace = () => {
|
|
@@ -63683,7 +64134,7 @@ Staying in the current session.
|
|
|
63683
64134
|
return;
|
|
63684
64135
|
}
|
|
63685
64136
|
case "game": {
|
|
63686
|
-
showGame();
|
|
64137
|
+
showGame(line);
|
|
63687
64138
|
return;
|
|
63688
64139
|
}
|
|
63689
64140
|
case "deferred": {
|
|
@@ -63946,7 +64397,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
63946
64397
|
return;
|
|
63947
64398
|
}
|
|
63948
64399
|
if (command.name === "/game") {
|
|
63949
|
-
showGame();
|
|
64400
|
+
showGame(line);
|
|
63950
64401
|
return;
|
|
63951
64402
|
}
|
|
63952
64403
|
if (command.name === "/mode") {
|
|
@@ -64376,6 +64827,9 @@ async function mountChatShell(otui, renderer, opts) {
|
|
|
64376
64827
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-model-k", content: otui.t`${otui.dim("Model")}`, marginTop: 1 }));
|
|
64377
64828
|
const sbModel = new otui.TextRenderable(r, { id: "sb-model-v", content: otui.t`${otui.dim(label())}` });
|
|
64378
64829
|
sidebar.add(sbModel);
|
|
64830
|
+
mountBalancePanel(sidebar, otui, r, {
|
|
64831
|
+
provider: selection.provider
|
|
64832
|
+
});
|
|
64379
64833
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-ctx-k", content: otui.t`${otui.dim("Context")}`, marginTop: 1 }));
|
|
64380
64834
|
const sbContext = new otui.TextRenderable(r, { id: "sb-ctx-v", content: otui.t`${otui.dim("~0 tokens (est)")}` });
|
|
64381
64835
|
sidebar.add(sbContext);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.63",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|