An inference-aware personal AI agent runtime for delegated tasks across applications, devices, and compute environments. The Rust harness is its execution foundation; coding is its first implemented capability. The CLI is the primary development and validation client. Desktop implementation is paused; GPUI is the selected future direction pending Phase 11 investigation and design. See the current checkpoint and engineering roadmap. Independent background execution and connected-device capabilities remain future work.
Development planning: phase review and delivery process, Phase 1 review, and Phase 2 tickets.
Phase 3 implements context accounting, selection, and compaction.
Use astrid run "inspect this repository" --model <model-slug> --show-context
to see stable context item IDs/sources and each prepared request's serialized
bytes by source, retention/removal reasons, and incomplete summary text/lineage.
Provider token counts remain unavailable; the displayed non-opaque JSON size
heuristic is not a full context total or guaranteed provider fit.
The CLI protects instructions, your task, and the latest complete tool exchange.
Older exchanges are selected with file-reference/lexical priority and recency,
then omitted history can become one bounded deterministic summary. Defaults:
65,536 estimated context units, 4,096 response-reserve units, 524,288 serialized
request bytes, and 4,096 summary text bytes. Change them with --context-tokens,
--response-reserve, --context-bytes, and --summary-bytes; compare policies
with --context-policy recency or --context-policy file-references.
If protected context exceeds the configured allowance, the run stops before the next model invocation. The response reserve is planning headroom, not an enforced provider output limit. Original session history remains retained; request selection does not bound all runtime memory; interactive idle snapshots persist session state separately.
For hands-on checks, see Try Phase 2.
An experimental Rust agent harness. Phase 2 provides a typed execution runtime
with ordered events, cancellation, bounded tool output, configurable permissions,
and workspace change evidence. Interactive conversations retain multiple runs;
astrid run executes one fresh repository task.
It streams OpenAI output and executes native tools sequentially.
macOS is the supported platform.
astrid batch run <tasks.json> --folder <folder> --concurrency 2 executes a bounded
caller-authored DAG with fresh owned sessions, read-only grants and explicit
visible-result dependencies. The final report preserves original results separately
from unverified synthesis. Ctrl-C joins owned work; failures skip descendants while
unrelated work continues. --json returns the complete bounded bundle/lifecycle.
astrid batch trace [id] separately inspects optional metadata-only batch traces.
See manifest examples, limits and API and the
implementation review. There is no concurrent mutation,
shell, worktree, durable job, daemon or new idle-session format.
cargo build
cargo install --path .
astrid login
astrid models
astrid run "find and fix the failing test" --model <model-slug>astrid login prints a Continue with ChatGPT link. Open it in your browser
on the same Mac and authorize Astrid's access to your ChatGPT plan. The callback
listener expires after five minutes. Astrid validates the returned identity and
plan permissions before saving credentials.
Host identifiers use persisted UUIDv4 URIs (urn:uuid:...). If an initial
development attempt saved the rejected astrid-... format, the next login
repairs it automatically before registration. Registered host identities are
never silently replaced. Generate a fresh link with astrid login; old links
belong to an expired or stopped callback listener.
Authentication follows the documented open-source Sign in with ChatGPT flow.
Astrid owns its registration and credentials; it never reads Codex credentials.
Credentials and a stable host identifier live under ~/.config/astrid/, with
owner-only permissions. Refreshes are serialized across processes and rotating
credentials are saved atomically. Interactive conversations are saved separately in a private workspace-scoped session store.
This release implements subscription authentication; API-key authentication is
not a prerequisite and is not automatically selected as a fallback.
astrid models queries the selected account's subscription model catalog.
The model must be supplied with --model or ASTRID_MODEL; there is no default
model in the architecture. Account entitlement requires successful inference,
not merely a model appearing in a catalog. ChatGPT plan limits still apply.
Phase 6 compares two existing context policies across five scripted-provider/real-tool tasks and keeps separate live notes quality evidence. See the plan and review.
ASTRID_EVAL_REPORT=/tmp/astrid-evaluation.json cargo test --locked --test evaluation -- --nocaptureBare astrid uses the rich composer when supported and plain line input otherwise.
In plain mode, enter one line at a time and type a listed choice exactly; /quit
exits. Piped stdin still uses the explicit astrid run interface.
For one explicit task, --reasoning-effort low requests a validated provider effort.
Omission keeps the provider default; unavailable/default effort is not reported as
an observed setting. Currently explicit efforts are verified for exact
gpt-5.6-sol and gpt-5.6-terra identifiers: none, low, medium, high,
xhigh, max. Unknown identifiers or unsupported efforts fail before dispatch.
Account/model access remains subject to provider errors.
astrid run "Inspect the sources and explain the result" --model gpt-5.6-sol \
--routing tests/fixtures/evaluation/routing.json --observability onMulti-stage routing works in fresh runs and resumable interactive sessions.
At the interactive prompt use /routing path/to/routing.json to configure
subsequent runs, or /routing off to disable stages. The policy is not saved;
a session's visible-history safety mode is saved and remains active across model
changes, routing-off and restart. Same-source native sessions retain private
continuation. Snapshot v2 records dispatch provider/model/effort per response;
legacy v1 history has unknown provenance and follows visible-history replay.
See ADR 0016 for migration limits.
The JSON policy sets initial effort and ordered stages, each with from_call,
model, caller-supplied class and optional effort. The example switches to
Terra at model call 2 after the preceding tool batch finishes. A final response
ends the run; unused stages do not generate extra calls. Classes label intended
work, not measured model competence. Paths are relative to the current directory;
notes tasks can use --notes --folder as usual.
Multi-stage requests rebuild visible conversation/tool exchanges without private
reasoning; the original private response data remains in the in-memory result.
Each core route event records model/effort, reason, provider and replay mode.
astrid trace <run-id> attaches route metadata to per-call timing, usage and
terminal outcome. Effective values describe the validated request; server-resolved
identity/default effort and subscription cost remain unavailable. Completion does
not grade task quality. See Phase 7 plan and
ADR 0015 for the compatibility boundary.
This is direct Responses inference, with Astrid executing its own tools. It does
not launch Codex or delegate to another coding-agent harness.
Execution status and the idle prompt show a compact meter by default, for example:
ctx est 57% ~271000/475000 budget
This example is an estimated local input admission budget, not measured model
window occupancy. The denominator is configured context allowance minus response
reserve (61,440 by default: 65,536 minus 4,096). The numerator is the existing
non-opaque JSON bytes/4 heuristic. Opaque continuation marks the estimate partial;
missing accounting/budget is unavailable, never zero. Model-window occupancy stays
unknown. With observability enabled, the normal CLI prints provider-reported
input/output/cached usage per call and known run totals with reporting-call coverage.
Missing/invalid counts stay unavailable; reported zero stays zero. Cached tokens
are a subset of input, and cumulative run usage is not current context occupancy.
astrid trace retains the same usage for later inspection.
During a multi-tool response, Astrid checks the mandatory accumulated exchange
before another tool executes. If actual results exceed local admission, pending
tools are skipped and the run fails explicitly; completed results and edits stay
intact. No replacement tool outcomes are invented. An incomplete batch still
requires /new. The crossing result is retained, so this guard is not a promise
that every result or session fits in memory.
The active meter updates from context selection/preflight, including compaction.
The idle prompt says last because it describes the last prepared request in the
selected session, not unsent text or all retained history. Plain output includes
one compact context line per prepared call; --show-context adds the detailed
breakdown. Command notices occupy their own footer row so the meter cannot hide them.
astrid run "Make a checklist of outstanding household actions" \
--notes --folder /path/to/notes --model <model-slug>The selected directory can be outside Git. --folder requires --notes and
otherwise notes mode uses the invocation directory. Before inference, the CLI
shows the canonical folder, provider/model and effective custom policy:
read Allow, write Deny, execute Deny. Notes mode overrides --mode and all
per-capability flags; note text cannot elevate authority. Retrieved notes go to
the selected provider. Reads retain existing folder/path validation; trusted
runtime evidence subprocesses and provider network access retain their existing
boundaries.
The model is guided to cite source filenames, exclude completed actions and preserve uncertainty. Streamed tool activity and the terminal coverage report show observed ranges, truncation and missing-source errors separately from the checklist. A completed run does not independently prove a correct or exhaustive checklist. Ctrl-C cancels the run; provider failures and cancellation exit nonzero. This is a fresh one-shot workflow; interactive coding modes, directory-bound sessions and their idle snapshots retain existing behavior.
M01 evidence separates scripted runtime tests from live factual-quality evaluation. To repeat the live synthetic baseline using existing auth (no new infrastructure):
cargo build --locked
python3 scripts/evaluate_m01.py --model <model-slug> --output /tmp/m01-live.jsonReview the output against the frozen rubric before assigning a factual-quality pass; the script records completion and metrics separately and leaves grading pending.
| Setting | CLI | Environment | Default |
|---|---|---|---|
| Model | --model |
ASTRID_MODEL |
Required |
| Model-call ceiling | --max-model-calls |
ASTRID_MAX_MODEL_CALLS |
20 |
| Shell timeout, seconds | --shell-timeout |
ASTRID_SHELL_TIMEOUT |
30 |
| Permission preset | --mode |
— | auto |
| Native read policy | --read-policy |
— | allow |
| Native write policy | --write-policy |
— | allow |
| Shell policy | --shell-policy |
— | ask |
The invocation directory is the workspace root. Astrid reads only that
directory's AGENTS.md as initial repository instructions; parent and nested
instruction discovery is deferred.
On an interactive terminal, astrid run shows Astrid's existing pixel logo,
compact model/provider/workspace metadata, and a scrolling execution stream.
Completed output appends to normal terminal scrollback; a small Ratatui inline
region shows unfinished streamed text, status, and permission input. Astrid does
not enter an alternate screen. Bare astrid opens a model/task prompt;
astrid run remains available for one-shot invocation. Bare astrid returns to
the prompt after each run, retaining the selected conversation. Ctrl-C cancels an
active run; at the idle prompt it exits.
Tool requests show their target, results show a concise summary, and shell
stdout/stderr stream separately while the command runs. Effective permission
policy, initial branch/dirty paths, native mutation patches, and final observed
workspace changes are visible in the execution stream. Completion means
the run ended normally, not that the task was independently verified.
Replies are cyan, approvals yellow, successful completion green, and failures
red. Labels and secondary information use dim terminal text. Set NO_COLOR=1
to disable colors. The former fixed header/footer viewport remains opt-in with
ASTRID_FIXED_VIEWPORT=1. The default inline renderer keeps the logo and startup
metadata in scrollback, without pinning a header above the execution stream.
When either output stream is redirected, or TERM=dumb/terminal sizing is
unavailable, rendering falls back to ordinary append-only text without cursor
controls or colors. Assistant text continues to stream to stdout; concise tool
activity, command output, and failures go to stderr. Operation approvals use
/dev/tty, including the complete command and workspace, independently of
redirected output. Context source/selection details are shown with --show-context.
Every successful response without tool calls ends the task. Recoverable tool failures return to the model. In one-shot mode, provider/protocol errors and an exhausted model-call ceiling exit nonzero. Interactive mode displays the outcome and returns to input. Astrid does not automatically retry mutations or provider requests. Its final answer is a model claim, not proof of success.
/new starts a fresh conversation. /sessions opens a searchable list showing
session ID, first-task label, active marker, run count, and latest outcome; use
arrow keys and Enter to switch. Up to 32 sessions live in the current Astrid
process and disappear on exit. Follow-ups retain committed messages and context;
they do not rerun old tools. An interrupted, incomplete tool batch is preserved
but cannot accept follow-ups; use /new in that case.
/mode opens the permission selector. Direct commands are:
| Command | Reads | Workspace edits | Shell |
|---|---|---|---|
/mode ask |
allow | ask | ask |
/mode auto (default) |
allow | allow | ask |
/mode unbound |
allow | allow | allow |
The mode is visible in startup metadata, composer, and execution status. It
applies to subsequent runs and remains unchanged when switching sessions.
unbound grants full tool access, including broad account-level shell execution.
Native path validation, shell timeouts, cancellation, and output limits remain.
Its logo arches are red (#F94447, #EC1A1D, #F94447) and eyes yellow
(#F7C600); NO_COLOR disables the palette.
One-shot runs accept --mode ask|auto|unbound. Explicit --read-policy,
--write-policy, or --shell-policy overrides the corresponding preset action;
the display uses custom when the effective policy matches no named mode.
Modes are not remembered after exit or read from repository settings.
| Tool | Arguments | Behavior |
|---|---|---|
read_file |
path, optional start_line, end_line |
Bounded UTF-8 content and numbered lines; inclusive range |
write_file |
path, content, overwrite |
Atomic creation or explicit overwrite; parent must exist |
edit_file |
path, old_text, new_text |
Exact replacement only when there is one match |
list_directory |
path |
Sorted immediate entries |
glob |
pattern |
Sorted workspace-relative file matches |
grep |
path, pattern, optional filters/context/page controls |
Bounded Rust regex results with filenames and numbered context |
shell |
command |
Policy-controlled noninteractive /bin/sh -c execution |
File tools automatically operate within the workspace. Traversal, outside
paths, symlink paths, and mutations to hard-linked files are denied. Search
skips .git, symlinks, and binary files for textual grep. Existing file
permissions survive edits. Empty or ambiguous edit targets cause no mutation.
Prefer targeted inspection over whole-file reads. For example, the model can call
read_file with {"path":"src/runtime.rs","start_line":600,"end_line":660}.
Line numbers are 1-based and both ends are inclusive. Omitted/null start_line
means 1; omitted/null end_line means EOF. Results preserve raw content and
add lines: [{"line":600,"text":"..."}]. An EOF before the requested start
returns an empty range with range_start_reached=false, not a tool error.
coverage.complete describes delivery of the requested range, not the whole file.
Check truncated, coverage.truncation_reason (output_limit or scan_limit),
and last_line_partial before treating an excerpt as a complete line.
Reads scan at most 16 MiB plus one EOF-detection byte, with cancellation between
fixed-size chunks. Only the requested range is retained: at most 32 KiB of raw
content and 1,024 lines, reduced further to keep the entire JSON result within
64 KiB. A distant range therefore avoids retaining its prefix but still requires
bounded prefix IO. Unscanned bytes and total omitted file size are unknown;
observed_bytes/omitted_observed_bytes count only observed requested-range data.
Line text removes LF/CRLF endings; content retains them. A long line may end at
a valid UTF-8 boundary, explicitly marked partial.
grep supports include_glob and exclude_glob on workspace-relative paths
(e.g. **/*.rs); filters are applied before bounded candidate selection.
before_context and after_context each accept 0–20 lines (default 0).
Each match retains path, line, and text, adding numbered before/after
arrays when context is requested. Each context array has a 4 KiB serialized budget;
oversized surrounding lines are omitted and context_truncated reports this.
Match text is not clipped: an indivisible match exceeding the page byte budget
is omitted with coverage.oversized_matches and incomplete search coverage.
Pages use offset (default 0) and limit (default 100, maximum 1,024), with a
32 KiB serialized match budget. For example:
{"path":"src","pattern":"ModelCall","include_glob":"**/*.rs","before_context":2,"after_context":2,"offset":0,"limit":20}Follow page.next_offset using identical query/filter/context arguments. Ordering
is workspace-relative path then line; each call rescans the bounded candidates.
Offsets count matching lines eligible for inspection, including records omitted
because their serialized size is too large. Pagination is stable only while files
are unchanged; it is not a saved filesystem snapshot. page.stop_reason distinguishes
match_limit from output_limit. A null next offset means no additional page is
known within the bounded scan; it does not prove complete repository coverage.
Always inspect coverage.complete and its omission/scan counters. Existing 1 MiB
per-file, 16 MiB aggregate, 16 KiB match-line, depth, and inventory limits still apply.
The final page does not require an extra empty call merely to confirm completion.
Omitted or null optional arguments use defaults; provider strict schemas expose
these controls as required nullable fields.
Permission policies are allow, ask, or deny for native reads, native
writes, and shell execution. Defaults allow validated workspace file operations
and ask for each shell call. Policy decisions and reasons are runtime events.
The CLI asks for an explicit yes through /dev/tty; missing input/EOF denies.
Repository files cannot configure or elevate authority. Policies govern tool
dispatch; runtime instruction loading and bounded evidence collection still read
workspace data. For example:
astrid run "inspect failing tests" --model <model-slug> --write-policy deny --shell-policy ask--shell-policy allow explicitly grants broad execution with your account's
permissions, including network and destructive commands. The general shell has
no separately enforceable network/destructive policy. It starts fresh in the
workspace with null stdin; cwd is not a sandbox. This release targets trusted
local repositories and assumes no hostile concurrent filesystem changes.
Shell reads are 8 KiB chunks, queued output is at most eight chunks (64 KiB), and capture/live admission are limited to a 64 KiB prefix per stream. Once live admission overflows, later bytes are drained and omitted, with explicit counters. Already accepted public events remain lossless. Timeout and cancellation stop and reap the process group independently of a stalled event consumer; final event delivery/run return can still wait for the receiver to drain or disconnect. Detached descendants remain outside the process-group contract. Partial output survives timeout/cancellation; unknown exit codes and unread bytes are not invented.
Output metadata distinguishes observed/captured/live-queued bytes, omissions, pipe completeness, and an incomplete UTF-8 suffix unavailable for text rendering. Raw streamed bytes preserve per-stream order. Invalid complete UTF-8 is rendered with replacement characters; terminal controls are sanitized. Streamed prefixes are not printed again at completion. Captured bytes that were not streamed remain in the structured result; the CLI reports live omissions.
Native inspection results are limited to 64 KiB serialized JSON. Inventories use bounded sorted selection (up to 1,024 candidate records, with a 32 KiB returned record budget). Grep scans up to 1 MiB per file/16 MiB total and skips oversized 16 KiB lines with explicit incomplete coverage. Large/deep inventories and nonrepresentable names cannot silently establish an empty search. Mutation inputs, repository instructions, model output, and total conversation/state memory remain separate from Phase 3's selected-request budget.
Git observation reports branch/HEAD and staged, unstaged, and untracked paths. It disables optional index writes, fsmonitor, configured clean/process filters, and submodule inspection. Filter suppression can conservatively change status classification relative to a normal Git invocation. Git operations have time and output limits; unavailable/incomplete metadata stays explicit. Non-Git workspaces remain usable.
Change evidence compares the starting working tree with the ending workspace, preserving existing staged/unstaged edits. Content baselines cover tracked and non-ignored untracked files, capped at 1 MiB per file and 16 MiB total; bounded path-presence scans include ignored names so ignore-rule changes cannot fabricate creation/deletion. Inventories stop at 10,000 entries or their scan deadline; a large ignored tree can make existence coverage incomplete. Native write/edit calls also retain bounded direct evidence, including ignored files. Patches are whole-file replacements capped at 64 KiB; final serialized reports are capped at 1 MiB including Git metadata. Coverage changes, binary content, oversized data, and collection failures are explicit. No rollback, staging, or commits occur.
Native mutation evidence is correlated to its tool under the stated concurrency assumption. Shell/external changes are observations during the run; causation is not guaranteed. Final evidence is attempted even for cancelled/failed runs without rewriting committed tool outcomes. Full event streams are not saved as session logs. Interactive idle snapshots persist original conversation history separately; request selection does not compact that retained history.
cargo test --locked
cargo fmt --check
cargo clippy --locked --all-targets -- -D warningsTests use a local HTTP/SSE server with deliberately fragmented events. They exercise the real Responses adapter and agent loop without network model APIs. They cover completion barriers, interrupted responses, tool errors, edit ambiguity, workspace escapes, shell rejection/timeout, exact call ceilings, instruction loading, reasoning-item replay, and OAuth identity validation. Phase 2 tests cover flood limits, early-output handshakes, cleanup with a stalled event consumer, the permission matrix, dirty Git baselines, ignore-rule changes, filter/fsmonitor execution prevention, and committed-edit evidence after cancellation. Runtime tests also cover correlated lifecycle reconstruction, cancellation races, cleanup failure, headless operation, UI failure after a committed edit, and cancellation during backpressured streaming. CLI input tests exercise nonblocking reads and cancellation without a blocked reader thread.
The deterministic acceptance test copies tests/fixtures/greeting into a
temporary directory. It runs the failing tests, searches/reads the code, edits
an existing file, creates GREETING.txt, runs tests again, checks the passing
result, and finishes. Model responses are scripted; native tools, file
mutations, and shell commands are real.
For live acceptance, copy the fixture to a disposable directory and invoke the installed binary there:
fixture_dir=$(mktemp -d)
cp -R tests/fixtures/greeting/. "$fixture_dir/"
cd "$fixture_dir"
astrid run "Inspect this repository, run its failing tests, fix the greeting bug without changing tests, create the required GREETING.txt, then run tests again and report their result." --model <model-slug>Review and confirm each shell command. Both tests must pass, name.trim()
must appear in src/lib.rs, and GREETING.txt must contain Hello, Astrid!
followed by a newline. No live inference result is implied by mocked tests.
runtime::run accepts a model provider, tool executor, permission handler,
RunConfig (including PermissionPolicy), Cancellation handle, and an optional bounded Tokio event sender.
Drain an attached event receiver concurrently with execution. Pass None for
headless operation. Dropping a receiver detaches event delivery; it does not
cancel the run. The CLI explicitly requests cancellation if rendering fails.
Keep polling the runtime until its terminal RunResult, including after calling
cancel; dropping its future is not the cancellation/cleanup API.
Each accepted run owns typed execution state and a fresh Session. Turns contain one model invocation and its resulting tool batch. Astrid IDs identify runs, sessions, turns, model calls, and tool calls; provider tool IDs remain protocol metadata. Event sequence numbers establish ordering within a run.
Assistant text deltas are provisional. Only validated model completion commits
assistant output. Interrupted text remains visible with a ! or × response-interrupted indicator.
All validated tool requests are announced before sequential execution. Permission
waiting is distinct from tool execution. Every accepted run and requested tool
receives exactly one terminal outcome, including denied, cancelled, timed-out,
and skipped operations. A completed shell call may have a nonzero command exit.
Normal run completion does not independently establish task success.
Events are owned and serializable, but are not persisted. They contain visible text, raw tool-output bytes, requests, permission decisions, mutation evidence, and workspace reports, excluding credentials and opaque provider continuation. The runtime validates and updates state before emitting events; it does not execute by replaying events. While an event receiver remains attached, backpressure can delay progress and cancellation notification. Already-applied events remain pending across model cancellation until delivered or the receiver closes, preserving sequence ordering.
See architecture, the Phase 0 contract, and the frozen Phase 1 runtime contract, with the Phase 2 amendments.
Run astrid without arguments to open the designed terminal welcome screen:
blue pixel logo with cyan eyes, model/provider/workspace metadata, and a task
input directly beneath it. Astrid reuses your last-selected model; ASTRID_MODEL can override it.
On first use with no saved choice, it selects the first model from your account
catalog. Type / in the input box for commands, then /model to see available
models. Use ↑/↓ and Enter to select; type to filter and Esc to cancel.
The slash-command menu also supports ↑/↓ and Enter. All popups replace the
same input area; Esc returns to task entry without creating another box.
Enter sends a task; Ctrl-J inserts a newline. The composer supports multiline
paste, wrapping, cursor movement, and Unicode editing. Ctrl-C cancels; empty
Ctrl-D exits (or cancels model selection). Tab completes a slash command.
Input is capped at 64 KiB with a visible notice when a paste exceeds that limit.
During a run, approvals require typing yes and pressing Enter after the full
command is displayed. Pasted text and pre-prompt typeahead cannot grant an
inline approval. Redirected output retains canonical /dev/tty approval input.
Saved authentication is reused. Bare astrid saves workspace conversations
for follow-ups across launches; astrid run starts one fresh repository task.
The default inline display inserts completed rows above its live region, keeping
the transcript in normal scrollback. Use your terminal's scrollbar, mouse wheel, trackpad, or
scrollback keyboard shortcuts to review earlier output. Replies are cyan,
approvals and cancellations yellow, successful completion green, and failures
red. Set NO_COLOR=1 for plain text. Redirected output stays plain.
The previous fixed header/footer viewport is available with
ASTRID_FIXED_VIEWPORT=1; its scroll region can prevent normal transcript
scrollback, so it is no longer the default.
astrid settings
astrid settings set observability on
astrid run "inspect the failing test" --model <model>
astrid run "task" --model <model> --observability off
astrid trace
astrid trace <run-id>
astrid stats
astrid settings set observability offObservability defaults to off. A run flag overrides the persistent user setting; settings changes affect subsequent runs. Both one-shot and interactive runs use the same runtime recorder. Disabling recording preserves execution events, permissions, cancellation and context budgets, and retains existing trace files. Invalid settings fail explicitly, including when a run flag is present.
Live usage uses the same opt-in: astrid settings set observability on or
astrid run "task" --observability on. With telemetry off or no final provider
usage, the CLI reports unavailable counts. An interrupted call can retain usage
already reported by the provider; that does not imply successful completion.
Settings live in ~/.config/astrid/settings.json; traces in
~/.config/astrid/traces/, with private file/directory permissions. Traces contain
correlated execution metadata, operation timings, numeric context accounting and
provider-reported usage where available. They omit task/response text, file paths,
tool arguments/results, summaries and private provider continuation. They cannot
resume sessions or replay the full conversation.
trace <run-id> prints a JSON summary. Timing names are explicit: inclusive
runtime call duration, adapter preparation/authentication/provider attempt, first
visible text, delivery waiting, and tool execution. Permission waiting is separate.
These are overlapping intervals; do not sum them into total elapsed time. True
first-token latency, prefill/decode rates and subscription monetary cost remain
unavailable. Missing usage is null; reported zero is zero. stats reports known
sums with reporting-call coverage; cached tokens are a subset of input tokens.
Unrecorded runs are unknown, and incomplete/unreadable traces are counted separately.
The recorder bounds its queue (256 records), each record (16 KiB), each run
(8 MiB), and the store (256 MiB, at most 4,096 directory entries including reserved
publication slots). It stops recording at limits or writer failure, reports
incomplete/unavailable recording independently of the run outcome, and never
silently deletes old traces. Finalization waits at most 250 ms by default. Partial
files remain inspectable; a complete trace has a terminal event, valid sequence,
footer and published .jsonl filename. See Phase 4 evidence
and accepted ADR 0008 /
ADR 0009.
Tool result previews can be enabled with /settings or
/settings expanded-tool-calls on (use off to collapse them). The preference
persists across launches. From the shell, use
astrid settings set expanded-tool-calls on|off. Expanded previews show up to
12 lines; shell output and permission/error information remain visible in either
mode. The input starts at two lines and grows with wrapping or Ctrl-J newlines.
Type @doc in the composer to look up matching repository files; use ↑/↓ to
select and Enter or Tab to insert a repository-relative @path. Lookup runs off
the terminal thread and adds a path reference to the message, without reading or
attaching file contents. Ignore rules apply first; ignored/hidden files appear
only when no ordinary files match. Dependency/build directories remain excluded.
Library callers can use astrid::file_lookup::find_files(repo_root, base, pattern, max_hits). It accepts repository-relative bases and glob patterns, returns sorted,
unique paths with / separators, and reports validation/traversal failures.
Interactive context limits can be set before launch with ASTRID_CONTEXT_TOKENS,
ASTRID_RESPONSE_RESERVE, and ASTRID_CONTEXT_BYTES; the same environment
variables supply defaults for the equivalent astrid run flags. Defaults remain
65,536 estimated tokens, a 4,096 response reserve, and 524,288 serialized bytes.
These are local admission limits, not a claim about the model's context window.
Protected overflow reports its measured heuristic, byte limits, and category
sizes. Instructions, the original/current tasks, and the latest complete exchange
cannot be silently discarded to make a request fit.
/sessions restores conversations and active selection when Astrid is reopened in
the same repository. Session names come from the first submitted message. Snapshots
live under ~/.config/astrid/sessions/<workspace-hash>/state.json, with private
file/directory permissions, atomic replacement, a 32 MiB workspace ceiling, and
an 8 MiB per-session ceiling. They include conversation/tool data and provider
continuation, independently of observability, but no authentication state or saved
permission grants. One interactive process can write each workspace store at a
time. Save failures remain visible and do not change a run's outcome. Recovery
uses the last saved idle state; no interrupted tool execution is replayed.
Sessions that were already lost before persistence was introduced are unavailable.
Snapshot v2 adds known response dispatch origins and a durable replay mode. V1 snapshots load without a write; a subsequent successful atomic idle save publishes v2. Earlier binaries reject v2. Private original messages remain stored, but legacy, routed or model/provider-changing history is submitted through a visible-only view; incomplete terminal tool batches remain non-continuable. Validation and other pre-publication failures retain the old snapshot. A post-publication directory-sync failure reports uncertain durability, following the existing persistence contract.
To reproduce the controlled synthetic routing comparison using existing auth:
cargo build --locked
python3 scripts/compare_routing.py --output /tmp/routing-comparison.jsonThe frozen matrix runs two counterbalanced repetitions for notes and coding under fixed Sol/low versus Sol-to-Terra/low. Notes need separate factual rubric review; coding artifacts are independently tested. Recorded eight-episode evidence shows both configurations passed this small sample, with no general quality or speed advantage established. Cost remains unavailable for subscription inference.