@ottocode/sdk 0.1.246 → 0.1.247

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottocode/sdk",
3
- "version": "0.1.246",
3
+ "version": "0.1.247",
4
4
  "description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
5
5
  "author": "nitishxyz",
6
6
  "license": "MIT",
@@ -37,6 +37,10 @@
37
37
  "import": "./src/core/src/tools/builtin/bash.ts",
38
38
  "types": "./src/core/src/tools/builtin/bash.ts"
39
39
  },
40
+ "./tools/builtin/shell": {
41
+ "import": "./src/core/src/tools/builtin/shell.ts",
42
+ "types": "./src/core/src/tools/builtin/shell.ts"
43
+ },
40
44
  "./tools/builtin/finish": {
41
45
  "import": "./src/core/src/tools/builtin/finish.ts",
42
46
  "types": "./src/core/src/tools/builtin/finish.ts"
@@ -1,266 +1 @@
1
- import { tool, type Tool } from 'ai';
2
- import { spawn } from 'node:child_process';
3
- import { z } from 'zod/v3';
4
- import DESCRIPTION from './bash.txt' with { type: 'text' };
5
- import { getAugmentedPath } from '../bin-manager.ts';
6
- import { createToolError, type ToolResponse } from '../error.ts';
7
- import { injectCoAuthorIntoGitCommit } from './git-identity.ts';
8
-
9
- function normalizePath(p: string) {
10
- const normalized = p.replace(/\\/g, '/');
11
- const driveMatch = normalized.match(/^([A-Za-z]):\//);
12
- const drivePrefix = driveMatch ? `${driveMatch[1]}:` : '';
13
- const rest = driveMatch ? normalized.slice(2) : normalized;
14
- const parts = rest.split('/');
15
- const stack: string[] = [];
16
- for (const part of parts) {
17
- if (!part || part === '.') continue;
18
- if (part === '..') stack.pop();
19
- else stack.push(part);
20
- }
21
- if (drivePrefix) return `${drivePrefix}/${stack.join('/')}`;
22
- return `/${stack.join('/')}`;
23
- }
24
-
25
- function resolveSafePath(projectRoot: string, p: string) {
26
- const root = normalizePath(projectRoot);
27
- const abs = normalizePath(`${root}/${p || '.'}`);
28
- if (!(abs === root || abs.startsWith(`${root}/`))) {
29
- throw new Error(`cwd escapes project root: ${p}`);
30
- }
31
- return abs;
32
- }
33
-
34
- function killProcessTree(pid: number) {
35
- try {
36
- process.kill(-pid, 'SIGTERM');
37
- } catch {
38
- try {
39
- process.kill(pid, 'SIGTERM');
40
- } catch {}
41
- }
42
- }
43
-
44
- type BashResult = ToolResponse<{
45
- exitCode: number;
46
- stdout: string;
47
- stderr: string;
48
- }>;
49
-
50
- type BashStreamChunk =
51
- | {
52
- channel: 'output';
53
- delta: string;
54
- }
55
- | {
56
- result: BashResult;
57
- };
58
-
59
- export function buildBashTool(projectRoot: string): {
60
- name: string;
61
- tool: Tool;
62
- } {
63
- const bash = tool({
64
- description: DESCRIPTION,
65
-
66
- inputSchema: z
67
- .object({
68
- cmd: z.string().describe('Shell command to run (bash -c <cmd>)'),
69
- cwd: z
70
- .string()
71
- .default('.')
72
- .describe('Working directory relative to project root'),
73
- allowNonZeroExit: z
74
- .boolean()
75
- .optional()
76
- .default(false)
77
- .describe('If true, do not throw on non-zero exit'),
78
- timeout: z
79
- .number()
80
- .optional()
81
- .default(300000)
82
- .describe('Timeout in milliseconds (default: 300000 = 5 minutes)'),
83
- })
84
- .strict(),
85
- execute(
86
- {
87
- cmd,
88
- cwd,
89
- allowNonZeroExit,
90
- timeout = 300000,
91
- }: {
92
- cmd: string;
93
- cwd?: string;
94
- allowNonZeroExit?: boolean;
95
- timeout?: number;
96
- },
97
- options?: { abortSignal?: AbortSignal },
98
- ): AsyncIterable<BashStreamChunk> | BashResult {
99
- const abortSignal = options?.abortSignal;
100
-
101
- if (abortSignal?.aborted) {
102
- return createToolError('Command aborted before execution', 'abort', {
103
- cmd,
104
- });
105
- }
106
-
107
- const absCwd = resolveSafePath(projectRoot, cwd || '.');
108
- const finalCmd = injectCoAuthorIntoGitCommit(cmd);
109
-
110
- const proc = spawn(finalCmd, {
111
- cwd: absCwd,
112
- shell: true,
113
- stdio: ['ignore', 'pipe', 'pipe'],
114
- env: { ...process.env, PATH: getAugmentedPath() },
115
- detached: true,
116
- });
117
-
118
- let stdout = '';
119
- let stderr = '';
120
- let didTimeout = false;
121
- let didAbort = false;
122
- let settled = false;
123
- let done = false;
124
- let timeoutId: ReturnType<typeof setTimeout> | null = null;
125
- const queue: BashStreamChunk[] = [];
126
- let notify: (() => void) | null = null;
127
-
128
- const wake = () => {
129
- if (!notify) return;
130
- notify();
131
- notify = null;
132
- };
133
-
134
- const pushDelta = (text: string) => {
135
- if (!text) return;
136
- queue.push({ channel: 'output', delta: text });
137
- wake();
138
- };
139
-
140
- const settle = (result: BashResult) => {
141
- if (settled) return;
142
- settled = true;
143
- if (timeoutId) clearTimeout(timeoutId);
144
- if (abortSignal) {
145
- abortSignal.removeEventListener('abort', onAbort);
146
- }
147
- queue.push({ result });
148
- done = true;
149
- wake();
150
- };
151
-
152
- const onAbort = () => {
153
- if (settled) return;
154
- didAbort = true;
155
- if (proc.pid) killProcessTree(proc.pid);
156
- else proc.kill('SIGTERM');
157
- };
158
-
159
- if (abortSignal) {
160
- abortSignal.addEventListener('abort', onAbort, { once: true });
161
- }
162
-
163
- if (timeout > 0) {
164
- timeoutId = setTimeout(() => {
165
- didTimeout = true;
166
- if (proc.pid) killProcessTree(proc.pid);
167
- else proc.kill();
168
- }, timeout);
169
- }
170
-
171
- proc.stdout?.on('data', (chunk) => {
172
- const text = chunk.toString();
173
- stdout += text;
174
- pushDelta(text);
175
- });
176
-
177
- proc.stderr?.on('data', (chunk) => {
178
- const text = chunk.toString();
179
- stderr += text;
180
- pushDelta(text);
181
- });
182
-
183
- proc.on('close', (exitCode) => {
184
- if (didAbort) {
185
- settle(
186
- createToolError(`Command aborted by user: ${cmd}`, 'abort', {
187
- cmd,
188
- stdout,
189
- stderr,
190
- }),
191
- );
192
- return;
193
- }
194
-
195
- if (didTimeout) {
196
- settle(
197
- createToolError(
198
- `Command timed out after ${timeout}ms: ${cmd}`,
199
- 'timeout',
200
- {
201
- parameter: 'timeout',
202
- value: timeout,
203
- stdout,
204
- stderr,
205
- suggestion: 'Increase timeout or optimize the command',
206
- },
207
- ),
208
- );
209
- return;
210
- }
211
-
212
- if (exitCode !== 0 && !allowNonZeroExit) {
213
- const errorDetail = stderr.trim() || stdout.trim() || '';
214
- const errorMsg = `Command failed with exit code ${exitCode}${errorDetail ? `\n\n${errorDetail}` : ''}`;
215
- settle(
216
- createToolError(errorMsg, 'execution', {
217
- exitCode,
218
- stdout,
219
- stderr,
220
- cmd,
221
- suggestion: 'Check command syntax or use allowNonZeroExit: true',
222
- }),
223
- );
224
- return;
225
- }
226
-
227
- settle({
228
- ok: true,
229
- exitCode: exitCode ?? 0,
230
- stdout,
231
- stderr,
232
- });
233
- });
234
-
235
- proc.on('error', (err) => {
236
- settle(
237
- createToolError(
238
- `Command execution failed: ${err.message}`,
239
- 'execution',
240
- {
241
- cmd,
242
- originalError: err.message,
243
- },
244
- ),
245
- );
246
- });
247
-
248
- const stream = async function* (): AsyncGenerator<BashStreamChunk> {
249
- while (!done || queue.length > 0) {
250
- if (queue.length === 0) {
251
- await new Promise<void>((resolve) => {
252
- notify = resolve;
253
- });
254
- }
255
- while (queue.length > 0) {
256
- const chunk = queue.shift();
257
- if (chunk) yield chunk;
258
- }
259
- }
260
- };
261
-
262
- return stream();
263
- },
264
- }) as unknown as Tool;
265
- return { name: 'bash', tool: bash };
266
- }
1
+ export { buildShellTool, buildShellTool as buildBashTool } from './shell.ts';
@@ -0,0 +1,273 @@
1
+ import { tool, type Tool } from 'ai';
2
+ import { spawn } from 'node:child_process';
3
+ import { z } from 'zod/v3';
4
+ import DESCRIPTION from './shell.txt' with { type: 'text' };
5
+ import { getAugmentedPath } from '../bin-manager.ts';
6
+ import { createToolError, type ToolResponse } from '../error.ts';
7
+ import { injectCoAuthorIntoGitCommit } from './git-identity.ts';
8
+
9
+ function normalizePath(p: string) {
10
+ const normalized = p.replace(/\\/g, '/');
11
+ const driveMatch = normalized.match(/^([A-Za-z]):\//);
12
+ const drivePrefix = driveMatch ? `${driveMatch[1]}:` : '';
13
+ const rest = driveMatch ? normalized.slice(2) : normalized;
14
+ const parts = rest.split('/');
15
+ const stack: string[] = [];
16
+ for (const part of parts) {
17
+ if (!part || part === '.') continue;
18
+ if (part === '..') stack.pop();
19
+ else stack.push(part);
20
+ }
21
+ if (drivePrefix) return `${drivePrefix}/${stack.join('/')}`;
22
+ return `/${stack.join('/')}`;
23
+ }
24
+
25
+ function resolveSafePath(projectRoot: string, p: string) {
26
+ const root = normalizePath(projectRoot);
27
+ const abs = normalizePath(`${root}/${p || '.'}`);
28
+ if (!(abs === root || abs.startsWith(`${root}/`))) {
29
+ throw new Error(`cwd escapes project root: ${p}`);
30
+ }
31
+ return abs;
32
+ }
33
+
34
+ function killProcessTree(pid: number) {
35
+ try {
36
+ process.kill(-pid, 'SIGTERM');
37
+ } catch {
38
+ try {
39
+ process.kill(pid, 'SIGTERM');
40
+ } catch {}
41
+ }
42
+ }
43
+
44
+ type ShellResult = ToolResponse<{
45
+ exitCode: number;
46
+ stdout: string;
47
+ stderr: string;
48
+ }>;
49
+
50
+ type ShellStreamChunk =
51
+ | {
52
+ channel: 'output';
53
+ delta: string;
54
+ }
55
+ | {
56
+ result: ShellResult;
57
+ };
58
+
59
+ const shellInputSchema = z
60
+ .object({
61
+ cmd: z
62
+ .string()
63
+ .describe('Non-interactive shell command to run (bash -c <cmd>)'),
64
+ cwd: z
65
+ .string()
66
+ .default('.')
67
+ .describe('Working directory relative to project root'),
68
+ allowNonZeroExit: z
69
+ .boolean()
70
+ .optional()
71
+ .default(false)
72
+ .describe('If true, do not throw on non-zero exit'),
73
+ timeout: z
74
+ .number()
75
+ .optional()
76
+ .default(300000)
77
+ .describe('Timeout in milliseconds (default: 300000 = 5 minutes)'),
78
+ })
79
+ .strict();
80
+
81
+ type ShellInput = z.infer<typeof shellInputSchema>;
82
+
83
+ type ShellToolFactory = (definition: {
84
+ description: string;
85
+ inputSchema: typeof shellInputSchema;
86
+ execute(
87
+ input: ShellInput,
88
+ options?: { abortSignal?: AbortSignal },
89
+ ): AsyncIterable<ShellStreamChunk> | ShellResult;
90
+ }) => Tool;
91
+
92
+ export function buildShellTool(projectRoot: string): {
93
+ name: string;
94
+ tool: Tool;
95
+ } {
96
+ const createTool = tool as unknown as ShellToolFactory;
97
+ const shell = createTool({
98
+ description: DESCRIPTION,
99
+ inputSchema: shellInputSchema,
100
+ execute(
101
+ { cmd, cwd, allowNonZeroExit, timeout = 300000 }: ShellInput,
102
+ options?: { abortSignal?: AbortSignal },
103
+ ): AsyncIterable<ShellStreamChunk> | ShellResult {
104
+ const abortSignal = options?.abortSignal;
105
+
106
+ if (abortSignal?.aborted) {
107
+ return createToolError('Command aborted before execution', 'abort', {
108
+ cmd,
109
+ });
110
+ }
111
+
112
+ const absCwd = resolveSafePath(projectRoot, cwd || '.');
113
+ const finalCmd = injectCoAuthorIntoGitCommit(cmd);
114
+
115
+ const proc = spawn(finalCmd, {
116
+ cwd: absCwd,
117
+ shell: true,
118
+ stdio: ['ignore', 'pipe', 'pipe'],
119
+ env: { ...process.env, PATH: getAugmentedPath() },
120
+ detached: true,
121
+ });
122
+
123
+ let stdout = '';
124
+ let stderr = '';
125
+ let didTimeout = false;
126
+ let didAbort = false;
127
+ let settled = false;
128
+ let done = false;
129
+ let timeoutId: ReturnType<typeof setTimeout> | null = null;
130
+ const queue: ShellStreamChunk[] = [];
131
+ let notify: (() => void) | null = null;
132
+
133
+ const wake = () => {
134
+ if (!notify) return;
135
+ notify();
136
+ notify = null;
137
+ };
138
+
139
+ const pushDelta = (text: string) => {
140
+ if (!text) return;
141
+ queue.push({ channel: 'output', delta: text });
142
+ wake();
143
+ };
144
+
145
+ const settle = (result: ShellResult) => {
146
+ if (settled) return;
147
+ settled = true;
148
+ if (timeoutId) clearTimeout(timeoutId);
149
+ if (abortSignal) {
150
+ abortSignal.removeEventListener('abort', onAbort);
151
+ }
152
+ queue.push({ result });
153
+ done = true;
154
+ wake();
155
+ };
156
+
157
+ const onAbort = () => {
158
+ if (settled) return;
159
+ didAbort = true;
160
+ if (proc.pid) killProcessTree(proc.pid);
161
+ else proc.kill('SIGTERM');
162
+ };
163
+
164
+ if (abortSignal) {
165
+ abortSignal.addEventListener('abort', onAbort, { once: true });
166
+ }
167
+
168
+ if (timeout > 0) {
169
+ timeoutId = setTimeout(() => {
170
+ didTimeout = true;
171
+ if (proc.pid) killProcessTree(proc.pid);
172
+ else proc.kill();
173
+ }, timeout);
174
+ }
175
+
176
+ proc.stdout?.on('data', (chunk) => {
177
+ const text = chunk.toString();
178
+ stdout += text;
179
+ pushDelta(text);
180
+ });
181
+
182
+ proc.stderr?.on('data', (chunk) => {
183
+ const text = chunk.toString();
184
+ stderr += text;
185
+ pushDelta(text);
186
+ });
187
+
188
+ proc.on('close', (exitCode) => {
189
+ if (didAbort) {
190
+ settle(
191
+ createToolError(`Command aborted by user: ${cmd}`, 'abort', {
192
+ cmd,
193
+ stdout,
194
+ stderr,
195
+ }),
196
+ );
197
+ return;
198
+ }
199
+
200
+ if (didTimeout) {
201
+ settle(
202
+ createToolError(
203
+ `Command timed out after ${timeout}ms: ${cmd}`,
204
+ 'timeout',
205
+ {
206
+ parameter: 'timeout',
207
+ value: timeout,
208
+ stdout,
209
+ stderr,
210
+ suggestion: 'Increase timeout or optimize the command',
211
+ },
212
+ ),
213
+ );
214
+ return;
215
+ }
216
+
217
+ if (exitCode !== 0 && !allowNonZeroExit) {
218
+ const errorDetail = stderr.trim() || stdout.trim() || '';
219
+ const errorMsg = `Command failed with exit code ${exitCode}${errorDetail ? `\n\n${errorDetail}` : ''}`;
220
+ settle(
221
+ createToolError(errorMsg, 'execution', {
222
+ exitCode,
223
+ stdout,
224
+ stderr,
225
+ cmd,
226
+ suggestion: 'Check command syntax or use allowNonZeroExit: true',
227
+ }),
228
+ );
229
+ return;
230
+ }
231
+
232
+ settle({
233
+ ok: true,
234
+ exitCode: exitCode ?? 0,
235
+ stdout,
236
+ stderr,
237
+ });
238
+ });
239
+
240
+ proc.on('error', (err) => {
241
+ settle(
242
+ createToolError(
243
+ `Command execution failed: ${err.message}`,
244
+ 'execution',
245
+ {
246
+ cmd,
247
+ originalError: err.message,
248
+ },
249
+ ),
250
+ );
251
+ });
252
+
253
+ const stream = async function* (): AsyncGenerator<ShellStreamChunk> {
254
+ while (!done || queue.length > 0) {
255
+ if (queue.length === 0) {
256
+ await new Promise<void>((resolve) => {
257
+ notify = resolve;
258
+ });
259
+ }
260
+ while (queue.length > 0) {
261
+ const chunk = queue.shift();
262
+ if (chunk) yield chunk;
263
+ }
264
+ }
265
+ };
266
+
267
+ return stream();
268
+ },
269
+ }) as unknown as Tool;
270
+ return { name: 'shell', tool: shell };
271
+ }
272
+
273
+ export const buildBashTool = buildShellTool;
@@ -0,0 +1,13 @@
1
+ - Execute a non-interactive shell command using `bash -lc`
2
+ - Returns `stdout`, `stderr`, and `exitCode`
3
+ - `cwd` is relative to the project root and sandboxed within it
4
+
5
+ **Use `shell` for one-off, non-interactive commands.** These may be short-lived checks or long-running commands that finish on their own and do not require stdin. For commands that need interactive input, a TTY, or persistence across turns, use the `terminal` tool instead.
6
+
7
+ ## Usage tips
8
+
9
+ - Chain commands with `&&` to fail-fast.
10
+ - For long outputs, redirect to a file and `read` it back.
11
+ - For long-running non-interactive commands, set an appropriate `timeout` and ensure the command exits on its own.
12
+ - Batch independent checks (e.g. `git status && git diff`) in parallel tool calls rather than sequential shell chains when you need results separately.
13
+ - Never use `shell` with `sed`/`awk` for programmatic file editing — use the dedicated file-editing tools instead.
@@ -1,4 +1,4 @@
1
- - Manage persistent terminals for long-running processes (dev servers, watchers, build processes)
1
+ - Manage interactive and persistent terminals (REPLs, prompts, dev servers, watchers)
2
2
  - Returns terminal information and output
3
3
  - Supports creating, reading, writing, listing, and killing terminals
4
4
 
@@ -7,7 +7,7 @@
7
7
  ### start
8
8
  - Spawns a new persistent terminal (interactive shell by default)
9
9
  - Returns terminal ID for future operations
10
- - Use for processes that need to stay alive (dev servers, watchers, logs)
10
+ - Use for commands that need stdin, a TTY, interactive input, or persistence across turns
11
11
  - Before starting, call `terminal(operation: "list")` to see if a matching service is already running
12
12
  - Provide a clear `purpose` or `title` (e.g. "web dev server port 9100") so humans can recognize it
13
13
  - Parameters:
@@ -51,22 +51,25 @@
51
51
  - Parameters:
52
52
  - terminalId (required): Terminal ID to kill
53
53
 
54
- ## When to Use Terminal vs Bash
54
+ ## When to Use Terminal vs Shell
55
55
 
56
56
  ### Use terminal for:
57
+ - Interactive commands that need stdin or a TTY: REPLs, prompts, shells, debuggers
57
58
  - Dev servers: npm run dev, bun dev
58
59
  - File watchers: bun test --watch, nodemon
59
60
  - Build watchers: bun build --watch
60
61
  - Log tailing: tail -f logs/app.log
61
62
  - Background services: docker compose up
62
63
  - Any process that needs to stay alive and produce continuous output
64
+ - Any process you need to read from or write to across turns
63
65
 
64
- ### Use bash for:
66
+ ### Use shell for:
65
67
  - Status checks: git status, ls, ps
66
68
  - One-off commands: mkdir, rm, curl
67
69
  - Quick scripts: bun run build, git commit
68
70
  - File operations: cat, sed, awk
69
- - Short-lived commands with immediate output
71
+ - Non-interactive long-running commands that finish on their own (set an appropriate timeout)
72
+ - Any command that does not need stdin, a TTY, or follow-up interaction
70
73
 
71
74
  ## Example Workflow
72
75
 
@@ -83,7 +86,7 @@
83
86
 
84
87
  ## Notes
85
88
 
86
- - Terminals persist across multiple LLM turns (unlike bash commands)
89
+ - Terminals persist across multiple LLM turns (unlike shell commands)
87
90
  - Maximum 10 terminals per session
88
91
  - Exited terminals auto-cleanup after 5 minutes
89
92
  - Output is buffered (last 500 lines kept)
@@ -4,7 +4,7 @@ import { finishTool } from './builtin/finish.ts';
4
4
  import { buildFsTools } from './builtin/fs/index.ts';
5
5
  import { buildGitTools } from './builtin/git.ts';
6
6
  import { progressUpdateTool } from './builtin/progress.ts';
7
- import { buildBashTool } from './builtin/bash.ts';
7
+ import { buildShellTool } from './builtin/shell.ts';
8
8
  import { buildRipgrepTool } from './builtin/ripgrep.ts';
9
9
  import { buildGlobTool } from './builtin/glob.ts';
10
10
  import { buildApplyPatchTool } from './builtin/patch.ts';
@@ -147,8 +147,8 @@ async function discoverStaticProjectTools(
147
147
  // Built-ins
148
148
  tools.set('finish', finishTool);
149
149
  tools.set('progress_update', progressUpdateTool);
150
- const bash = buildBashTool(projectRoot);
151
- tools.set(bash.name, bash.tool);
150
+ const shell = buildShellTool(projectRoot);
151
+ tools.set(shell.name, shell.tool);
152
152
  // Search
153
153
  const rg = buildRipgrepTool(projectRoot);
154
154
  tools.set(rg.name, rg.tool);
@@ -18,13 +18,13 @@ Pick the right tool for the job (each tool's description has its full contract):
18
18
 
19
19
  After making changes:
20
20
 
21
- 1. Run project-specific build/lint/test commands with `bash` (check `package.json`, `README.md`, or `AGENTS.md` for the right command).
21
+ 1. Run project-specific build/lint/test commands with `shell` (check `package.json`, `README.md`, or `AGENTS.md` for the right command).
22
22
  2. Review diffs with `git_status` / `git_diff`.
23
23
  3. Do NOT commit unless the user explicitly asks. It is very important to only commit when asked.
24
24
 
25
25
  ## Terminal tool — when to use
26
26
 
27
- - Prefer `terminal` over `bash` for long-lived processes (dev servers, watchers, log tailing). `bash` is for one-off commands with immediate output.
27
+ - Prefer `terminal` over `shell` for interactive or persistent processes (dev servers, watchers, log tailing). `shell` is for one-off non-interactive commands.
28
28
  - List existing terminals before starting a new one; reuse when possible to avoid duplicate services.
29
29
  - Give each terminal a clear `purpose` / `title` (e.g. "web dev server 9100").
30
30
  - Mention active terminals (purpose, command, port) in your responses so humans know what's running.
@@ -4,7 +4,7 @@ GUIDED MODE is active. This OVERRIDES all conciseness and brevity instructions.
4
4
  The user is NOT a developer. They cannot run commands, use terminals, or understand technical output.
5
5
 
6
6
  YOU MUST:
7
- - USE YOUR TOOLS to execute every action. Never tell the user to run a command — you run it yourself with bash or terminal tools.
7
+ - USE YOUR TOOLS to execute every action. Never tell the user to run a command — you run it yourself with shell or terminal tools.
8
8
  - NEVER ask permission or say "Want me to...?" — just do it immediately.
9
9
  - NEVER respond with just instructions or steps. Every response must include actual tool calls that do the work.
10
10
  - Check if services are already running before starting them. If not running, start them yourself.
@@ -12,7 +12,7 @@ YOU MUST:
12
12
  - Tell the user the exact URL to open and what they'll see there.
13
13
  - Explain what you DID (past tense) in simple language — not what they should do.
14
14
  - If something fails, fix it yourself silently. Only tell the user once it's resolved.
15
- - Use the terminal tool (not bash) for long-running services so they persist.
15
+ - Use the terminal tool (not shell) for interactive or persistent services so they persist.
16
16
  - Verbose, friendly responses are expected in this mode — ignore any "be concise" instructions.
17
17
 
18
18
  WRONG response: "Run `bun dev` in the project root, then open http://localhost:9100"
@@ -62,7 +62,7 @@ user: which file contains the implementation of foo?
62
62
  assistant: src/foo.c
63
63
  </example>
64
64
 
65
- When you run a non-trivial bash command — especially one that changes state — briefly explain what it does and why, so the user knows what's happening.
65
+ When you run a non-trivial shell command — especially one that changes state — briefly explain what it does and why, so the user knows what's happening.
66
66
 
67
67
  # Proactiveness
68
68
 
@@ -99,7 +99,7 @@ For software engineering requests (bugs, features, refactors, explanations):
99
99
  1. Use `update_todos` to plan multi-step work.
100
100
  2. Explore the codebase with the search tools (`glob`, `ripgrep`, `tree`, `read`). Batch independent searches in parallel.
101
101
  3. Implement the solution.
102
- 4. Verify — run the project's build/lint/test commands with `bash`. Check `README.md` / `AGENTS.md` to find the right command.
102
+ 4. Verify — run the project's build/lint/test commands with `shell`. Check `README.md` / `AGENTS.md` to find the right command.
103
103
  5. Review diffs with `git_status` / `git_diff`.
104
104
  6. NEVER commit unless the user explicitly asks.
105
105
 
@@ -27,7 +27,7 @@ You are a coding agent running in otto, a terminal-based coding assistant. Preci
27
27
  1. Understand — use `glob`, `ripgrep`, `tree`, `read` to map the code. Batch independent searches.
28
28
  2. Plan — use `update_todos` for multi-step work. Mark one step `in_progress` at a time.
29
29
  3. Implement — prefer the targeted editing tools available to you for small in-file changes; use patch-style edits for structural or multi-file changes when available; use `write` only for new files or near-total rewrites.
30
- 4. Verify — run project-specific build/lint/test commands via `bash`. Check `README.md` / `AGENTS.md` for the right command.
30
+ 4. Verify — run project-specific build/lint/test commands via `shell`. Check `README.md` / `AGENTS.md` for the right command.
31
31
  5. Review — `git_status` / `git_diff`. Do NOT commit unless asked.
32
32
 
33
33
  # Direct file references
@@ -39,7 +39,7 @@ Your reasoning is powerful, but it can override what you actually read. Guard ag
39
39
  1. Understand — use `glob`, `ripgrep`, `tree`, `read` to map the code. Batch independent searches.
40
40
  2. Plan — use `update_todos` for multi-step work. Mark one step `in_progress` at a time.
41
41
  3. Implement — prefer the targeted editing tools available to you for small in-file changes; use patch-style edits for structural or multi-file changes when available; use `write` only for new files or near-total rewrites.
42
- 4. Verify — run project-specific build/lint/test commands via `bash`. Check `README.md` / `AGENTS.md` for the right command.
42
+ 4. Verify — run project-specific build/lint/test commands via `shell`. Check `README.md` / `AGENTS.md` for the right command.
43
43
  5. Review — `git_status` / `git_diff`. Do NOT commit unless asked.
44
44
 
45
45
  # Direct file references
@@ -30,7 +30,7 @@ For bug fixes, features, refactors, or explanations:
30
30
  1. **Understand requirements** — features, UX, platform, constraints. Ask targeted clarification questions if critical info is missing.
31
31
  2. **Propose plan.** Present a concise high-level summary (type, core purpose, key technologies, main features, visual/UX approach). For visual assets, describe the strategy for placeholders (geometric shapes, procedural patterns, open-source assets).
32
32
  3. **User approval.** Obtain approval before implementing.
33
- 4. **Implement** autonomously. Scaffold with `bash` (`npm init`, `npx create-react-app`, etc.). Create placeholder assets when needed; aim for a visually coherent prototype.
33
+ 4. **Implement** autonomously. Scaffold with `shell` (`npm init`, `npx create-react-app`, etc.). Create placeholder assets when needed; aim for a visually coherent prototype.
34
34
  5. **Verify** against the original request and approved plan. Fix bugs, deviations, placeholders. Ensure the build produces no compile errors.
35
35
  6. **Solicit feedback** — provide start-up instructions and ask for feedback.
36
36
 
@@ -48,14 +48,14 @@ For bug fixes, features, refactors, or explanations:
48
48
 
49
49
  ## Security and safety
50
50
 
51
- - **Explain critical commands.** Before executing `bash` commands that modify the file system or system state, briefly explain purpose and potential impact. Don't ask permission — the user will confirm via dialog.
51
+ - **Explain critical commands.** Before executing `shell` commands that modify the file system or system state, briefly explain purpose and potential impact. Don't ask permission — the user will confirm via dialog.
52
52
  - **Security first.** Apply security best practices. Never expose, log, or commit secrets, API keys, or sensitive information.
53
53
 
54
54
  ## Tool usage
55
55
 
56
56
  - **Parallelism.** Execute multiple independent tool calls in parallel when feasible (e.g. codebase searches).
57
- - **Command execution.** Use `bash` for shell commands; explain modifying commands first.
58
- - **Background processes.** Use `&` for long-running processes that don't stop on their own (e.g. `node server.js &`). If unsure, ask. Prefer the `terminal` tool for processes you'll monitor over multiple turns.
57
+ - **Command execution.** Use `shell` for non-interactive shell commands; explain modifying commands first.
58
+ - **Background processes.** Prefer the `terminal` tool for interactive or persistent processes you'll monitor over multiple turns. Use `shell` only for non-interactive commands that finish on their own.
59
59
  - **Interactive commands.** Avoid commands that require user interaction (e.g. `git rebase -i`). Prefer non-interactive versions (`npm init -y`).
60
60
  - **Confirmations.** If the user cancels a tool call, respect their choice — don't retry unless they ask again.
61
61
 
@@ -78,7 +78,7 @@ model: [tool_call: ls for path '/path/to/project']
78
78
 
79
79
  <example>
80
80
  user: start the server implemented in server.js
81
- model: [tool_call: bash for 'node server.js &' because it must run in the background]
81
+ model: [tool_call: terminal for 'node server.js' because it must stay running]
82
82
  </example>
83
83
 
84
84
  <example>
@@ -105,7 +105,7 @@ user: Yes
105
105
  model:
106
106
  [tool_call: use the appropriate available file-editing tool to apply the refactoring to 'src/auth.py']
107
107
  Refactoring complete. Running verification...
108
- [tool_call: bash for 'ruff check src/auth.py && pytest']
108
+ [tool_call: shell for 'ruff check src/auth.py && pytest']
109
109
  (After verification passes)
110
110
  All checks passed. This is a stable checkpoint.
111
111
  </example>
@@ -125,7 +125,7 @@ Now I'll look for existing or related test files to understand current testing c
125
125
  (After reviewing existing tests and the file content)
126
126
  [tool_call: write to create /path/to/someFile.test.ts with the test code]
127
127
  I've written the tests. Now I'll run the project's test command to verify them.
128
- [tool_call: bash for 'npm run test']
128
+ [tool_call: shell for 'npm run test']
129
129
  </example>
130
130
 
131
131
  <example>
@@ -39,7 +39,7 @@ Your reasoning is powerful, but it can override what you actually read. Guard ag
39
39
  1. Understand — use `glob`, `ripgrep`, `tree`, `read` to map the code. Batch independent searches.
40
40
  2. Plan — use `update_todos` for multi-step work. Mark one step `in_progress` at a time.
41
41
  3. Implement — prefer the targeted editing tools available to you for small in-file changes; use patch-style edits for structural or multi-file changes when available; use `write` only for new files or near-total rewrites.
42
- 4. Verify — run project-specific build/lint/test commands via `bash`. Check `README.md` / `AGENTS.md` for the right command.
42
+ 4. Verify — run project-specific build/lint/test commands via `shell`. Check `README.md` / `AGENTS.md` for the right command.
43
43
  5. Review — `git_status` / `git_diff`. Do NOT commit unless asked.
44
44
 
45
45
  # Direct file references
@@ -881,6 +881,32 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
881
881
  output: 128000,
882
882
  },
883
883
  },
884
+ {
885
+ id: 'gpt-5.5',
886
+ ownedBy: 'openai',
887
+ label: 'GPT-5.5',
888
+ modalities: {
889
+ input: ['text', 'image', 'pdf'],
890
+ output: ['text'],
891
+ },
892
+ toolCall: true,
893
+ reasoningText: true,
894
+ attachment: true,
895
+ temperature: false,
896
+ knowledge: '2025-12-01',
897
+ releaseDate: '2026-04-23',
898
+ lastUpdated: '2026-04-23',
899
+ openWeights: false,
900
+ cost: {
901
+ input: 5,
902
+ output: 30,
903
+ cacheRead: 0.5,
904
+ },
905
+ limit: {
906
+ context: 1050000,
907
+ output: 130000,
908
+ },
909
+ },
884
910
  {
885
911
  id: 'gpt-image-1',
886
912
  ownedBy: 'openai',
@@ -2813,7 +2839,7 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
2813
2839
  },
2814
2840
  },
2815
2841
  {
2816
- id: 'gemma-4-26b-it',
2842
+ id: 'gemma-4-26b-a4b-it',
2817
2843
  ownedBy: 'google',
2818
2844
  label: 'Gemma 4 26B',
2819
2845
  modalities: {
@@ -4934,6 +4960,31 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
4934
4960
  output: 262144,
4935
4961
  },
4936
4962
  },
4963
+ {
4964
+ id: 'moonshotai/kimi-k2.6',
4965
+ ownedBy: 'moonshot',
4966
+ label: 'Kimi K2.6',
4967
+ modalities: {
4968
+ input: ['text', 'image'],
4969
+ output: ['text'],
4970
+ },
4971
+ toolCall: true,
4972
+ reasoningText: true,
4973
+ attachment: true,
4974
+ temperature: true,
4975
+ releaseDate: '2026-04-20',
4976
+ lastUpdated: '2026-04-20',
4977
+ openWeights: true,
4978
+ cost: {
4979
+ input: 0.95,
4980
+ output: 4,
4981
+ cacheRead: 0.16,
4982
+ },
4983
+ limit: {
4984
+ context: 262144,
4985
+ output: 262144,
4986
+ },
4987
+ },
4937
4988
  {
4938
4989
  id: 'nousresearch/hermes-3-llama-3.1-405b:free',
4939
4990
  label: 'Hermes 3 405B Instruct (free)',
@@ -5707,9 +5758,9 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
5707
5758
  lastUpdated: '2026-03-17',
5708
5759
  openWeights: false,
5709
5760
  cost: {
5710
- input: 7.5e-7,
5711
- output: 0.0000045,
5712
- cacheRead: 7.5e-8,
5761
+ input: 0.75,
5762
+ output: 4.5,
5763
+ cacheRead: 0.075,
5713
5764
  },
5714
5765
  limit: {
5715
5766
  context: 400000,
@@ -5733,9 +5784,9 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
5733
5784
  lastUpdated: '2026-03-17',
5734
5785
  openWeights: false,
5735
5786
  cost: {
5736
- input: 2e-7,
5737
- output: 0.00000125,
5738
- cacheRead: 2e-8,
5787
+ input: 0.2,
5788
+ output: 1.25,
5789
+ cacheRead: 0.02,
5739
5790
  },
5740
5791
  limit: {
5741
5792
  context: 400000,
@@ -8141,6 +8192,31 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
8141
8192
  output: 256000,
8142
8193
  },
8143
8194
  },
8195
+ {
8196
+ id: 'hy3-preview-free',
8197
+ label: 'Hy3 preview Free',
8198
+ modalities: {
8199
+ input: ['text'],
8200
+ output: ['text'],
8201
+ },
8202
+ toolCall: true,
8203
+ reasoningText: true,
8204
+ attachment: false,
8205
+ temperature: true,
8206
+ knowledge: '2025-06',
8207
+ releaseDate: '2026-04-20',
8208
+ lastUpdated: '2026-04-20',
8209
+ openWeights: true,
8210
+ cost: {
8211
+ input: 0,
8212
+ output: 0,
8213
+ cacheRead: 0,
8214
+ },
8215
+ limit: {
8216
+ context: 256000,
8217
+ output: 64000,
8218
+ },
8219
+ },
8144
8220
  {
8145
8221
  id: 'kimi-k2',
8146
8222
  ownedBy: 'moonshot',
@@ -8245,6 +8321,56 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
8245
8321
  output: 262144,
8246
8322
  },
8247
8323
  },
8324
+ {
8325
+ id: 'kimi-k2.6',
8326
+ ownedBy: 'moonshot',
8327
+ label: 'Kimi K2.6',
8328
+ modalities: {
8329
+ input: ['text', 'image', 'video'],
8330
+ output: ['text'],
8331
+ },
8332
+ toolCall: true,
8333
+ reasoningText: true,
8334
+ attachment: true,
8335
+ temperature: true,
8336
+ knowledge: '2024-10',
8337
+ releaseDate: '2026-04-21',
8338
+ lastUpdated: '2026-04-21',
8339
+ openWeights: true,
8340
+ cost: {
8341
+ input: 0.95,
8342
+ output: 4,
8343
+ cacheRead: 0.16,
8344
+ },
8345
+ limit: {
8346
+ context: 262144,
8347
+ output: 65536,
8348
+ },
8349
+ },
8350
+ {
8351
+ id: 'ling-2.6-flash-free',
8352
+ label: 'Ling 2.6 Flash Free',
8353
+ modalities: {
8354
+ input: ['text'],
8355
+ output: ['text'],
8356
+ },
8357
+ toolCall: true,
8358
+ reasoningText: false,
8359
+ attachment: false,
8360
+ temperature: true,
8361
+ knowledge: '2025-06',
8362
+ releaseDate: '2026-04-21',
8363
+ lastUpdated: '2026-04-21',
8364
+ openWeights: true,
8365
+ cost: {
8366
+ input: 0,
8367
+ output: 0,
8368
+ },
8369
+ limit: {
8370
+ context: 262100,
8371
+ output: 32800,
8372
+ },
8373
+ },
8248
8374
  {
8249
8375
  id: 'mimo-v2-flash-free',
8250
8376
  label: 'MiMo V2 Flash Free',
@@ -8430,6 +8556,32 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
8430
8556
  npm: '@ai-sdk/anthropic',
8431
8557
  },
8432
8558
  },
8559
+ {
8560
+ id: 'minimax-m2.7',
8561
+ ownedBy: 'minimax',
8562
+ label: 'MiniMax M2.7',
8563
+ modalities: {
8564
+ input: ['text'],
8565
+ output: ['text'],
8566
+ },
8567
+ toolCall: true,
8568
+ reasoningText: true,
8569
+ attachment: false,
8570
+ temperature: true,
8571
+ knowledge: '2025-01',
8572
+ releaseDate: '2026-03-18',
8573
+ lastUpdated: '2026-03-18',
8574
+ openWeights: true,
8575
+ cost: {
8576
+ input: 0.3,
8577
+ output: 1.2,
8578
+ cacheRead: 0.06,
8579
+ },
8580
+ limit: {
8581
+ context: 204800,
8582
+ output: 131072,
8583
+ },
8584
+ },
8433
8585
  {
8434
8586
  id: 'nemotron-3-super-free',
8435
8587
  label: 'Nemotron 3 Super Free',
@@ -8505,7 +8657,7 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
8505
8657
  output: 65536,
8506
8658
  },
8507
8659
  provider: {
8508
- npm: '@ai-sdk/alibaba',
8660
+ npm: '@ai-sdk/anthropic',
8509
8661
  },
8510
8662
  },
8511
8663
  {
@@ -8534,7 +8686,7 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
8534
8686
  output: 65536,
8535
8687
  },
8536
8688
  provider: {
8537
- npm: '@ai-sdk/alibaba',
8689
+ npm: '@ai-sdk/anthropic',
8538
8690
  },
8539
8691
  },
8540
8692
  {
@@ -9458,6 +9610,32 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
9458
9610
  output: 262144,
9459
9611
  },
9460
9612
  },
9613
+ {
9614
+ id: 'kimi-k2.6',
9615
+ ownedBy: 'moonshot',
9616
+ label: 'Kimi K2.6',
9617
+ modalities: {
9618
+ input: ['text', 'image', 'video'],
9619
+ output: ['text'],
9620
+ },
9621
+ toolCall: true,
9622
+ reasoningText: true,
9623
+ attachment: true,
9624
+ temperature: true,
9625
+ knowledge: '2025-01',
9626
+ releaseDate: '2026-04-21',
9627
+ lastUpdated: '2026-04-21',
9628
+ openWeights: true,
9629
+ cost: {
9630
+ input: 0.95,
9631
+ output: 4,
9632
+ cacheRead: 0.16,
9633
+ },
9634
+ limit: {
9635
+ context: 262144,
9636
+ output: 262144,
9637
+ },
9638
+ },
9461
9639
  ],
9462
9640
  label: 'Moonshot AI',
9463
9641
  env: ['MOONSHOT_API_KEY'],
@@ -21,6 +21,7 @@ const OAUTH_MODEL_IDS: Partial<Record<ProviderId, string[]>> = {
21
21
  'gpt-5.3-codex',
22
22
  'gpt-5.4',
23
23
  'gpt-5.4-mini',
24
+ 'gpt-5.5',
24
25
  ],
25
26
  };
26
27
 
@@ -1,12 +0,0 @@
1
- - Execute a one-off shell command using `bash -lc`
2
- - Returns `stdout`, `stderr`, and `exitCode`
3
- - `cwd` is relative to the project root and sandboxed within it
4
-
5
- **Use `bash` for short-lived commands.** For long-running processes (dev servers, watchers, log tailing) use the `terminal` tool instead — terminals persist across turns.
6
-
7
- ## Usage tips
8
-
9
- - Chain commands with `&&` to fail-fast.
10
- - For long outputs, redirect to a file and `read` it back.
11
- - Batch independent checks (e.g. `git status && git diff`) in parallel tool calls rather than sequential bash chains when you need results separately.
12
- - Never use `bash` with `sed`/`awk` for programmatic file editing — use the dedicated file-editing tools instead.