Skip to content

v1.13.7

Choose a tag to compare

@github-actions github-actions released this 05 Oct 03:32
1ff51be

Changes

Left to their own devices, small models will happily play Qxh9. This PR gives any model a harness for chess, Connect Four and Othello where illegal moves can't happen, with dials for how much help it gets. It also lets scripts and other agents hand work to the companion's coding-agent queue without their own API keys, and keeps long agent runs from drowning in oversized tool results and tool schemas.

Highlights

  • Any model can play chess, Connect Four or Othello and only ever play legal moves: it picks from a menu, a bad answer is sent back with the reason, and a move it never gets right is played for it and counted.
  • A help level runs from "every legal move" up to "only the engine's best move", and concept switches (forks, pins, double threats, corners ...) decide which ideas a model is told about, so a bot can know forks but not skewers.
  • Connect Four and Othello come with built-in engines, so every level works with nothing to install; chess uses Stockfish for the engine levels.
  • Full games between models, engines and random players, and chess puzzle runs that measure whether teaching a concept helps a model.
  • prompture delegate "..." hands a task to Claude Code or Codex through the companion, which starts itself if needed; delegate-status, delegate-answer and delegate-resume follow up, and exit codes tell scripts how the run ended.
  • Agents keep large tool results instead of cutting them off: the model gets a preview and reads the rest on demand, and very large tool sets are searched and loaded as needed instead of all being sent every turn.
  • Deep agents now pick when to summarise from the model's context window, and the summary keeps user instructions, findings and the current step in fixed sections.
Technical changes

Game harnesses (prompture.games)

  • games/base.py: GameHarness offers candidate moves, puts their names in the reply schema as an enum (enforced natively by drivers with schema support, such as Ollama's format), validates replies through Game.parse_move, retries with the rejection reason, and falls back to the engine's best or a random move with MoveDecision.rescued=True. Usage is summed across attempts.
  • level (0-5) controls which moves are offered (all, tagged, blunders removed by a blunder_drop in winning chances, engine top-N unranked, top-N ranked with scores, best only); concepts filters which motif tags are attached. Engine levels use Game.default_engine() when no engine is passed.
  • A failed model call (timeout, backend down) is recorded as a failed attempt instead of raising, so a match survives a flaky backend and the rescue shows up in the report.
  • Game and Engine protocols, plus Motif, Score, Candidate, Attempt, MoveDecision and check_concepts, are the extension points for new games.
  • games/match.py: play_match(game, players, state=None, max_moves=400) returns a MatchResult with the winner, moves, per-side rescues and a finished flag; EnginePlayer and RandomPlayer implement the same choose(state) interface as a harness.
  • games/connect4.py: Connect4Board (flat cells, play/undo, winning_cells, immediate_wins), tags for win, block, threat, double-threat, safety and center, a prompt with a grid plus per-column stacks, and Connect4Engine, a centre-first alpha-beta search over open-line windows (default depth 6) with "wins in N" / "loses in N" labels.
  • games/othello.py: OthelloBoard with flips and forced passes (PASS), tags for corner, x-square, edge, mobility and safety (newly conceded corners), and OthelloEngine, alpha-beta on a positional weight table plus mobility (default depth 4).
  • games/chess/motifs.py: describe_move tags facts (capture, check, promotion, castling) and concepts (mate, back-rank-mate, fork, pin including capturing a pinned piece, skewer, discovered-attack, hanging-piece, safety), using one-move static checks; a piece lost on the spot cannot fork, pin or skewer, and pawn pins and pawn discoveries are not reported.
  • games/chess/harness.py: ChessGame (SAN names, SAN/UCI parsing, FEN plus piece lists in the prompt), ChessEngine wrapping a UCI engine path or SimpleEngine into Lichess winning-chance scores, and ChessHarness as a GameHarness subclass.
  • games/chess/puzzles.py: load_puzzles, play_puzzle and run_puzzles read Lichess-format packs (opponent's move first, any mate accepted on the last move) into a PuzzleReport with solve rate and rescues.
  • New chess extra (chess>=1.10), included in all; prompture.games.chess raises a clear install hint without it.

Task delegation (prompture/cli/delegate_cmd.py)

  • delegate, delegate-answer, delegate-resume and delegate-status drive the companion's automation queue over its loopback API with the bearer token from ~/.prompture/companion.json, through a stdlib urllib client.
  • The client refuses any companion URL that is not 127.0.0.1 or localhost, so the bearer token never leaves the machine, matching the hook client.
  • A missing companion is started detached (DETACHED_PROCESS on Windows, a new session elsewhere), then the command waits for its state file to answer.
  • Step logs stream to stdout by polling; exit codes are 0 finished, 1 failed, 3 waiting for an answer, 4 stopped or detached. --json, --no-wait, --timeout, --agent, --model and --cost-cap are supported.
  • Registered through _COMMAND_MODULES in cli.py; documented in the README.

Tool-context management (prompture/agents/tool_context.py)

  • ToolResultPolicy and shape_tool_results move results over per_call_tokens (default 4000), or the largest results of a round over batch_tokens (default 12000), into an ArtifactStore, leaving a head/tail preview and an artifact:// handle; read_tool_result(handle, query, offset) is registered only once something is stored. Error results are cut, never offloaded; offload=False restores plain truncation.
  • ToolLoader sends search_tools / load_tools instead of the full schemas when they would exceed ~8k tokens (defer_tools="auto"), keeps loaded tools for the rest of the conversation, and always sends preload_tools.
  • repair_tool_pairs gives every assistant tool call a result (a placeholder for interrupted calls) and drops orphaned results, so trimmed or interrupted histories are not rejected by providers.
  • Conversation and AsyncConversation gain this through ToolContextMixin; Agent and AsyncAgent accept tool_result_policy, defer_tools and preload_tools and pass them through.
  • ArtifactStore gains text(handle, scope=...), a scope-checked text read, and clear(scope=...).
  • create_deep_agent and its async twin default to summarize_at_tokens="auto", which resolves via resolve_summarize_threshold to the smallest of 80k, 75% of the model's context window, and the window minus the output reservation (from max_output_option or the model's limit), with a 2k floor and a fallback to 80k for unknown models.
  • SUMMARIZER_SYSTEM_PROMPT rewritten into fixed sections (task, user messages, findings, actions, errors, open items, current step) that keep exact values and artifact:// handles.

Details