@ottocode/sdk 0.1.193 → 0.1.194

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.193",
3
+ "version": "0.1.194",
4
4
  "description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
5
5
  "author": "nitishxyz",
6
6
  "license": "MIT",
@@ -30,6 +30,16 @@ function resolveSafePath(projectRoot: string, p: string) {
30
30
  return abs;
31
31
  }
32
32
 
33
+ function killProcessTree(pid: number) {
34
+ try {
35
+ process.kill(-pid, 'SIGTERM');
36
+ } catch {
37
+ try {
38
+ process.kill(pid, 'SIGTERM');
39
+ } catch {}
40
+ }
41
+ }
42
+
33
43
  export function buildBashTool(projectRoot: string): {
34
44
  name: string;
35
45
  tool: Tool;
@@ -55,23 +65,34 @@ export function buildBashTool(projectRoot: string): {
55
65
  .describe('Timeout in milliseconds (default: 300000 = 5 minutes)'),
56
66
  })
57
67
  .strict(),
58
- async execute({
59
- cmd,
60
- cwd,
61
- allowNonZeroExit,
62
- timeout = 300000,
63
- }: {
64
- cmd: string;
65
- cwd?: string;
66
- allowNonZeroExit?: boolean;
67
- timeout?: number;
68
- }): Promise<
68
+ async execute(
69
+ {
70
+ cmd,
71
+ cwd,
72
+ allowNonZeroExit,
73
+ timeout = 300000,
74
+ }: {
75
+ cmd: string;
76
+ cwd?: string;
77
+ allowNonZeroExit?: boolean;
78
+ timeout?: number;
79
+ },
80
+ options?: { abortSignal?: AbortSignal },
81
+ ): Promise<
69
82
  ToolResponse<{
70
83
  exitCode: number;
71
84
  stdout: string;
72
85
  stderr: string;
73
86
  }>
74
87
  > {
88
+ const abortSignal = options?.abortSignal;
89
+
90
+ if (abortSignal?.aborted) {
91
+ return createToolError('Command aborted before execution', 'abort', {
92
+ cmd,
93
+ });
94
+ }
95
+
75
96
  const absCwd = resolveSafePath(projectRoot, cwd || '.');
76
97
 
77
98
  return new Promise((resolve) => {
@@ -80,17 +101,48 @@ export function buildBashTool(projectRoot: string): {
80
101
  shell: true,
81
102
  stdio: ['ignore', 'pipe', 'pipe'],
82
103
  env: { ...process.env, PATH: getAugmentedPath() },
104
+ detached: true,
83
105
  });
84
106
 
85
107
  let stdout = '';
86
108
  let stderr = '';
87
109
  let didTimeout = false;
110
+ let didAbort = false;
111
+ let settled = false;
88
112
  let timeoutId: ReturnType<typeof setTimeout> | null = null;
89
113
 
114
+ const settle = (
115
+ result: ToolResponse<{
116
+ exitCode: number;
117
+ stdout: string;
118
+ stderr: string;
119
+ }>,
120
+ ) => {
121
+ if (settled) return;
122
+ settled = true;
123
+ if (timeoutId) clearTimeout(timeoutId);
124
+ if (abortSignal) {
125
+ abortSignal.removeEventListener('abort', onAbort);
126
+ }
127
+ resolve(result);
128
+ };
129
+
130
+ const onAbort = () => {
131
+ if (settled) return;
132
+ didAbort = true;
133
+ if (proc.pid) killProcessTree(proc.pid);
134
+ else proc.kill('SIGTERM');
135
+ };
136
+
137
+ if (abortSignal) {
138
+ abortSignal.addEventListener('abort', onAbort, { once: true });
139
+ }
140
+
90
141
  if (timeout > 0) {
91
142
  timeoutId = setTimeout(() => {
92
143
  didTimeout = true;
93
- proc.kill();
144
+ if (proc.pid) killProcessTree(proc.pid);
145
+ else proc.kill();
94
146
  }, timeout);
95
147
  }
96
148
 
@@ -103,10 +155,16 @@ export function buildBashTool(projectRoot: string): {
103
155
  });
104
156
 
105
157
  proc.on('close', (exitCode) => {
106
- if (timeoutId) clearTimeout(timeoutId);
107
-
108
- if (didTimeout) {
109
- resolve(
158
+ if (didAbort) {
159
+ settle(
160
+ createToolError(`Command aborted by user: ${cmd}`, 'abort', {
161
+ cmd,
162
+ stdout,
163
+ stderr,
164
+ }),
165
+ );
166
+ } else if (didTimeout) {
167
+ settle(
110
168
  createToolError(
111
169
  `Command timed out after ${timeout}ms: ${cmd}`,
112
170
  'timeout',
@@ -120,7 +178,7 @@ export function buildBashTool(projectRoot: string): {
120
178
  } else if (exitCode !== 0 && !allowNonZeroExit) {
121
179
  const errorDetail = stderr.trim() || stdout.trim() || '';
122
180
  const errorMsg = `Command failed with exit code ${exitCode}${errorDetail ? `\n\n${errorDetail}` : ''}`;
123
- resolve(
181
+ settle(
124
182
  createToolError(errorMsg, 'execution', {
125
183
  exitCode,
126
184
  stdout,
@@ -131,7 +189,7 @@ export function buildBashTool(projectRoot: string): {
131
189
  }),
132
190
  );
133
191
  } else {
134
- resolve({
192
+ settle({
135
193
  ok: true,
136
194
  exitCode: exitCode ?? 0,
137
195
  stdout,
@@ -141,8 +199,7 @@ export function buildBashTool(projectRoot: string): {
141
199
  });
142
200
 
143
201
  proc.on('error', (err) => {
144
- if (timeoutId) clearTimeout(timeoutId);
145
- resolve(
202
+ settle(
146
203
  createToolError(
147
204
  `Command execution failed: ${err.message}`,
148
205
  'execution',
@@ -4,7 +4,8 @@ export type ToolErrorType =
4
4
  | 'permission'
5
5
  | 'execution'
6
6
  | 'timeout'
7
- | 'unsupported';
7
+ | 'unsupported'
8
+ | 'abort';
8
9
 
9
10
  export type ToolErrorResponse = {
10
11
  ok: false;
@@ -30,7 +30,7 @@ const PREFERRED_FAST_MODELS: Partial<Record<ProviderId, string[]>> = {
30
30
  google: ['gemini-2.0-flash-lite'],
31
31
  openrouter: ['anthropic/claude-3.5-haiku'],
32
32
  opencode: ['claude-3-5-haiku'],
33
- setu: ['kimi-k2'],
33
+ setu: ['kimi-k2-turbo-preview'],
34
34
  zai: ['glm-4.5-flash'],
35
35
  copilot: ['gpt-4.1-mini'],
36
36
  };