Tools
A3S Code keeps a registry of available tools. toolNames() returns the current
session tool surface. That surface is assembled from the workspace capability
set plus session-level integrations, so a non-local workspace can intentionally
hide tools it cannot service.
Tool activity reaches clients through the session event stream, so a UI can render starts, output, errors, and completion without parsing terminal text.
Tool Surface
Use toolNames() / toolDefinitions() in tests or application diagnostics when
a workflow depends on a specific tool being visible. Tool visibility is not a
security grant. Model-selected tool calls inside send, run, and stream
pass through active-skill restrictions, permission policy, confirmation, hooks,
budget, queue/timeouts, cancellation, recursive-call protection, output
sanitization, artifact limits, and workspace path checks. Direct SDK calls such
as session.tool(...) are host control-plane calls with a different explicit
policy, described below.
Use activeTools() for a different question: which tool calls are currently
running in an active operation.
One Invocation Kernel
The runtime attaches an origin to every invocation:
Model sub-runs created by a host-direct Skill, Task, or custom tool start again
with the Agent origin. A public custom tool can make nested calls only through
InvocationRuntime, which always creates the governed nested origin. This
prevents one direct call to arbitrary extension code from becoming a reusable
authority token.
All origins use the same invocation kernel. Pre-hooks can block them; budget checks run before side effects; queue/timeout, cancellation, recursive invocation protection, post-hooks, and security-provider output sanitization remain active. A nested call cannot fall back to a raw registry while a scoped invoker is installed.
The host-direct policy is not an end-user authorization system. The embedding application must authenticate and authorize a user before translating their request into a direct SDK helper. Closing the session cancels an in-flight host-direct tool through the session cancellation scope.
Bounded Tool Contracts
Governed agent, nested, and session calls validate arguments against each tool's cached JSON Schema before confirmation or side effects. Tools also publish per-invocation scheduling capabilities: read-only, idempotent, resumable, cancellation-safe, pagination support, maximum parallelism, and output kind.
read, ls, glob, Git log/list/diff, and web_fetch return explicit
continuation cursors or offsets. Shell capture is byte-bounded across both
streams, retains the beginning and end with exact accounting, and still
exposes stdout and stderr separately to a sandbox host. A command deadline
includes both output draining and child.wait(), so closing both pipes cannot
evade it. Timeout and cancellation terminate the complete Unix process group.
Large file changes replace full before/after event payloads with bounded
previews and unified diff text plus hashes, sizes, and artifact references.
Repository-context modes
Use read.files when the relevant paths are already known and one bounded
response is cheaper than several tool turns:
The shared byte budget includes headers and the continuation text. Results
stay in request order, and one unreadable member does not discard successful
members. When metadata.batch.truncated is true, copy
metadata.batch.continuation into the next call's files value; its offsets
and remaining limits resume without repeating completed lines.
grep.output_mode selects the smallest useful result shape:
Built-in workspace backends perform non-content scans without constructing
discarded match text. S3 results set metadata.search.truncated and warn when
the backend's object scan limit makes totals or paths incomplete.
glob keeps backend relevance or recency order by default. Set sort: "path"
before cursor pagination when stable lexical pages are required.
For exact-string changes, call edit with dry_run: true to return the same
before/after diff metadata without writing. Then apply the edit with
expected_replacements set to the previewed count, and optionally add
max_replacements as an independent upper bound. Dry runs advertise read-only
capability and are safe for batch parallelization.
batch accepts at most 32 calls and applies at most 16-way concurrency. It
fans out only when every child declares safe read-only, idempotent behavior;
mutating and unknown tools are serialized. A partial batch identifies failed
indices while treating the orchestration as completed, so callers retry only
failed items. parallel_task has the same 32-task bound and settles cancelled
children before publishing terminal state.
parallel_task requires 2-32 independent foreground branches. Each branch
accepts agent, description, prompt, optional max_steps, and optional
output_schema; background is not a parallel-branch parameter. The optional
min_success_count is valid only with allow_partial_failure: true and must be
between 1 and the submitted task count. Provider and child runtimes own typed
retry policy; the fan-out layer never replays a branch based on error text.
web_search reports complete, partial, or failed in metadata. Empty
results with engine errors are failures rather than successful empty searches.
Timeout, cancellation, invalid-argument, partial-failure, and rate-limit errors
carry structured error kinds.
Structured Output with generate_object
The generate_object tool asks the configured LLM for a JSON value, validates
the response against a JSON Schema, and returns the validated value only on a
zero-exit result. Root objects, arrays, enums, constants, composition keywords,
and local $ref definitions are supported. It works in two ways:
- Agent-driven: The LLM sees
generate_objectin its tool list and calls it autonomously when structured output is needed. - Direct call: Your application calls
session.tool('generate_object', ...)to bypass model-driven tool selection. The tool itself still calls the configured LLM.
Parameters
Modes
- tool: Forces a synthetic tool whose parameters are the schema when the provider supports forced tool calls.
- prompt: Appends schema instructions to the prompt. It is useful as a prompt-only fallback, but depends more on model compliance.
- auto: Selects forced-tool mode when available, otherwise prompt mode.
- strict: Uses provider-native strict JSON Schema when supported, otherwise safely falls back to forced-tool or prompt mode.
- json: Uses provider-native JSON-object mode when supported, otherwise safely falls back to forced-tool or prompt mode.
Streaming
When called through session.stream(), partial objects are emitted as
tool_output_delta events. Snapshots are rate-limited to one event per 100 ms;
objects over the event budget emit byte accounting instead of duplicating the
full value in the stream:
Repair Loop
If the LLM output fails schema validation, the tool automatically retries by feeding the validation errors back to the model. This handles edge cases like missing required fields or wrong enum values without application-level retry logic.
Direct Tool Calls
SDK callers can call deterministic tools directly:
Direct calls execute inside the session workspace and should be treated as privileged host operations. They do not claim the session's single-flight conversation lease because they do not update transcript history.
For delegated child work, use SDK helpers over the same core tools:
Automatic subagent delegation also uses these core tools. autoParallel: false
disables only automatic parallel fan-out; it does not remove parallel_task or
session.tasks(...).
Programmatic Tool Calling
PTC is the next step beyond a single direct call. The program tool runs a sandboxed JavaScript script in an embedded QuickJS VM. The script defines async function run(ctx, inputs) and replaces repeated model-tool turns with one bounded program.
Instead of spending LLM turns on:
the model can ask program to run a script:
Run it through the SDK helper with either inline source or a workspace-relative .js or .mjs file path:
session.program(...) is equivalent to session.tool('program', { type: 'script', language: 'javascript', ... }) but uses SDK-native naming. When allowedTools / allowed_tools is omitted, the script can call every registered tool except program. Provide an allow-list when a workflow should run with a smaller capability surface.
The QuickJS VM receives no filesystem, network, subprocess, or environment permissions. The only useful capabilities are the ctx methods wired back to A3S Code tools. PTC returns a readable ToolResult.output and structured data in ToolResult.metadataJson. Keep large raw output out of the prompt; summarize findings, evidence refs, risks, and suggested next actions.
In a3s code, PTC is also used by DynamicWorkflowRuntime. Recursive
program, dynamic_workflow, and parallel_task calls are kept out of the
default TUI PTC allow-list. When a dynamic workflow needs local parallel
subagents, it schedules a Flow step named parallel_task; the TUI host runs
the native parallel_task implementation outside QuickJS. After /login,
dynamic workflow PTC steps may call ctx.tool("runtime", ...) when a host
runtime tool is registered in the session.
Tool Results
ToolResult includes name, output, exitCode, and optional metadataJson.
Direct session.tool(...), session.program(...), session.git(...),
session.writeFile(...), session.ls(...), session.editFile(...), and
session.patchFile(...) calls return ToolResult. The typed read/search/shell
helpers return simpler values: readFile, grep, and bash return strings,
while glob returns a string array. Long outputs should be summarized before
they are fed back to the model.
Verification
Use verification commands to turn "done" into evidence: