sagent-ai
v2.2.34
Published
SAgent AI auto installer and launcher for Windows and Linux
Maintainers
Readme
SAgent AI
SAgent AI is a compact DSL coding agent based on OpenAI Codex.
The core idea is simple: keep the Codex foundation, but replace the model-visible tool surface with one compact raw DSL tool: dsl.
Release 2.2.32 hardens the refreshed Codex integration with Unicode-safe Windows background scripts, protocol-matched sandbox resources, semantic-index recovery, and more complete release checks while preserving direct dsl access.
Instead of many JSON tool calls, the model writes a readable script of actions:
RG --summary "GameEngine" src
READ --outline src/game/engine.ts
READ --def createSnapshot src/game/engine.ts
USES createSnapshot src --summary
REPLACE src/game/engine.ts --def `export function createSnapshot() {
return { ok: true }
}
`
RUN `npm test
`No JSON escaping. No JSON-escaped multi-line patches. The conversation history stays readable.
Install
npm install -g sagent-ai
sagentsagent-ai is a small auto installer/launcher package.
It installs and runs the native package for the current platform:
Windows x64 -> sagent-ai-win
Linux x64 -> sagent-ai-linuxDirect platform packages:
npm install -g sagent-ai-win
npm install -g sagent-ai-linuxRequirements:
- Windows x64 or Linux x64;
- Node.js 22.6+ and npm for installation and
EXEC/JSscripting; - CodeGraph support for
CGis installed as the optional npm dependency@colbymchenry/codegraphunder thesagent-aipackage; SAgent falls back to a globalcodegraphbinary if needed. SEMsemantic search uses Ollama withqwen3-embedding:4bby default and can be configured to use OpenAI embeddings instead.
Use
Start in the current project:
sagentStart in a specific project:
sagent -C C:\path\to\projectRun a non-interactive task:
sagent exec "inspect the project, fix the failing test, and run the checks"Run JavaScript automation inside a DSL batch with EXEC:
EXEC --timeout 30 `const outline = await dsl.READ("--outline src/app.ts");
const next = await dsl.ask(outline + "\nWhich definition should I inspect next?");
dsl.PRINT(next);
`Inside EXEC, every regular DSL command is available as an async dsl.* helper with the same arguments and bodies as the line-command form. Only nested dsl.EXEC / dsl.JS are blocked.
EXEC also has direct Node.js access to the current project, network, and child processes. Node and every child inherit the same OS sandbox. Import packages installed in the project normally:
const ts = await import("typescript");
const { writeFile } = await import("node:fs/promises");
await writeFile(".agent-dsl/tmp/typescript-version.txt", ts.version);Node resolves packages from the project node_modules; npm cache, prefix, home, and temp directories stay under .agent-dsl. EXEC clears the inherited host environment and exposes a small synthetic environment. Add a comma-separated [node.exec] env = "HTTP_PROXY,..." allowlist when specific host variables are required. If the platform sandbox runner is missing, EXEC fails closed instead of launching unsandboxed.
Reusable EXEC functions can live in .agent-dsl/exec/function/<name>.ts as plain async body code. SAgent wraps and caches them under .agent-dsl/exec/function/.compiled.
EXEC pick-ranges --limit 3 `raw search output...
`Inside the function body, use dsl, dan, args, and argText directly:
const answer = await dsl.ask(`Choose ranges from:\n${dan}`);
await dsl.PRINT(answer);The same function can be called from regular EXEC code:
const ranges = await dsl.function(`pick-ranges --limit 3
${searchOutput}`);Use dsl.try.function(...) when a missing or failing helper should return { ok: false, error } instead of throwing.
Configurable ASK / GEN profiles
DSL summarization and generation helpers can use named profiles from:
- CLI overrides such as
-c agent_dsl.llm.ask_profile="sonnet"; - project
.agent-dsl/config.toml; - global
~/.codex/.agent-dsl/config.toml; - built-in defaults.
Example:
[llm]
ask_profile = "default"
gen_profile = "default"
fallback_profile = "gpt55-low"
[llm.profiles.sonnet]
model_provider = "claude"
model = "sonnet"
max_input_tokens = 100000
max_output_tokens = 4000
[node.exec]
env = "HTTP_PROXY,HTTPS_PROXY,NO_PROXY,NODE_EXTRA_CA_CERTS"Built-in profiles are:
default—openai/gpt-5.3-codex-spark, reasoninglow;gpt55-low—openai/gpt-5.5, reasoninglow;gpt55-xhigh—openai/gpt-5.5, reasoningxhigh.
model_provider = "claude" uses the local claude CLI with tools disabled. Use profiles with --ask-profile, --gen-profile, ASK --profile name, or await dsl.ask(prompt, ["YES", "NO"], "gpt55-xhigh"). Unknown profiles fall back to default; unavailable profiles retry fallback_profile when configured, then default.
For noisy searches or summaries, add {{ranges}} to an --ask / --ask-full / ASK capture prompt when you want the ASK model to choose exact source ranges. SAgent removes the marker before asking, then expands returned links into real numbered snippets:
RG "handleSubmit" src --ask "{{ranges}} Pick the 1-3 ranges I should read next."Single-file commands may return >>START-END; ASK capture and multi-file answers should return >>path:START-END. Duplicate or overlapping ranges are merged and capped.
Skills can be loaded on demand without adding the full Codex skills list to the system prompt:
SKILL --list
SKILL --list --paths
SKILL --find ast
SKILL ast-grep-cli
SKILL ast-grep-cli --ask "Give only commands useful for this task."For one-off expert ASK prompts, inject a skill only into that ASK request:
RG "defineComponent" src --ask-profile gpt55-xhigh --ask "{{SKILL:ast-grep-cli}} Pick the safest ast-grep command."{{SKILL:name}} works in global --ask / --ask-full, ASK --begin / ASK --end, normal ASK, and EXEC dsl.ask(...).
Plain SKILL --list and SKILL --find show names, scopes, and descriptions without noisy paths; add --paths when you need exact locations. If a {{SKILL:name}} marker cannot be resolved, SAgent removes it from the ASK prompt and prefixes the visible answer with ASK_WITHOUT_SKILL ....
Reusable helper libraries can live in .agent-dsl/exec/lib and be imported from both regular EXEC scripts and named EXEC functions:
const { readRanges } = await dsl.lib("kit.ts");dsl.lib("kit") defaults to kit.ts; paths are restricted to .agent-dsl/exec/lib.
Bundled skill packages can also provide EXEC libraries and functions:
const kit = await dsl.lib("skill:exec-kit/kit.ts");
await dsl.function("skill:some-skill/function/check\ninput");skill:name/path.ts resolves through the normal skill resolver and materializes the selected file under .agent-dsl/exec/.skill-cache before Node imports it. SKILL name remains read-only and never installs or runs code by itself.
SAgent loads:
system.mdfrom the same directory assagent.exe;- global and project
AGENTS.mdinstructions.
The packaged SAgent build disables MCP startup to avoid unrelated MCP startup failures and reduce noise.
Version
Current npm package version: 2.2.32.
Highlights in this release:
- Unicode and emoji-safe Windows background
SH 0/RUN 0scripts; - resilient Windows background startup when a running PowerShell process temporarily locks its log file;
- Windows sandbox helper binaries rebuilt and packaged with the same protocol version as SAgent;
- automatic rebuilding after a corrupt semantic manifest;
- release checks for semantic sidecars and sandbox resources;
- WSL-backed Linux package version and artifact-freshness checks on Windows release hosts;
- hosted image-result compatibility with both added and completed response items;
- a hard whole-command deadline for standalone, captured, global, and generated ASK requests, preventing retry/fallback multiplication from looking like a hung
dsl; --timeout Nnow limits the complete ASK operation rather than every individual provider attempt;- cancellable async Claude CLI execution with child cleanup when an ASK times out;
- synchronization with 157 upstream Codex commits, including GPT-5.6 metadata, persisted turn items, HTTP/proxy improvements, Windows sandbox fixes, and dependency security updates;
- direct
dslexposure for new models even when their metadata requestscode_mode_only; - restored compact TUI command, ASK, and ERR icons through the canonical command lifecycle;
- native hybrid semantic search sidecars for Windows and Linux.
See CHANGELOG.md for release history and PLANNED_CHANGES.md for planned improvements.
Why SAgent is different
Raw DSL instead of JSON
dsl is a raw DSL, not JSON. The model writes commands directly:
READ src/app.ts
PATCH `*** Begin Patch
*** Update File: src/app.ts
@@
-console.log("old")
+console.log("new")
*** End Patch
`This is easier for coding tasks because patches, regexes, shell scripts, and multi-line code do not need JSON string escaping.
Fewer tool calls
One batch can combine related work:
FILES src --limit 80
RG --summary "TODO|FIXME" src
READ --outline src/main.ts
RUN 120 `npm test
`SAgent is designed to encourage this workflow:
- search;
- read;
- edit;
- run tests;
- inspect logs;
- continue from failure.
Prefer one comprehensive dsl per step instead of many tiny calls. The --ask and --gen modes use a fast, cheap single-shot model and are intended to reduce raw context and boilerplate while keeping work batched.
Recovery with RESUME
If a batch fails in the middle, completed commands are saved as done and the failed command plus the remaining commands stay in .agent-dsl/tasks/<N>.dsl.
The model can resume that saved task from anywhere in a later batch. Commands before and after RESUME still belong to the current batch:
NOTE before
RESUME 12 `old broken command text` `fixed command text`
NOTE afterOr skip a no-longer-needed failed command:
RESUME 12 --skip
RESUME 12 --skip 2Successful work is not repeated.
If a resumed task fails, the current batch stops at RESUME. Use RESUME? ... when the resumed failure should be reported as a warning and the current batch should continue.
Passive feedback
SAgent appends useful passive blocks to later dsl results:
CURRENT_PLAN
2/5: Implement parser
UNREAD_LOGS
7 out=12 err=1
8 out=4
NOTIFICATIONS
- BG 7 exited:0 npm run devThis helps the model track plans, background logs, and finished processes without extra status calls.
ASK summarization
SAgent can call a small single-shot model from the DSL.
ASK `Answer only OK if this README explains installation clearly.
`Most commands also support a global --ask "prompt" post-processing argument:
RG "GameEngine" src --ask "Choose the 3 most relevant files to read next"
RUN 120 --ask "If tests failed, return only the failing file, error, and likely cause. If OK, answer OK." `npm test
`The command runs normally, but the visible result is replaced by the ASK model's concise answer. This is useful for reducing large search, test, log, and web outputs. If the command fails, ASK receives the failure text and the command still fails unless the directive is optional with ?.
For edit commands, --ask sends the compact edit summary plus diff hunks to the ASK model. Use --ask-full "prompt" when the ASK model also needs post-edit file contents. Full-file context is hidden from the main model and is not added to the read journal. All fast-model requests (--ask, --ask-full, ASK, dsl.ask, and --gen) are capped to roughly a 100K-token prompt budget.
To keep history compact, visible command headers omit long --ask / --ask-full prompts and --gen, replacing them with short ASK, ASK_FULL, and GEN markers.
For multi-command summarization, use an ASK capture block:
ASK --begin
RG "GameEngine" src
READ --outline src/game/engine.ts
ASK --end `Summarize the captured output and choose the next 3 files to inspect.
`Commands between ASK --begin and ASK --end run normally, but their visible output is captured and replaced by the single ASK answer. If a captured command fails before ASK --end, SAgent aborts the capture, prints the captured output, and the batch fails normally.
Every ASK has one wall-clock timeout budget shared by all retries and fallback profiles. --timeout N sets that whole-command budget for a standalone ASK; without an override, SAgent uses the largest timeout among the resolved profiles. A slow provider can no longer multiply its timeout by every retry and fallback.
Cheap context compression patterns
Use --ask on one noisy command when you only need a decision, ranking, or summary:
RG "handleSubmit" src --ask "Pick the 3 files most likely to need edits."
RUN 120 --ask "If failed: file, error, likely cause. If OK: answer OK." `npm test
`Use ASK --begin / ASK --end to compress several command outputs into one answer:
ASK --begin
READ --outline src/a.ts
READ --outline src/b.ts
READ --outline src/c.ts
ASK --end `Choose the 1-2 files to inspect next and explain briefly.
`Use two-level compression for broad searches: each command summarizes itself with --ask, then the final capture prompt chooses the next action:
ASK --begin
READ --outline src/a.ts --ask "Does this file contain auth logic? Briefly."
READ --outline src/b.ts --ask "Does this file contain auth logic? Briefly."
READ --outline src/c.ts --ask "Does this file contain auth logic? Briefly."
ASK --end `Choose the 1-2 files most likely relevant to the auth bug.
`Command reference
Every command starts on a new line.
Append ? directly to a command name to make failure a warning and continue the batch.
Use backticks for short payloads and double a literal backtick inside them; backslashes remain literal. For multiline or backtick-heavy payloads, use an exact heredoc terminator:
CREATE tmp/change-note.txt <<TEXT
Тест переведён с фиксированной задержки на ожидание состояния ready.
TEXT
REPLACE file.txt <<OLD <<NEW
старая строка1
старая строка2
OLD
новая строка1
новая строка2
NEWDeclare heredoc markers in body order. Terminators must appear alone on a line, and body whitespace is preserved literally.
Read and inspect
READ path
READ --fresh path
READ -n path:10-40
READ --outline path
READ --def name[,name2] path
READ --fn name[,name2] path
USES name [path] [--all] [--summary]
FILES [path ...] [--limit N]
FILES [path ...] --hash
FILES [path ...] --changed
JSON path [dot.path]
RG [--summary] [--limit N] args...
CG query
CG [status|sync|query|explore|node|files|callers|callees|impact|affected] args...
SEM "natural language query"
SEM status
SEM [--path path] [--glob mask] [--ext ext] [--label name] [--no-tests] "query"
AROUND path "text" [--before N] [--after N]
EXPECT path exists
EXPECT path not-exists
EXPECT path contains "text"
EXPECT path not-contains "text"
EXPECT path --def name[,name2]
ASK [seconds|--timeout N] [--model model] `prompt text
`
ASK --begin
READ src/a.ts
READ src/b.ts
ASK --end `prompt for captured output
`
EXEC [seconds|--timeout N] `await dsl.READ("src/app.ts");
dsl.PRINT("visible output");
`
JS [seconds|--timeout N] `await dsl.RG('"GameEngine" src --summary');
`Aliases:
READ --fn: --func
AROUND: READAROUND, LINES
READ --fresh: --forceCodeGraph (CG) uses the indexed project graph for fast symbol/file navigation and impact checks. First non-status use initializes .codegraph if needed; after file edits, SAgent starts a background codegraph sync when an index exists. Examples:
CG start_node_exec
CG --ask "Pick files, risks, and NEXT DSL." `start_node_exec
resolve_profile_attempts
`
CG impact start_node_exec --depth 3
CG node codex-rs/core/src/compact.rsSemantic search (SEM) builds a project-local index under .agent-dsl/semantic and combines dense embeddings with lexical identifier matching. Indexing runs in the background through the packaged sagent-semantic-index sidecar, while queries use the read-only sagent-semantic-query sidecar.
SEM status
SEM "where is command execution rendered in the TUI?"
SEM --path codex-rs/core --no-tests "canonical tool lifecycle"
SEM --ask "{{ranges}} Pick the 3 ranges to inspect next." "command badge rendering"Results include symbols, signatures, CodeGraph caller/callee context when available, and ready-to-use READ ranges. Scope, labels, embedding providers, and optional reranking can be configured in .agent-dsl/config.toml.
Structural commands support:
- JavaScript;
- TypeScript;
- JSX / React;
- TSX / React;
- Vue SFC
scriptandscript setup; - Rust.
Structural commands include:
READ --outline;READ --def;READ --fn;USES;REPLACE --def;COPY --def;MOVE --def;DELETE --def.EXPECT --def.
Raw READ output is remembered in memory while it remains safe to assume the model has seen it. If the same unchanged file/range is read again, SAgent can answer with a short already read unchanged message instead of re-sending the content. READ -n records numbered coverage separately: raw READ can reuse raw or numbered coverage, while numbered READ only reuses numbered coverage. Use READ --fresh (or --force) when you need the bytes printed again. Reads hidden behind --ask, ASK --begin, or EXEC are not remembered, and the memory is cleared after context compaction.
EXPECT
Use EXPECT to assert preconditions before edits or postconditions after commands:
EXPECT src/game/engine.ts exists
EXPECT src/game/engine.ts contains "createSnapshot"
EXPECT src/game/engine.ts not-contains "legacySnapshot"
EXPECT src/game/engine.ts --def createSnapshotIf an expectation fails, the batch stops and the failed EXPECT plus following commands remain in .agent-dsl/tasks/<N>.dsl for RESUME.
Use EXPECT? ... for a soft check that warns and continues. Failure messages include matching lines, closest lines, or available definitions where possible.
READ --outline
Returns a structural map of the file:
- imports;
- top-level definitions;
- classes and methods;
- functions;
- constants;
- React components;
- Vue definitions;
- Rust items.
READ --def
Reads exact definitions by symbol name:
READ --def createSnapshot,GameEngine src/game/engine.tsUse this instead of reading a whole large file when only specific definitions are needed.
USES --summary
Shows compact per-file usage counts:
USES createSnapshot src --summaryUse --all to include hidden or less direct references.
FILES --changed
Compares against the last FILES --hash baseline.
If no baseline exists, SAgent returns NO_BASELINE and continues the batch.
Global --ask
Use --ask "prompt" on a command when the raw output is large and you only need a model-produced answer:
SH --log 7 --ask "Extract the local dev server URL only"ASK treats command output as untrusted data and answers the prompt without tools.
--ask is fast and cheap; use it to select the important parts from large READ, RG, test, log, and web outputs before loading more context.
EXEC / JS
EXEC runs JavaScript through ordinary Node.js inside the platform OS sandbox and gives the script async access to the same DSL command handlers that normal commands use.
EXEC --timeout 45 `const files = await dsl.FILES("src --limit 80");
const choice = await dsl.ask(files + "\nPick the 2 files most likely related to auth.");
dsl.PRINT(choice);
`Key rules:
all regular DSL commands are available as
await dsl.READ(...),await dsl.RG(...),await dsl.PATCH(...),await dsl.PLAN(...), etc.;arguments and flags are the same as the text after
COMMAND;multiline command bodies use template strings passed separately from arguments;
REPLACEaccepts both old and new bodies:await dsl.PATCH('--gen src/game/engine.ts --ask "Summarize the change"', ` Add a guard for empty enemy waves. Keep public signatures unchanged.`); await dsl.REPLACE('src/version.txt', `old value`, `new value`);dsl.EXECanddsl.JSare intentionally unavailable inside EXEC to avoid recursive script runners;visible successful output must be written with
dsl.PRINT(value)ordsl.print(value);console.log/ stdout are hidden on success and shown only as diagnostics when the script fails;dsl.ask(prompt, choices?)calls the fast ASK model;choicescan constrain answers, for example["yes", "no"];dsl.try.COMMAND(...)returns{ ok, output }or{ ok: false, error, response }instead of throwing;persistent JSON state is available through
dsl.state.set(key, value),dsl.state.get(key), anddsl.state.exists(key).
Generated scripts are stored under .agent-dsl/exec/<task>-<step>.mjs. The local RPC port and token are passed through environment variables and are not written into the script.
EXEC requires Node.js 22.6+. Windows uses the packaged codex-sandbox-runner.exe; Linux uses codex-linux-sandbox through the SAgent self-dispatch; macOS uses /usr/bin/sandbox-exec. There is no unsandboxed fallback. On Windows, ACL grants from older sandbox runs can persist, so use a VM or container for fully hostile code.
Edit files
CREATE path `file body
`
CREATE path --diff `file body
`
CREATE path --after `append body
`
CREATE path --before `prepend body
`
CREATE --gen path `instruction for generating the file/content
`
REPLACE path `old text` `new text
`
REPLACE path --after `anchor text` `inserted text
`
REPLACE path --before `anchor text` `inserted text
`
REPLACE path --def `export function name() {
return true
}
`
REPLACE path --diff `old text` `new text
`
REPLACE --gen path --def name[,name2] `instruction for generating replacement definition bodies
`
PATCH `*** Begin Patch
*** Update File: file.txt
@@
-old
+new
*** End Patch
`
PATCH --diff `*** Begin Patch
*** Update File: file.txt
@@
-old
+new
*** End Patch
`
PATCH --gen path [path2 ...] `instruction for generating an apply_patch patch for only those files
`
COPY src -> dst
COPY src --def name[,name2] -> dst
MV src -> dst
MOVE src --def name[,name2] -> dst
DELETE path
DELETE path --def name[,name2]Aliases:
COPY: CP
MV: MOVE, RENAME
DELETE: RM, DELSymbol-aware editing
Prefer REPLACE --def when replacing a whole function, class, component, or Rust item:
REPLACE src/file.ts --def `export function foo() {
return 1
}
export function bar() {
return 2
}
`REPLACE --def:
- takes only new definition bodies;
- finds matching definitions by name;
- can replace multiple definitions in one command;
- validates replacement syntax before modifying the file.
Definition copy/move/delete
COPY src/game/engine.ts --def createSnapshot -> src/game/snapshot.ts
MOVE src/game/engine.ts --def createSnapshot -> src/game/snapshot.ts
DELETE src/game/engine.ts --def createSnapshotThese commands operate on named definitions instead of raw text.
They do not currently auto-fix imports, exports, or call-sites. A planned --fix-imports mode is listed in PLANNED_CHANGES.md.
Fast generated edits with --gen
--gen uses the same fast, cheap single-shot model as ASK to generate small, well-scoped edits without making the main model hand-write boilerplate.
CREATE --gen src/game/damage.ts `Create a small damage helper module with applyDamage(unit, amount).
Clamp health at 0 and export the function.
`
PATCH --gen src/game/engine.ts src/game/damage.ts `Use applyDamage from damage.ts in the engine damage flow.
`
REPLACE --gen src/game/engine.ts --def applyDamage `Keep the same signature, but clamp health at 0 and preserve existing return shape.
`CREATE --gen pathgenerates file/content text and writes it throughCREATE.PATCH --gen path...reads the listed files, asks for anapply_patchpatch, verifies that only those files are touched, and applies it.REPLACE --gen path --def name[,name2]reads the current definitions, asks for replacement definition bodies, and validates them throughREPLACE --def.
Use --gen for targeted local edits, boilerplate, and tests. For broad architecture work, still inspect the code first and batch READ/PATCH/RUN together.
Syntax diagnostics
After file edits, SAgent prints compact summaries such as:
REPLACE src/app.ts M +3 -1
PATCH
M src/a.ts +3 -1
M src/b.ts +10 -4For single-target CREATE and REPLACE, the target path is already in the command header, so the compact summary omits the duplicate path. PATCH keeps per-file paths because it can touch many files. Use --diff on CREATE, REPLACE, or PATCH when you need visible DIFF hunks. Otherwise, prefer the compact summary to save context. SAgent also runs parser-based syntax diagnostics for supported languages and prints SYNTAX WARN when it detects parse errors.
Diagnostics are tree-sitter based and may miss grammar-accepted invalid constructs.
Shell, tests, and background jobs
SH [seconds] `command body
`
RUN [seconds] `command body
`
SH 0 [--tty] `long-running command body
`
WAIT N
SH --status [job_id]
SH --stop [job_id]
SH --log job_id [--limit N]
SH --log job_id --all [--limit N]
SH --send job_id [--noAwait] [--timeout N] `input for the background process
`SH and RUN execute shell commands from the project root.
WAIT N pauses the batch for N seconds without invoking a shell, which is useful before checking background job logs.
Timed command
RUN 120 `npm test
`Background command
SH 0 `npm run dev
`Returns:
job_id=7
pid=12345
mode=normal
status=runningSH 0 starts outside the sandbox through the exec approval flow.
Interactive TTY job
SH 0 --tty `node
`Send input:
SH --send 7 --timeout 3 `1 + 2
`SH --send waits for output by default and advances the log cursor.
Use --noAwait to send without waiting.
For interactive programs such as SSH, SFTP, and REPLs, SAgent sends terminal Enter as CRLF.
Logs
SH --log 7Returns only unread stdout/stderr since the previous log read and advances per-stream cursors.
SH --log 7 --allReturns the full log without advancing cursors.
SH --log 7 --limit 50In normal unread mode, keeps only the newest 50 unread lines, advances the cursor to the current log end, and reports omitted_before_limit=M if older unread lines were skipped.
For --tty jobs, the PTY stream is shown as STDOUT; STDERR is omitted.
Status and stop
SH --status
SH --status 7
SH --stop 7
SH --stopSH --status lists only active jobs.
SH --stop kills the process tree.
Notes
NOTE short noteAdds a note to the batch output.
Recovery
STATUS [task_name] [--limit N]Shows unfinished saved commands.
RESUME task_name `old text in saved task` `new text for saved task`RESUME:
- loads
.agent-dsl/tasks/<task_name>.dsl; - optionally applies exact text replacement or
--skip [N]; - runs that saved task separately;
- returns to the current task and continues commands after
RESUME; - moves successful resumed commands to
<task_name>.ok; - leaves failed and remaining resumed commands in
<task_name>.dsl.
RESUME can appear anywhere in a batch and can be used multiple times. If it fails, the current task stops at the RESUME command. RESUME? ... reports the resumed task name, failed command, and remaining command count, then continues the current task.
Planning
SAgent provides a DSL-native plan cursor.
PLAN --new Optional explanation `[>] First step
multiline detail
[ ] Second step
[ ] Third step
[x] Already completed step
`Markers:
[>]current step;[ ]pending step;[x]completed step.
Multiline step details continue until the next marker.
If no [>] exists, the first [ ] becomes current in memory.
File-backed plan
PLAN --open PLAN.md Optional explanationor:
PLAN --load PLAN.md Optional explanationThe plan file uses the same marker format:
[>] Investigate current behavior
details may span multiple lines
[ ] Implement file-backed PLAN
[ ] Test synchronization
[x] Old completed stepFor file-backed plans:
- the file is the source of truth;
- SAgent re-reads it on later
dslcalls; - manual edits are picked up automatically;
PLAN --doneis the only command that writes markers back to the file;PLAN --clearonly clears the active plan/binding and does not modify the file.
If multiple [>] markers exist, SAgent uses the first as current and treats later current markers as pending in memory. PLAN --done normalizes markers when it writes the file.
Plan commands
PLAN --done Optional explanation
PLAN --clear Optional explanation
PLAN --show --limit N
PLAN --showAll
PLAN --show-allWhile a plan is active, later dsl outputs append:
CURRENT_PLAN
2/5: Current step descriptionWhen every step is complete, CURRENT_PLAN stops appearing, but the completed plan remains available through PLAN --showAll until cleared.
Web commands
WEB_SEARCH query response_length=short
WEB_IMAGE_SEARCH query response_length=short
WEB_IMG_SEARCH query response_length=short
WEB_OPEN ref_or_url lineno=N
WEB_FIND ref_or_url pattern
WEB key=value ...
WEB `{"open":[{"ref_id":"https://example.com","lineno":5}]}
`WEB accepts key/value arguments or a raw JSON block.
Image commands
IMAGE_GEN filename `prompt text
`
IMG_GEN filename `prompt text
`
VIEW_IMAGE path
VIEW_IMAGE path detail=originalIMAGE_GEN / IMG_GEN generate PNG images.
VIEW_IMAGE loads a local image for visual inspection.
Golden workflows
Safely refactor a function
READ --outline src/file.ts
READ --def targetFunction src/file.ts
USES targetFunction src --summary
REPLACE src/file.ts --def `export function targetFunction() {
return "updated"
}
`
RUN `npm test
`Start a dev server and inspect logs
SH 0 `npm run dev
`
WAIT 10
SH --status
SH --log 1Later:
SH --log 1
SH --stop 1Work with a large plan
PLAN --open PLAN.md
READ --outline src/main.ts
RG --summary "createApp" srcAfter the active step is complete:
PLAN --done implemented parser lookupRuntime files
The sagent-ai meta package ships the launcher and selects a native package for the current platform:
sagent-ai
├── bin/sagent.js
├── sagent-ai-win (Windows x64)
└── sagent-ai-linux (Linux x64)Each native package contains:
bin/sagent
bin/apply_patch
bin/sagent-semantic-index
bin/sagent-semantic-query
bin/rg
bin/system.md
bin/dsl_runtime.mjsWindows uses the corresponding .exe filenames and also packages bin/codex-sandbox-runner.exe plus its codex-resources helpers. system.md and dsl_runtime.mjs are loaded next to the native binary, semantic sidecars power SEM, and the bundled rg binary powers RG.
Summary
SAgent keeps the Codex foundation and changes the model-facing workflow into a compact script-like protocol.
The result is a coding agent workflow with:
- fewer tool calls;
- less JSON overhead;
- readable command history;
- reliable batch recovery;
- structural code navigation and edits;
- background and interactive process control;
- passive status feedback;
- file-backed planning;
- web and image support.
It is designed for real project work: search, inspect, edit, test, run, recover, and continue.
