Tools
Tool selection, direct tool calls, and verification evidence
Tools
A3S Code keeps a registry of available tools. toolNames() returns the current
session tool surface.
Default Core Tools
| Category | Tools |
|---|---|
| Files | read, write, edit, patch |
| Search | grep, glob, ls |
| Shell | bash |
| Delegation | task, parallel_task |
| Skills | search_skills, Skill |
| Structured Output | generate_object |
Additional Registered Tools
web_fetch, web_search, git, batch, program, and MCP tools are also
part of the broader tool surface. Validate model-visible selection behavior in
the product flow that depends on it.
Structured Output with generate_object
The generate_object tool produces a JSON object that strictly conforms to a
provided JSON Schema. 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 the LLM and get deterministic structured output.
const result = await session.tool('generate_object', {
schema: {
type: 'object',
required: ['sentiment', 'confidence'],
properties: {
sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
confidence: { type: 'number', minimum: 0, maximum: 1 },
},
},
prompt: 'Classify: "This product is amazing!"',
schema_name: 'sentiment',
mode: 'tool', // 'auto' | 'tool' | 'prompt'
max_repair_attempts: 2, // retries if schema validation fails
});
const { object } = JSON.parse(result.output);
// object = { sentiment: "positive", confidence: 0.95 }Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
schema | object | yes | JSON Schema the output must conform to |
prompt | string | yes | What to generate or extract |
schema_name | string | no | Short name (default: "result") |
schema_description | string | no | Description for the synthetic tool |
system | string | no | Optional system prompt |
mode | string | no | "auto" / "tool" / "prompt" (default: auto) |
max_repair_attempts | integer | no | 0–5 (default: 2) |
Modes
- tool (default): Injects a synthetic tool whose parameters IS the schema. The LLM is forced to call it. Most reliable across all providers.
- prompt: Appends schema instructions to the prompt. Less reliable but works without tool-calling support.
- auto: Selects the best mode for the provider (currently resolves to
tool).
Streaming
When called through session.stream(), partial objects are emitted as
tool_output_delta events:
for await (const ev of await session.stream('Extract all invoices...')) {
if (ev.type === 'tool_output_delta' && ev.toolName === 'generate_object') {
const { object_partial } = JSON.parse(ev.text);
renderProgress(object_partial);
}
}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:
const files = await session.glob('src/**/*.rs');
const hits = await session.grep('PermissionPolicy');
const status = await session.git('status');
const output = await session.bash('cargo test -p a3s-code-core');
const raw = await session.tool('read', { file_path: 'README.md' });
const schemas = session.toolDefinitions();Direct calls execute inside the session workspace and should be treated as privileged host operations.
For delegated child work, use SDK helpers over the same core tools:
await session.task({
agent: 'explore',
description: 'Find auth files',
prompt: 'Inspect auth-related files and return a compact evidence list.',
});
await session.tasks([
{ agent: 'explore', description: 'Find tests', prompt: 'Locate auth tests.' },
{ agent: 'verification', description: 'Check risk', prompt: 'Review auth edge cases.' },
]);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:
grep -> read -> grep -> read -> summarizethe model can ask program to run a script:
// search-auth.js
export default async function run(ctx, inputs) {
const hits = await ctx.grep(inputs.query, { glob: '*.rs' });
const files = await ctx.glob('crates/**/*.rs');
const snippets = [];
for (const file of files.slice(0, 20)) {
const content = await ctx.readFile(file);
if (content.includes(inputs.query)) {
snippets.push({ file, preview: content.slice(0, 1200) });
}
}
return {
summary: `Found ${snippets.length} candidate files for ${inputs.query}`,
evidence: snippets,
rawSearch: hits,
};
}Run it through the SDK helper with either inline source or a workspace-relative .js or .mjs file path:
await session.program({
path: 'scripts/ptc/search-auth.js',
inputs: { query: 'PermissionPolicy' },
allowedTools: ['grep', 'glob', 'read'],
limits: {
timeoutMs: 30000,
maxToolCalls: 30,
maxOutputBytes: 65536,
},
});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.
Tool Results
ToolResult includes name, output, exitCode, and optional metadataJson. Long outputs should be summarized before they are fed back to the model.
Verification
Use verification commands to turn "done" into evidence:
const report = await session.verifyCommands('release readiness', [
{
id: 'unit',
kind: 'test',
description: 'Run core tests',
command: 'cargo test -p a3s-code-core',
required: true,
timeoutMs: 120000,
},
]);