Gajae Code

Documentation

What’s new in v0.16.4

Published 2026-09-05. This page is generated from the immutable release changelog.

Added

  • Added selectable astra-lite, astra-default, and astra-heavy CODEX presets with ASTRA- labels, plus astra-fable and astra-fable-opus cross-family review combinations. Existing presets and automatic provider recommendations remain unchanged.
  • An interactive gjc now shows up inside Paseo by itself, controllable from the desktop and mobile apps, instead of only being reachable when Paseo started it. Every terminal session already registered its SDK endpoint with the broker, and GJC's ACP surface already resolves a live index entry straight to the running host — a session/load attaches to it rather than resuming a second copy — so the only thing missing was telling the Paseo daemon the session exists. That is now done at interactive startup with paseo import --provider <key> --cwd <cwd> <session-id>, which makes the terminal session and the Paseo agent the same live session rather than two transcripts. The announcement is gated so it costs nothing when it cannot apply: an absent $PASEO_HOME/config.json (default ~/.paseo/config.json), no enabled GJC ACP provider entry, no paseo on PATH, a daemon that is not listening, or a session the broker does not yet report as live each stop it before anything is spawned. The daemon check is a plain socket probe rather than a CLI invocation because the Paseo CLI blocks indefinitely when its daemon is down (measured: no output and no exit after two minutes), and the liveness check exists because importing a not-yet-registered id would make session/load fork a second host for a session that is already running. While the daemon is merely not up yet, the probe retries every 30 seconds for approximately ten minutes on unref'd timers, so launching Paseo after the terminal still works; the loaded opt-in can cancel its own pending or active announcement, changes made by another process apply on the next launch, and Paseo's own duplicate-import refusal is read as success. Interactive-only by construction — a gjc acp provider process never builds the interactive mode, so an imported session cannot announce itself back. Create-only by construction too: GJC adds an entry and never deletes, archives, or rewrites one, so it can never remove an agent you still wanted. The consequence is yours to manage — every interactive start gets a fresh session id (including gjc -c), so each launch adds one row, and ending a session leaves its row listed as idle until a send fails with SDK session attachment is stale and Paseo marks it error; prune with paseo delete <agent-id>. Opt-in: the new operator-owned paseo.autoImport setting is off by default, so a repository cannot enable it and an installation that never asks for it never reads a Paseo file, probes a socket, or spawns the CLI; enable it with gjc config set paseo.autoImport true (Settings → Interaction → "Announce Sessions to Paseo"). See docs/terminal-app-integrations.md.
  • Corrected a stale claim in docs/external-control-readiness.md: interactive gjc sessions are broker-registered and are listed by ACP clients, and an ACP session/load against a live entry attaches to the running host instead of resuming a copy.
  • An externally submitted prompt against a session on the SDK-only host runtime no longer hangs forever after its turn has already finished. That runtime published the lifecycle boundary as a bare { type, sessionId } frame: it computed each ended invocation's commandId/turnId for durable reconciliation and then discarded them at publication, and it never carried a normalized outcome at all — only the agent_failed diagnostic was published per invocation with its correlation. An ACP client matches a terminal against the correlation it was acknowledged under and requires the outcome, so a real agent_end was rejected as acp_prompt_terminal_dropped / incomplete_correlation, session/prompt never settled, and the client sat in working until the 30-minute prompt deadline (reproduced against Paseo 0.6.1 attached to a live interactive session: the terminal rendered the full answer while Paseo showed the agent running indefinitely). An owned agent_end is now published once per ended invocation carrying that invocation's correlation and a normalized stopped outcome, mirroring the existing agent_failed shape. Frames that terminalize nothing are unchanged and stay uncorrelated: agent_start, a host-originated run with no owning invocation (autonomous continuation, cron, monitor), and a continuing maintenance checkpoint (pruned/compacted/promoted) — which is published but is deliberately not a terminal boundary, so stamping it would settle a client's prompt mid-turn. The ACP ownership gate is untouched.
  • An ACP client attached to a live interactive session now receives the turn it submitted: assistant text, thinking, and tool calls stream as they happen instead of arriving as an empty completed turn. The SDK-only host runtime subscribed to lifecycle and watchdog events only — its two tool_execution_* handlers just renewed a deadline watchdog and it never subscribed to message_update/message_end at all — so it published no agent wire events and an attached client could drive the session and watch the turn settle without receiving a single word of the answer (measured against Paseo 0.6.1: paseo logs showed the prompt and nothing else, with --filter tools and --filter text both reporting no activity). It now streams the same five events the notifications runtime does (message_update, message_end, tool_execution_start/update/end), built with the same shared toAgentWireEventPayload producer so the wire shape cannot drift from what ACP maps. Delivery matches the notifications runtime too: one frame per owning invocation, sent directly to the connection that submitted the turn and carrying that invocation's correlation. It deliberately does not go through the session event ring — that ring is bounded by frame count with no byte ceiling, so streaming high-frequency chunks through it would evict the lifecycle events clients depend on (including the agent_end that settles their prompt) and would broadcast a turn's content to every authenticated subscriber rather than the one that asked for it. A turn nobody submitted over the SDK (an ordinary terminal prompt, an autonomous continuation, cron, monitor) has no owning connection and streams nothing.
  • gjc no longer reports an error at every interactive launch when the Paseo daemon requires a password. paseo import refuses with Cannot connect to daemon ...: Password required, which the socket probe cannot anticipate because the TCP connect succeeds; that refusal is now read as a quiet skip instead of a failed announcement. GJC has no credential setting and reads no Paseo credential file: paseo import runs with the session's own environment, so an exported PASEO_PASSWORD is used and an absent one simply skips.
  • Status-line settings now include a draft-local spatial custom editor with exact-slot keyboard reordering, hidden-segment palette management, command text configuration, persisted Confirm/Exit semantics, live multi-row preview compatibility, narrow/Unicode rendering, and deterministic visual evidence.
  • Added opt-in reasoningLanguage: english guidance for technical work while preserving the user's requested response language; the default remains off.

Changed

  • Interactive assistant text_delta presentation now coalesces eligible updates into the latest message projection before the existing TUI frame while preserving ordered event processing, immediate non-text updates, and authoritative final output. Session/transcript replacement and disposal cancel stale presentation; temporary TUI stop/start rearms current live text before the first restarted frame without invalidating live historical image callbacks.

Fixed

  • Credential replacement lookups now propagate the owning request's cancellation through scoped and allowed unscoped retries, preventing aborted turns from continuing OAuth preparation or selecting another key.
  • Removing the committed status-line command segment or clearing its trusted command now cancels the running process and clears stale output; draft-only previews remain side-effect-free.
  • Slack inbound acknowledgment reactions now use bounded, abortable, tracked provider work that drains on shutdown or attachment retirement without delaying accepted turns; rejected or timed-out reactions emit sanitized diagnostics.
  • Task repository bindings now accept explicit relativeSubdir: "." as the already-bound worktree root, avoiding unnecessary launch retries while retaining traversal, absolute-path, symlink-escape, and sibling-repository rejection.
  • Blank optional task output schemas now inherit the configured agent/session schema instead of failing successful subagents at completion; malformed nonempty schemas and required-field validation remain fail-closed.
  • The read-URL cache is now bounded (FIFO, 64 keys), so long-lived TUI and SDK-host processes no longer retain every fetched page's full output and image payload for the process lifetime; an evicted entry simply refetches on the next read, and unpersisted sessions sharing a working directory remain isolated by session identity.
  • The insane-search web bridge now kills the engine's whole process group instead of only the python3 process. killChild promised a group kill but the engine was not spawned detached, so when the bridge timeout fired mid-flight (25s default vs the engine's internal 90s playwright budget), node and headless chromium children survived as orphans. The engine now leads its own POSIX process group and receives group-wide SIGTERM→SIGKILL escalation even when the group leader exits during the grace period, with a direct child-kill fallback on Windows.
  • Contribution-prep outbound artifacts now redact complete AWS credentials, including AKIA/ASIA access-key IDs and SecretAccessKey/SessionToken values in shell-style and escaped JSON forms, while preserving structured output and existing GitHub, Slack, authorization-header, and cookie redaction.
  • Model preset registry URL and disable overrides now resolve only from explicitly inherited or user-owned environment sources, so a checkout .env cannot suppress updates or redirect signed-registry polling while dependency injection, HTTPS validation, signatures, ETags, cache, and offline behavior remain unchanged (#5300).
  • OpenCode Go's provider conversation identity now remains distinct from generic cache affinity across normal turns and maintenance calls, so the required session header reuses the session manager's opaque lifecycle without accepting auth-gateway prompt-derived cache keys (#5295).
  • Perplexity web search now clamps upstream search breadth to the API's 3–100 contract, retries one structurally empty completion, rejects malformed or ungrounded success payloads through the provider fallback path, and preserves valid grounded responses.
  • Architect/task subagents now retry API-key lookup without the parent session id after a scoped miss, so broker OAuth that works for gjc -p --no-session is not thrown away as Agent run failed / 0 tokens (#5081 follow-up). Failures still throw provider_unavailable without leaking the token.
  • Python work that completes after strict session disposal now returns its result without attempting to append to the closed session transcript.
  • SDK-hosted workflow tools now retain the logical session id for ask, skill, deep-interview, and ultragoal state paths while job, monitor, bash, cron, task, and subagent ownership routes through a separate opaque async endpoint key. Explicit provider session identities no longer create encoded duplicate workflow trees or redirect endpoint-owned work to another live session.
  • Explicit root startup --thinking off and --thinking=off now override a configured higher startup default while leaving the provider Effort catalog unchanged.
  • Skills installed from marketplace plugins are now enumerated into sessions instead of only resolving by exact name. loadSkills asked the native provider alone, so every installed plugin skill was absent from / autocomplete and from the skill tool's Available: list while gjc skill list reported it and the discovery fallback still loaded it — 46 installed skills presented as 5. The already-registered marketplace provider is now loaded alongside native, keeping <plugin>:<skill> namespacing and per-item scope trust.
  • File-content search now has a call-wide timeout (timeout, default 5 seconds, range 0.5–60) that is forwarded through internal-URL resolution and native grep together with cancellation. Native scans poll cancellation around each bounded file, and the tool joins cancellation cleanup before returning instead of leaving scope resolution, archive scratch, or grep workers running after a timeout. Timeout failures tell callers to narrow the scope or explicitly increase the budget, while user cancellation remains a distinct tool abort.
  • restart:sdk-broker -- --close-session-hosts now continues replacing the broker when its pre-restart session-host listing fails, reports the partial teardown with exit code 1, tolerates another authenticated restart caller retiring the same exact broker identity, and catches other top-level failures instead of emitting an unhandled-rejection crash record.
  • install:dev now builds the workspace native addon before linking the source CLI, preventing fresh or cleaned development checkouts from failing startup with a missing or stale addon. The installer also supports the local --dev binary shortcut.
  • Plain and explicit-model launches now refresh a missing provider's cached dynamic catalog before resolving a qualified startup selector, so configured models such as glm-zcode/glm-5.3 no longer open in no-model state while preserving cache-only startup for already available models.
  • The explicit thinking-choice gate for xAI models now derives from the parsed grok generation (4.5+) shared with @gajae-code/ai, instead of the exact ids grok-4.5/grok-4.6. A future grok release failed the id list, so supportsReasoningEffort stayed false and the user's thinking level was silently dropped from requests; reseller-hosted grok routes (OpenRouter & co.) keep their existing audited-transport behavior.
  • The status-line usage segment now paints only the active model's provider, so switching to Grok no longer keeps a stale Claude 5h/7d pair. Canonical Grok Build reports render only their authoritative weekly window, while monthly credits are omitted instead of being mislabeled as 7d.
  • Bundled plugin MCP launchers now execute Node .mjs entrypoints on Linux from startup-authenticated interpreter bytes and a complete copied-file snapshot. Runtime options cannot precede the entrypoint, path-qualified/shebang commands fail closed, unexpected post-install files quarantine the bundle, and canonical registry identity plus the complete copied-file manifest are rebound before every spawn. Runtime captures the interpreter and authenticated bundle files through bounded stable handles into a unique owner-only launch capsule; the Node loader returns authenticated captured module bytes while preserving file-URL relative imports and blocking package/CommonJS/native escapes. Concurrent connections use separate authority. Capsule cleanup failures retain ownership and fence reconnect until cleanup succeeds. Bun, macOS, and Windows bundled stdio launch fail closed pending equivalent loader and owner/process-tree authority; capsules do not provide hostile-same-UID process isolation.
  • Moving the /model preset cursor now reuses the authentication snapshot produced by catalog and credential refreshes instead of resolving every profile against the full model catalog on each Up/Down keypress; auth-dependent preset actions remain blocked until the initial static refresh and the availability snapshot are complete.
  • Bundled Grok Build models now explicitly declare reasoning_effort support only for catalog-supported models, so grok-build/grok-4.6:xhigh reaches the Responses request while unsupported Grok models remain fail-closed. (#5264)
  • Mid-run OpenAI remote-compaction fallback failures now stop before another provider request, surface the local summarization error through ACP/SDK terminal handling, and leave the uncompacted context unsubmitted. Successful local fallback still commits the compaction and resumes normally.
  • Opening /model now renders the preset landing before constructing the full model browser catalog. Canonical search indexing, sorting, and the redundant offline registry refresh are deferred until the user searches or chooses a browse action, while the lightweight preset-availability check remains immediate and scoped or direct-search selectors still load immediately.
  • Slack lean notifications now publish only finalized assistant answers and actionable states instead of the internal lifecycle, identity, and activity frame stream. Final answer delivery uses the message reference as a durable publication identity, so repeated unpositioned SDK frames no longer post duplicate replies; accepted inbound Slack messages receive a 👀 reaction while GJC awaits the agent's answer.
  • /model provider-tab refreshes now reuse the already loaded static catalog and update only the selected provider's discovery state, avoiding repeated signed preset registry work while retaining full-catalog refresh behavior for static configuration changes.
  • Added the command status-line segment for user-produced HUD content. It runs configured user/global shell commands in the background with scheduled cached refreshes, bounded timeout/output, ANSI sanitization, placeholder degradation, disposal cancellation, and custom-editor configuration while preserving synchronous TUI rendering; project-scoped settings cannot trigger execution.
  • Business/Enterprise/Team ChatGPT accounts can bind GPT-5.6 Sol again. The Codex entitlement preflight treated only plan_type values containing "pro" as eligible, so an openai-codex OAuth account on a Business/Enterprise/Team plan was rejected at model-binding/subagent setup time with a spurious entitlement error even though the backend accepts the request. Exact Pro/Business/Enterprise/Team tiers are now recognized as entitled, Plus/Free remain rejected, and unfamiliar plan names are deferred to the provider rather than guessed locally.

Upgrade

Download the matching standalone binary from the latest release, replace the existing executable, then verify:

gjc --version && gjc --smoke-test

Download the latest standalone binary. See the full v0.16.4 release on GitHub.