Skip to content

g023 Code 1.3.3

Choose a tag to compare

@g023 g023 released this 21 Aug 22:21
· 1 commit to main since this release
b2af2d1

g023 Code — Release 1.3.3

Version: 1.3.3
Date: 2026-08-21
Supersedes: 1.3.2
Artifacts: g023-code-1.3.3.tar.gz, g023-code-1.3.3.zip, unpacked tree in g023-code-1.3.3/

Pure-Python DeepSeek V4 powered coding agent. Subagent-First architecture,
terminal-native UI built on rich + prompt_toolkit.

1.3.3

The theme of this release is money: knowing what the API costs before,
while, and after you spend it. DeepSeek moved to tidal (peak/off-peak) pricing
on 2026-08-17, and 1.3.3 makes that visible and steerable; it also adds a
second, third and fourth way to see images; it makes every edit recoverable;
and it lets settings live in two scopes instead of one. 147 tests, all passing.

Surge pricing — /surge

DeepSeek bills peak and off-peak separately: peak hours are 09:00–12:00 and
14:00–18:00 Beijing time (UTC+8)
, and off-peak is exactly half of peak.
g023 now knows those windows and does three things with them:

  • It shows them on your clock. ds_times.json anchors the windows to
    Beijing wall-clock time; the CLI converts them to the machine's local
    timezone and prints every time in both AM/PM and 24-hour form.
  • It prices with them. /cost and the status bar split regular vs surge
    (2×) rates per model, straight from ds_pricing.json — so the number you
    see during a peak hour is the peak number, not a stale off-peak estimate.
    The status bar shows SURGE 2× while a peak window is active.
  • It can pause for them. /surge on stops sending requests from
    surge_pause_buffer_minutes (default 5) before the first peak of the day
    until that long after the last one ends, then resumes automatically; the CLI
    waits out the window instead of reading input, and Ctrl-C bypasses it for
    that pass. /surge notify on|off (default on) banners the transitions.

/surge status also counts down — minutes to the next surge and how long it
will last — and shows the pause window it would use. surge.py is pure local
arithmetic: no API call, no network. The price sheet lives in the two data
files (ds_pricing.json, ds_times.json), read from the install folder or
the project root, so updating a price is editing JSON, not code.

OpenRouter vision — /or

Vision was previously Ollama-only. There is now a three-backend split,
selected with /vision backend none|ollama|deepseek|openrouter:

  • openrouter — the new openrouter_client.py talks to OpenRouter using
    the OK.dat key. Models come from ./OR_VISION.json (project folder) in
    priority order; when one returns HTTP 429 the next on the list is tried.
    Default model google/gemma-4-26b-a4b-it:free. Free :free models are
    capped at 50 requests/24h for accounts under $10 of lifetime credits, and
    up to 1000/24h after that — /or tier free|credits tells g023 which
    ceiling you are on, and /or shows today's usage against it.
  • deepseek — DeepSeek's own experimental multimodal model,
    deepseek-v4-flash-vision-exp, billed on the same K.dat key at exactly
    the token rates of deepseek-v4-flash. The wire format is selectable with
    /vision endpoint responses|chat_completions (default: responses, the
    program's native format).
  • ollama — unchanged: local or remote daemon, models filtered to the
    ones the daemon reports as vision-capable.

subagents/vision.py + subagents/router.py own the dispatch: AnalyzeImage
is routed to the active backend, and answers are cached by image hash +
question + backend + model in the SQLite cache. While vision is disabled the
tool is not even offered to the orchestrator.

WriteFile backups — a bad write is one copy away

WriteFile replaces the entire file, and a write reconstructed from a summary
can silently truncate it. Every overwrite now first snapshots the previous
version to .g023/backups/<relative-path-to-file>/<timestamp>-<sha256-prefix>.bak,
mirroring the file tree; only the newest few versions per file are retained
(default 5). The write result line names the backup that was taken. Skipped
for files inside .g023/ itself, for brand-new files, and when the content is
unchanged. Disable with G023_WRITE_BACKUP=0, tune retention with
G023_WRITE_BACKUP_KEEP (minimum 1). Covered by tests/test_write_backup.py.

Config scopes — /config

Settings live in two files now: the machine-level config.json next to
K.dat (applies to every project) and, when present, the project's own
.g023/config.json (overrides the machine file for that project only).

/config               show both files and which scope is active
/config global        promote the current settings to the global default
/config save project  pin the current settings to this project
/config save          save to the active scope
/config clear         drop this project's override; global applies again

/settings marks the persistent keys; test_config_scopes.py holds the
scope precedence to account.

Usage & call accounting

usage.py was rewritten around per-model arithmetic: cache-hit, cache-miss
and output tokens are priced separately (they are priced an order of magnitude
apart), the rates are surge-aware, and when the API omits the hit/miss split
the arithmetic takes the worst-case assumption and says so. The API-call table
in the README — which reads cost 0 calls, which cost 1 — is now asserted by
tests/test_call_accounting.py against a stub client, so the honest numbers
are checked rather than remembered. tests/test_usage_accounting.py covers
the cost arithmetic and the command/handler contract.

Tool registry & schemas split

tools/registry.py now owns permission checks and dispatch to local executors
or subagents; tools/schemas.py owns the wire schemas — including the entire
web-search integration, which is still the one line {"type": "web_search"}
marker appended to the tool list, executed server-side by DeepSeek.
subagents/searcher.py formalises the local, metadata-first grep (pure local
Python — zero API calls).

Documentation & media

The docs were split one page per part (api.md, cache.md, cli.md,
commands.md, config.md, data_files.md, ollama_client.md,
openrouter_client.md, surge.md, tests.md, usage.md, plus the existing
pages) with an index in docs/README.md and a keep-alive map in
READ_1ST_CHANGES.txt.

The screenshot set was re-shot on 1.3.3 — nineteen frames covering the banner,
the command panels, a full read-only agent turn, a file edit with the new
backup line, and the 1.3.3 panels (/signals, /surge, /or, /config).
The captures use sanitised paths (/tmp/g023-shoot/...), so no frame exposes
a real home directory, user name, or API key.

What changed in 1.3.2 (since 1.3.1)

  • /goal is no longer bounded by the step cap — it sets
    max_iterations to UNLIMITED_ITERATIONS for the duration of its turn
    (alongside the max reasoning effort it already forced), restoring the
    previous value in the same finally that restores effort and /thinking.
  • The default step cap is 255, up from 100 — where /steps default
    restores and /help steps quotes; the shipped config.json carries it.

What changed in 1.3.0 / 1.3.1 (since 1.2.0)

  • A configurable per-turn step cap (/steps [n|off|default]), with a
    progress line every 10 steps and Ctrl-C as the escape hatch.
  • An output budget dial (/max-tokens [n|off|default]).
  • Symbol-to-line-range file reading: every Python read returns
    symbols: {name -> [start_line, end_line]} from the AST walk, decorators
    included, so escalation to verbatim source is one targeted ReadFile with
    exact line numbers — zero API calls.
  • Drift signals (/signals, alias /drift): prefix cache hit-rate
    compared against its own history, unrecognised response item types, and
    unexplained empty responses — the three cheapest observations that would
    move first, stored per day in the SQLite cache so the baseline survives
    restarts.
  • An honesty pass removing unverified claims from the docs.

What changed in 1.2.0 (still current)

  • Moved entirely to the DeepSeek Responses API (POST /responses), which
    is what makes DeepSeek's native web search a first-class tool of the
    loop rather than a provider bolted on the side — no client executor, no key
    to supply, no /websearch command.
  • Response items are echoed back byte-identical (message, reasoning,
    web_search_call, function_call/function_call_output pairs), and
    compaction/truncation/error recovery repair pairing before the next request
    — a function_call with no matching output is a hard 400 in either
    direction.
  • Subagent-first delegation with an SQLite cache: repeated reads of unchanged
    bytes are local lookups.

Install

Requirements: Python 3.11+ and a DeepSeek API key.

Drop the folder anywhere and run the installer inside it:

cd /path/to/g023-code
./installer.sh          # Windows: installer.bat

It finds a Python 3.11+, builds a .venv, installs only missing
dependencies, asks once for the API key, writes a default config.json, and
puts g023 on your PATH. Re-running it is safe and repairs a half-finished
setup. Manual install is pip install -r requirements.txt, the key on the
first line of K.dat, then g023.sh (or g023.bat) from a project folder.

Optional extras: pillow (vision preprocessing), curl_cffi + h2 +
brotli + zstandard (browser-grade fetching); prompt_toolkit is strongly
recommended for tab completion, history and the live status bar but the input
line degrades to a plain prompt without it.

Contents

g023_code/            the package
  api.py              DeepSeek Responses API client
  cli.py              the CLI class, main loop, slash handlers
  commands.py         the command catalogue as data
  config.py           paths, settings, scopes, pricing data
  ollama_client.py    Ollama daemon client
  openrouter_client.py  OpenRouter client (vision)
  orchestrator.py     the agent loop
  prompt.py           input line and pickers
  signals.py          drift signals
  surge.py            peak/off-peak pricing arithmetic
  ui.py               theme, glyphs, gauges, traces
  usage.py            token/cost accounting
  web_fetch.py        FetchUrl
  subagents/          file_reader, searcher, vision, router
  tools/              registry (permissions) + schemas (wire shapes)
tests/                pytest suite (147 tests, no network)
docs/                 one page per part, indexed by docs/README.md
media/                screenshots (sanitised) + capture tooling
installer.sh/.bat     installers
g023.sh/.bat          launchers
ds_pricing.json       DeepSeek price sheet (regular + surge)
ds_times.json         DeepSeek peak windows
K.dat.example         key file template (create K.dat from it)
config.json           default configuration

What's included

Source, tests, documentation, installers and the screenshot set. Not
included: any API key (K.dat, OK.dat are never shipped), the local
OR_VISION.json, cache/history files, or the media videos.

Requirements

rich, httpx, tiktoken, pygments, prompt_toolkit (recommended),
pillow (vision), curl_cffi/h2/brotli/zstandard (fetching) — see
requirements.txt.

Notes

Costs shown are computed locally from the token counts the API returns and
the price table in ds_pricing.json / SPEC.md §3.2 — an estimate of your
bill, not a reading of it. The prices are surge-aware as of the sheet's
effective date; if DeepSeek changes its rate card, update the JSON.

Verify the archives with SHA256SUMS in this folder.