@mrciphersmith/keryx 0.2.59 → 0.2.60

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.
Files changed (2) hide show
  1. package/dist/cli.js +86 -25
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -53454,7 +53454,7 @@ import { spawnSync as spawnSync2 } from "child_process";
53454
53454
  // package.json
53455
53455
  var package_default = {
53456
53456
  name: "@mrciphersmith/keryx",
53457
- version: "0.2.59",
53457
+ version: "0.2.60",
53458
53458
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
53459
53459
  private: false,
53460
53460
  publishConfig: {
@@ -57441,6 +57441,31 @@ function checkWinner(board) {
57441
57441
  }
57442
57442
  return null;
57443
57443
  }
57444
+ function freeCells(board) {
57445
+ return board.flatMap((cell, index) => cell === null ? [index] : []);
57446
+ }
57447
+ function bestLocalMove(board, mark) {
57448
+ const free = freeCells(board);
57449
+ if (free.length === 0) {
57450
+ return;
57451
+ }
57452
+ const opponent = mark === "X" ? "O" : "X";
57453
+ for (const candidate of [mark, opponent]) {
57454
+ for (const index of free) {
57455
+ const next = board.slice();
57456
+ next[index] = candidate;
57457
+ if (checkWinner(next)?.winner === candidate) {
57458
+ return index;
57459
+ }
57460
+ }
57461
+ }
57462
+ for (const index of [4, 0, 2, 6, 8]) {
57463
+ if (board[index] === null) {
57464
+ return index;
57465
+ }
57466
+ }
57467
+ return free[0];
57468
+ }
57444
57469
  function placeMark(board, index, mark) {
57445
57470
  if (index < 0 || index > 8 || board[index] !== null) {
57446
57471
  return;
@@ -57482,7 +57507,8 @@ function gameSystemPrompt() {
57482
57507
  "3 4 5",
57483
57508
  "6 7 8",
57484
57509
  "Reply with ONLY the index of the cell you choose, as a single digit 0-8.",
57485
- "Choose an empty cell. Prefer winning, then blocking, then center/corner."
57510
+ "Choose an empty cell. Prefer winning, then blocking, then center/corner.",
57511
+ "Answer immediately. No explanation, no working out \u2014 one character."
57486
57512
  ].join(`
57487
57513
  `);
57488
57514
  }
@@ -57500,7 +57526,7 @@ async function modelMove(board, opts = {}) {
57500
57526
  user: gameUserPrompt(board),
57501
57527
  ...opts.provider !== undefined ? { provider: opts.provider } : {},
57502
57528
  ...opts.model !== undefined ? { model: opts.model } : {},
57503
- maxOutputTokens: 16,
57529
+ maxOutputTokens: 256,
57504
57530
  requestId: "keryx-game",
57505
57531
  ...opts.providerFactory !== undefined ? { providerFactory: opts.providerFactory } : {},
57506
57532
  ...opts.env !== undefined ? { env: opts.env } : {}
@@ -57519,9 +57545,20 @@ var GAME_FOOTER = [
57519
57545
  { key: "r", label: "new game" },
57520
57546
  { key: "esc", label: "minimize" }
57521
57547
  ];
57522
- var GAME_CELL_WIDTH = 5;
57523
- var GAME_CELL_HEIGHT = 3;
57524
57548
  var GAME_CELL_GAP = 1;
57549
+ var GAME_BOARD_CHROME_X = 4;
57550
+ var GAME_CELL_SIZES = {
57551
+ large: { width: 9, height: 5 },
57552
+ small: { width: 5, height: 3 }
57553
+ };
57554
+ function gameBoardWidth(cellWidth) {
57555
+ return cellWidth * 3 + GAME_CELL_GAP * 2 + GAME_BOARD_CHROME_X;
57556
+ }
57557
+ function resolveCellSize(availableWidth) {
57558
+ const large = GAME_CELL_SIZES.large;
57559
+ return gameBoardWidth(large.width) <= availableWidth ? large : GAME_CELL_SIZES.small;
57560
+ }
57561
+ var GAME_MODEL_TIMEOUT_MS = 12000;
57525
57562
  function asOtui2(otui) {
57526
57563
  if (otui === undefined || otui === null) {
57527
57564
  return;
@@ -57548,7 +57585,7 @@ function statusText(state) {
57548
57585
  if (state.draw) {
57549
57586
  return "Draw! (r \u2014 new game)";
57550
57587
  }
57551
- return `Your turn \u2014 ${state.turn}`;
57588
+ return state.turn === "X" ? "Your turn \u2014 X" : "Agent's turn \u2014 O";
57552
57589
  }
57553
57590
  function presentGame(openModalFn, otui, chrome, options = {}) {
57554
57591
  const renderer = options.renderer;
@@ -57591,23 +57628,42 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
57591
57628
  modelBusy = true;
57592
57629
  notice = undefined;
57593
57630
  paint();
57594
- const result = await modelMove(currentGame.board, {
57595
- ...options.provider !== undefined ? { provider: options.provider } : {},
57596
- ...options.model !== undefined ? { model: options.model } : {},
57597
- ...options.providerFactory !== undefined ? { providerFactory: options.providerFactory } : {},
57598
- ...options.env !== undefined ? { env: options.env } : {}
57631
+ const timeoutMs = options.timeoutMs ?? GAME_MODEL_TIMEOUT_MS;
57632
+ let timer;
57633
+ const deadline = new Promise((resolve3) => {
57634
+ timer = setTimeout(() => resolve3("timeout"), timeoutMs);
57599
57635
  });
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
+ ]);
57645
+ if (timer !== undefined) {
57646
+ clearTimeout(timer);
57647
+ }
57600
57648
  modelBusy = false;
57601
- if (result.move !== undefined) {
57602
- const placed = placeMark(currentGame.board, result.move, "O");
57603
- if (placed !== undefined) {
57604
- currentGame = placed;
57605
- } else {
57606
- currentGame = { ...currentGame, turn: "X" };
57607
- }
57649
+ const timedOut = outcome === "timeout";
57650
+ const result = timedOut ? { move: undefined, error: undefined } : outcome;
57651
+ let move = result.move;
57652
+ let playedLocally = false;
57653
+ if (move === undefined && result.error === undefined) {
57654
+ move = bestLocalMove(currentGame.board, "O");
57655
+ playedLocally = move !== undefined;
57656
+ }
57657
+ const placed = move === undefined ? undefined : placeMark(currentGame.board, move, "O");
57658
+ currentGame = placed ?? { ...currentGame, turn: "X" };
57659
+ if (result.error !== undefined) {
57660
+ notice = `agent: ${result.error}`;
57661
+ } else if (timedOut && playedLocally) {
57662
+ notice = `agent timed out after ${Math.round(timeoutMs / 1000)}s \u2014 played a local move`;
57663
+ } else if (playedLocally) {
57664
+ notice = "agent gave no usable answer \u2014 played a local move";
57608
57665
  } else {
57609
- currentGame = { ...currentGame, turn: "X" };
57610
- notice = result.error === undefined ? undefined : `agent: ${result.error}`;
57666
+ notice = undefined;
57611
57667
  }
57612
57668
  paint();
57613
57669
  };
@@ -57645,7 +57701,11 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
57645
57701
  title: "/game",
57646
57702
  tabs: [{ id: "game", label: "Tic-tac-toe" }],
57647
57703
  footer: GAME_FOOTER,
57648
- renderTab: (_tabId, body) => {
57704
+ onArrowKeys: (_key, direction) => {
57705
+ moveCursor(0, direction === "left" ? -1 : 1);
57706
+ return true;
57707
+ },
57708
+ renderTab: (_tabId, body, ctx) => {
57649
57709
  if (body === undefined || body === null) {
57650
57710
  return;
57651
57711
  }
@@ -57655,6 +57715,7 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
57655
57715
  }
57656
57716
  const theme = getTheme();
57657
57717
  const r = renderer;
57718
+ const cellSize = resolveCellSize(ctx.width);
57658
57719
  const wrap2 = new core.BoxRenderable(r, {
57659
57720
  id: "game-wrap",
57660
57721
  width: "100%",
@@ -57693,8 +57754,8 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
57693
57754
  const index = row * 3 + col;
57694
57755
  const cellBox = new core.BoxRenderable(r, {
57695
57756
  id: `game-cell-${index}`,
57696
- width: GAME_CELL_WIDTH,
57697
- height: GAME_CELL_HEIGHT,
57757
+ width: cellSize.width,
57758
+ height: cellSize.height,
57698
57759
  flexShrink: 0,
57699
57760
  flexGrow: 0,
57700
57761
  flexDirection: "row",
@@ -57750,11 +57811,11 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
57750
57811
  moveCursor(1, 0);
57751
57812
  return;
57752
57813
  }
57753
- if (token === "left" || token === "h") {
57814
+ if (token === "h") {
57754
57815
  moveCursor(0, -1);
57755
57816
  return;
57756
57817
  }
57757
- if (token === "right" || token === "l") {
57818
+ if (token === "l") {
57758
57819
  moveCursor(0, 1);
57759
57820
  return;
57760
57821
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.2.59",
3
+ "version": "0.2.60",
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": {