@ottocode/server 0.1.265 → 0.1.266
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 +3 -3
- package/src/routes/auth/copilot.ts +699 -0
- package/src/routes/auth/oauth.ts +578 -0
- package/src/routes/auth/onboarding.ts +45 -0
- package/src/routes/auth/providers.ts +189 -0
- package/src/routes/auth/service.ts +167 -0
- package/src/routes/auth/state.ts +23 -0
- package/src/routes/auth/status.ts +203 -0
- package/src/routes/auth/wallet.ts +229 -0
- package/src/routes/auth.ts +12 -2080
- package/src/routes/config/models-service.ts +411 -0
- package/src/routes/config/models.ts +6 -426
- package/src/routes/config/providers-service.ts +237 -0
- package/src/routes/config/providers.ts +10 -242
- package/src/routes/files/handlers.ts +297 -0
- package/src/routes/files/service.ts +313 -0
- package/src/routes/files.ts +12 -608
- package/src/routes/git/commit-service.ts +207 -0
- package/src/routes/git/commit.ts +6 -220
- package/src/routes/git/remote-service.ts +116 -0
- package/src/routes/git/remote.ts +8 -115
- package/src/routes/git/staging-service.ts +111 -0
- package/src/routes/git/staging.ts +10 -205
- package/src/routes/mcp/auth.ts +338 -0
- package/src/routes/mcp/lifecycle.ts +263 -0
- package/src/routes/mcp/servers.ts +212 -0
- package/src/routes/mcp/service.ts +664 -0
- package/src/routes/mcp/state.ts +13 -0
- package/src/routes/mcp.ts +6 -1233
- package/src/routes/ottorouter/billing.ts +593 -0
- package/src/routes/ottorouter/service.ts +92 -0
- package/src/routes/ottorouter/topup.ts +301 -0
- package/src/routes/ottorouter/wallet.ts +370 -0
- package/src/routes/ottorouter.ts +6 -1319
- package/src/routes/research/service.ts +339 -0
- package/src/routes/research.ts +12 -390
- package/src/routes/sessions/crud.ts +563 -0
- package/src/routes/sessions/queue.ts +242 -0
- package/src/routes/sessions/retry.ts +121 -0
- package/src/routes/sessions/service.ts +768 -0
- package/src/routes/sessions/share.ts +434 -0
- package/src/routes/sessions.ts +8 -1977
- package/src/routes/skills/service.ts +221 -0
- package/src/routes/skills/spec.ts +309 -0
- package/src/routes/skills.ts +31 -909
- package/src/routes/terminals/service.ts +326 -0
- package/src/routes/terminals.ts +19 -295
- package/src/routes/tunnel/service.ts +217 -0
- package/src/routes/tunnel.ts +29 -219
- package/src/runtime/agent/registry-prompts.ts +147 -0
- package/src/runtime/agent/registry.ts +6 -124
- package/src/runtime/agent/runner-errors.ts +116 -0
- package/src/runtime/agent/runner-reminders.ts +45 -0
- package/src/runtime/agent/runner-setup-model.ts +75 -0
- package/src/runtime/agent/runner-setup-prompt.ts +185 -0
- package/src/runtime/agent/runner-setup-tools.ts +103 -0
- package/src/runtime/agent/runner-setup-utils.ts +21 -0
- package/src/runtime/agent/runner-setup.ts +54 -288
- package/src/runtime/agent/runner-telemetry.ts +112 -0
- package/src/runtime/agent/runner-text.ts +108 -0
- package/src/runtime/agent/runner-tool-observer.ts +86 -0
- package/src/runtime/agent/runner.ts +79 -378
- package/src/runtime/provider/custom.ts +73 -0
- package/src/runtime/provider/index.ts +2 -85
- package/src/runtime/provider/reasoning-builders.ts +280 -0
- package/src/runtime/provider/reasoning.ts +67 -264
- package/src/tools/adapter/events.ts +116 -0
- package/src/tools/adapter/execution.ts +160 -0
- package/src/tools/adapter/pending.ts +37 -0
- package/src/tools/adapter/persistence.ts +166 -0
- package/src/tools/adapter/results.ts +97 -0
- package/src/tools/adapter.ts +124 -451
package/src/tools/adapter.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import type { Tool } from 'ai';
|
|
2
|
-
import {
|
|
3
|
-
import { eq } from 'drizzle-orm';
|
|
4
|
-
import { publish } from '../events/bus.ts';
|
|
5
|
-
import { logger, type DiscoveredTool } from '@ottocode/sdk';
|
|
6
|
-
import { getCwd, setCwd, joinRelative } from '../runtime/utils/cwd.ts';
|
|
2
|
+
import type { DiscoveredTool } from '@ottocode/sdk';
|
|
7
3
|
import type {
|
|
8
4
|
ToolAdapterContext,
|
|
9
5
|
StepExecutionState,
|
|
@@ -19,6 +15,37 @@ import {
|
|
|
19
15
|
skipsGuardApproval,
|
|
20
16
|
} from '../runtime/tools/approval.ts';
|
|
21
17
|
import { guardToolCall } from '../runtime/tools/guards.ts';
|
|
18
|
+
import { consumeToolStream, executeBaseTool } from './adapter/execution.ts';
|
|
19
|
+
import {
|
|
20
|
+
logToolCall,
|
|
21
|
+
logToolResult,
|
|
22
|
+
publishPlanUpdated,
|
|
23
|
+
publishToolCall,
|
|
24
|
+
publishToolDelta,
|
|
25
|
+
publishToolResult,
|
|
26
|
+
} from './adapter/events.ts';
|
|
27
|
+
import {
|
|
28
|
+
computeToolTiming,
|
|
29
|
+
persistToolCall,
|
|
30
|
+
persistToolErrorResult,
|
|
31
|
+
persistToolResultWithIndex,
|
|
32
|
+
updateToolSessionStats,
|
|
33
|
+
} from './adapter/persistence.ts';
|
|
34
|
+
import {
|
|
35
|
+
extractToolCallId,
|
|
36
|
+
getPendingQueue,
|
|
37
|
+
shiftPendingCall,
|
|
38
|
+
type PendingCallMeta,
|
|
39
|
+
} from './adapter/pending.ts';
|
|
40
|
+
import {
|
|
41
|
+
buildToolResultContent,
|
|
42
|
+
createBlockedToolResult,
|
|
43
|
+
createRejectedToolResult,
|
|
44
|
+
createToolExceptionResult,
|
|
45
|
+
markToolFailed,
|
|
46
|
+
markToolSucceeded,
|
|
47
|
+
type ToolFailureState,
|
|
48
|
+
} from './adapter/results.ts';
|
|
22
49
|
|
|
23
50
|
export type { ToolAdapterContext } from '../runtime/tools/context.ts';
|
|
24
51
|
|
|
@@ -34,57 +61,6 @@ type ToolExecuteOptions = ToolExecuteSignature['options'] extends never
|
|
|
34
61
|
: ToolExecuteSignature['options'];
|
|
35
62
|
type ToolExecuteReturn = ToolExecuteSignature['result'];
|
|
36
63
|
|
|
37
|
-
type PendingCallMeta = {
|
|
38
|
-
callId: string;
|
|
39
|
-
startTs: number;
|
|
40
|
-
stepIndex?: number;
|
|
41
|
-
args?: unknown;
|
|
42
|
-
approvalPromise?: Promise<boolean>;
|
|
43
|
-
blocked?: boolean;
|
|
44
|
-
blockReason?: string;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
function getPendingQueue(
|
|
48
|
-
map: Map<string, PendingCallMeta[]>,
|
|
49
|
-
name: string,
|
|
50
|
-
): PendingCallMeta[] {
|
|
51
|
-
let queue = map.get(name);
|
|
52
|
-
if (!queue) {
|
|
53
|
-
queue = [];
|
|
54
|
-
map.set(name, queue);
|
|
55
|
-
}
|
|
56
|
-
return queue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function extractToolCallId(options: unknown): string | undefined {
|
|
60
|
-
return (options as { toolCallId?: string } | undefined)?.toolCallId;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const DEFAULT_TRACED_TOOL_INPUTS = new Set([
|
|
64
|
-
'write',
|
|
65
|
-
'edit',
|
|
66
|
-
'multiedit',
|
|
67
|
-
'copy_into',
|
|
68
|
-
'apply_patch',
|
|
69
|
-
]);
|
|
70
|
-
|
|
71
|
-
function shouldTraceToolInput(name: string): boolean {
|
|
72
|
-
void DEFAULT_TRACED_TOOL_INPUTS;
|
|
73
|
-
void name;
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function summarizeTraceValue(value: unknown, max = 160): string {
|
|
78
|
-
try {
|
|
79
|
-
const json = JSON.stringify(value);
|
|
80
|
-
if (typeof json === 'string') {
|
|
81
|
-
return json.length > max ? `${json.slice(0, max)}…` : json;
|
|
82
|
-
}
|
|
83
|
-
} catch {}
|
|
84
|
-
const fallback = String(value);
|
|
85
|
-
return fallback.length > max ? `${fallback.slice(0, max)}…` : fallback;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
64
|
function unwrapDoubleWrappedArgs(
|
|
89
65
|
input: unknown,
|
|
90
66
|
expectedName: string,
|
|
@@ -117,7 +93,7 @@ export function adaptTools(
|
|
|
117
93
|
) {
|
|
118
94
|
const out: Record<string, Tool> = {};
|
|
119
95
|
const pendingCalls = new Map<string, PendingCallMeta[]>();
|
|
120
|
-
const failureState:
|
|
96
|
+
const failureState: ToolFailureState = {
|
|
121
97
|
active: false,
|
|
122
98
|
toolName: undefined,
|
|
123
99
|
};
|
|
@@ -150,67 +126,6 @@ export function adaptTools(
|
|
|
150
126
|
|
|
151
127
|
const processedToolErrors = new WeakSet<object>();
|
|
152
128
|
|
|
153
|
-
const persistToolErrorResult = async (
|
|
154
|
-
errorResult: unknown,
|
|
155
|
-
{
|
|
156
|
-
callId,
|
|
157
|
-
startTs,
|
|
158
|
-
stepIndexForEvent,
|
|
159
|
-
args,
|
|
160
|
-
}: {
|
|
161
|
-
callId?: string;
|
|
162
|
-
startTs?: number;
|
|
163
|
-
stepIndexForEvent?: number;
|
|
164
|
-
args?: unknown;
|
|
165
|
-
},
|
|
166
|
-
) => {
|
|
167
|
-
const resultPartId = crypto.randomUUID();
|
|
168
|
-
const endTs = Date.now();
|
|
169
|
-
const effectiveStepIndex = stepIndexForEvent ?? ctx.stepIndex;
|
|
170
|
-
const dur =
|
|
171
|
-
typeof startTs === 'number' ? Math.max(0, endTs - startTs) : null;
|
|
172
|
-
|
|
173
|
-
const contentObj: {
|
|
174
|
-
name: string;
|
|
175
|
-
result: unknown;
|
|
176
|
-
callId?: string;
|
|
177
|
-
args?: unknown;
|
|
178
|
-
} = {
|
|
179
|
-
name,
|
|
180
|
-
result: errorResult,
|
|
181
|
-
callId,
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
if (args !== undefined) {
|
|
185
|
-
contentObj.args = args;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const index = await ctx.nextIndex();
|
|
189
|
-
|
|
190
|
-
await ctx.db.insert(messageParts).values({
|
|
191
|
-
id: resultPartId,
|
|
192
|
-
messageId: ctx.messageId,
|
|
193
|
-
index,
|
|
194
|
-
stepIndex: effectiveStepIndex,
|
|
195
|
-
type: 'tool_result',
|
|
196
|
-
content: JSON.stringify(contentObj),
|
|
197
|
-
agent: ctx.agent,
|
|
198
|
-
provider: ctx.provider,
|
|
199
|
-
model: ctx.model,
|
|
200
|
-
startedAt: startTs,
|
|
201
|
-
completedAt: endTs,
|
|
202
|
-
toolName: name,
|
|
203
|
-
toolCallId: callId,
|
|
204
|
-
toolDurationMs: dur ?? undefined,
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
publish({
|
|
208
|
-
type: 'tool.result',
|
|
209
|
-
sessionId: ctx.sessionId,
|
|
210
|
-
payload: { ...contentObj, stepIndex: effectiveStepIndex },
|
|
211
|
-
});
|
|
212
|
-
};
|
|
213
|
-
|
|
214
129
|
// Add cache control for Anthropic to cache tool definitions (max 2 tools)
|
|
215
130
|
const shouldCache =
|
|
216
131
|
provider === 'anthropic' &&
|
|
@@ -236,9 +151,6 @@ export function adaptTools(
|
|
|
236
151
|
startTs: Date.now(),
|
|
237
152
|
stepIndex: ctx.stepIndex,
|
|
238
153
|
});
|
|
239
|
-
if (shouldTraceToolInput(name)) {
|
|
240
|
-
void (sdkCallId ?? queue[queue.length - 1]?.callId ?? 'unknown');
|
|
241
|
-
}
|
|
242
154
|
if (typeof base.onInputStart === 'function')
|
|
243
155
|
// biome-ignore lint/suspicious/noExplicitAny: AI SDK types are complex
|
|
244
156
|
await base.onInputStart(options as any);
|
|
@@ -246,25 +158,15 @@ export function adaptTools(
|
|
|
246
158
|
async onInputDelta(options: unknown) {
|
|
247
159
|
const delta = (options as { inputTextDelta?: string } | undefined)
|
|
248
160
|
?.inputTextDelta;
|
|
249
|
-
const sdkCallId = extractToolCallId(options);
|
|
250
161
|
const queue = pendingCalls.get(name);
|
|
251
162
|
const meta = queue?.length ? queue[queue.length - 1] : undefined;
|
|
252
|
-
if (shouldTraceToolInput(name)) {
|
|
253
|
-
void (sdkCallId ?? meta?.callId ?? 'unknown');
|
|
254
|
-
void summarizeTraceValue(delta ?? '');
|
|
255
|
-
}
|
|
256
163
|
// Stream tool argument deltas as events if needed
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
delta,
|
|
264
|
-
stepIndex: meta?.stepIndex ?? ctx.stepIndex,
|
|
265
|
-
callId: meta?.callId,
|
|
266
|
-
messageId: ctx.messageId,
|
|
267
|
-
},
|
|
164
|
+
publishToolDelta(ctx, {
|
|
165
|
+
name,
|
|
166
|
+
channel: 'input',
|
|
167
|
+
delta,
|
|
168
|
+
stepIndex: meta?.stepIndex ?? ctx.stepIndex,
|
|
169
|
+
callId: meta?.callId,
|
|
268
170
|
});
|
|
269
171
|
if (typeof base.onInputDelta === 'function')
|
|
270
172
|
// biome-ignore lint/suspicious/noExplicitAny: AI SDK types are complex
|
|
@@ -291,10 +193,6 @@ export function adaptTools(
|
|
|
291
193
|
const callId = meta.callId;
|
|
292
194
|
const callPartId = crypto.randomUUID();
|
|
293
195
|
const startTs = meta.startTs;
|
|
294
|
-
if (shouldTraceToolInput(name)) {
|
|
295
|
-
void callId;
|
|
296
|
-
void summarizeTraceValue(args);
|
|
297
|
-
}
|
|
298
196
|
|
|
299
197
|
if (
|
|
300
198
|
!firstToolCallReported &&
|
|
@@ -308,40 +206,22 @@ export function adaptTools(
|
|
|
308
206
|
|
|
309
207
|
// Special-case: progress updates must render instantly. Publish before any DB work.
|
|
310
208
|
if (name === 'progress_update') {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
payload: {
|
|
315
|
-
name,
|
|
316
|
-
args,
|
|
317
|
-
callId,
|
|
318
|
-
stepIndex: ctx.stepIndex,
|
|
319
|
-
messageId: ctx.messageId,
|
|
320
|
-
},
|
|
321
|
-
});
|
|
322
|
-
logger.debug(`[tools] call ${name}`, {
|
|
323
|
-
sessionId: ctx.sessionId,
|
|
324
|
-
messageId: ctx.messageId,
|
|
325
|
-
toolName: name,
|
|
209
|
+
publishToolCall(ctx, {
|
|
210
|
+
name,
|
|
211
|
+
input: args,
|
|
326
212
|
callId,
|
|
327
213
|
stepIndex: ctx.stepIndex,
|
|
328
214
|
});
|
|
215
|
+
logToolCall(ctx, { name, callId, stepIndex: ctx.stepIndex });
|
|
329
216
|
// Persist synchronously to maintain correct ordering
|
|
330
217
|
try {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
218
|
+
await persistToolCall(ctx, {
|
|
219
|
+
partId: callPartId,
|
|
220
|
+
name,
|
|
221
|
+
input: args,
|
|
222
|
+
callId,
|
|
223
|
+
startTs,
|
|
336
224
|
stepIndex: ctx.stepIndex,
|
|
337
|
-
type: 'tool_call',
|
|
338
|
-
content: JSON.stringify({ name, args, callId }),
|
|
339
|
-
agent: ctx.agent,
|
|
340
|
-
provider: ctx.provider,
|
|
341
|
-
model: ctx.model,
|
|
342
|
-
startedAt: startTs,
|
|
343
|
-
toolName: name,
|
|
344
|
-
toolCallId: callId,
|
|
345
225
|
});
|
|
346
226
|
} catch {}
|
|
347
227
|
if (typeof base.onInputAvailable === 'function') {
|
|
@@ -352,33 +232,21 @@ export function adaptTools(
|
|
|
352
232
|
}
|
|
353
233
|
|
|
354
234
|
// Publish promptly so UI shows the call header before results
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
args,
|
|
361
|
-
callId,
|
|
362
|
-
stepIndex: ctx.stepIndex,
|
|
363
|
-
messageId: ctx.messageId,
|
|
364
|
-
},
|
|
235
|
+
publishToolCall(ctx, {
|
|
236
|
+
name,
|
|
237
|
+
input: args,
|
|
238
|
+
callId,
|
|
239
|
+
stepIndex: ctx.stepIndex,
|
|
365
240
|
});
|
|
366
241
|
// Persist synchronously to maintain correct ordering
|
|
367
242
|
try {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
243
|
+
await persistToolCall(ctx, {
|
|
244
|
+
partId: callPartId,
|
|
245
|
+
name,
|
|
246
|
+
input: args,
|
|
247
|
+
callId,
|
|
248
|
+
startTs,
|
|
373
249
|
stepIndex: ctx.stepIndex,
|
|
374
|
-
type: 'tool_call',
|
|
375
|
-
content: JSON.stringify({ name, args, callId }),
|
|
376
|
-
agent: ctx.agent,
|
|
377
|
-
provider: ctx.provider,
|
|
378
|
-
model: ctx.model,
|
|
379
|
-
startedAt: startTs,
|
|
380
|
-
toolName: name,
|
|
381
|
-
toolCallId: callId,
|
|
382
250
|
});
|
|
383
251
|
} catch {}
|
|
384
252
|
// Start approval request with full args
|
|
@@ -421,9 +289,7 @@ export function adaptTools(
|
|
|
421
289
|
async execute(input: ToolExecuteInput, options: ToolExecuteOptions) {
|
|
422
290
|
input = unwrapDoubleWrappedArgs(input, name);
|
|
423
291
|
const sdkCallId = extractToolCallId(options);
|
|
424
|
-
const
|
|
425
|
-
const meta = queue?.shift();
|
|
426
|
-
if (queue && queue.length === 0) pendingCalls.delete(name);
|
|
292
|
+
const meta = shiftPendingCall(pendingCalls, name);
|
|
427
293
|
const callIdFromQueue = sdkCallId || meta?.callId;
|
|
428
294
|
const startTsFromQueue = meta?.startTs;
|
|
429
295
|
const stepIndexForEvent = meta?.stepIndex ?? ctx.stepIndex;
|
|
@@ -446,16 +312,14 @@ export function adaptTools(
|
|
|
446
312
|
const executeWithGuards = async (): Promise<ToolExecuteReturn> => {
|
|
447
313
|
try {
|
|
448
314
|
if (meta?.blocked) {
|
|
449
|
-
const blockedResult =
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
};
|
|
454
|
-
await persistToolErrorResult(blockedResult, {
|
|
315
|
+
const blockedResult = createBlockedToolResult(meta.blockReason);
|
|
316
|
+
await persistToolErrorResult(ctx, {
|
|
317
|
+
name,
|
|
318
|
+
errorResult: blockedResult,
|
|
455
319
|
callId: callIdFromQueue,
|
|
456
320
|
startTs: startTsFromQueue,
|
|
457
321
|
stepIndexForEvent,
|
|
458
|
-
|
|
322
|
+
input: meta?.args,
|
|
459
323
|
});
|
|
460
324
|
return blockedResult as ToolExecuteReturn;
|
|
461
325
|
}
|
|
@@ -463,143 +327,49 @@ export function adaptTools(
|
|
|
463
327
|
if (meta?.approvalPromise) {
|
|
464
328
|
const approved = await meta.approvalPromise;
|
|
465
329
|
if (!approved) {
|
|
466
|
-
const rejectedResult =
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
};
|
|
471
|
-
await persistToolErrorResult(rejectedResult, {
|
|
330
|
+
const rejectedResult = createRejectedToolResult();
|
|
331
|
+
await persistToolErrorResult(ctx, {
|
|
332
|
+
name,
|
|
333
|
+
errorResult: rejectedResult,
|
|
472
334
|
callId: callIdFromQueue,
|
|
473
335
|
startTs: startTsFromQueue,
|
|
474
336
|
stepIndexForEvent,
|
|
475
|
-
|
|
337
|
+
input: meta?.args,
|
|
476
338
|
});
|
|
477
339
|
return rejectedResult as ToolExecuteReturn;
|
|
478
340
|
}
|
|
479
341
|
}
|
|
480
342
|
// Handle session-relative paths and cwd tools
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
cwd,
|
|
488
|
-
String((input as Record<string, unknown>)?.path ?? '.'),
|
|
489
|
-
);
|
|
490
|
-
setCwd(ctx.sessionId, next);
|
|
491
|
-
res = { cwd: next };
|
|
492
|
-
} else if (
|
|
493
|
-
['read', 'write', 'ls', 'tree'].includes(name) &&
|
|
494
|
-
typeof (input as Record<string, unknown>)?.path === 'string'
|
|
495
|
-
) {
|
|
496
|
-
const rel = joinRelative(
|
|
497
|
-
cwd,
|
|
498
|
-
String((input as Record<string, unknown>).path),
|
|
499
|
-
);
|
|
500
|
-
const nextInput = {
|
|
501
|
-
...(input as Record<string, unknown>),
|
|
502
|
-
path: rel,
|
|
503
|
-
} as ToolExecuteInput;
|
|
504
|
-
// biome-ignore lint/suspicious/noExplicitAny: AI SDK types are complex
|
|
505
|
-
res = base.execute?.(nextInput, options as any);
|
|
506
|
-
} else if (name === 'shell' || name === 'bash') {
|
|
507
|
-
const needsCwd =
|
|
508
|
-
!input ||
|
|
509
|
-
typeof (input as Record<string, unknown>).cwd !== 'string';
|
|
510
|
-
const nextInput = needsCwd
|
|
511
|
-
? ({
|
|
512
|
-
...(input as Record<string, unknown>),
|
|
513
|
-
cwd,
|
|
514
|
-
} as ToolExecuteInput)
|
|
515
|
-
: input;
|
|
516
|
-
// biome-ignore lint/suspicious/noExplicitAny: AI SDK types are complex
|
|
517
|
-
res = base.execute?.(nextInput, options as any);
|
|
518
|
-
} else {
|
|
519
|
-
// biome-ignore lint/suspicious/noExplicitAny: AI SDK types are complex
|
|
520
|
-
res = base.execute?.(input, options as any);
|
|
521
|
-
}
|
|
343
|
+
const res = executeBaseTool(ctx, {
|
|
344
|
+
base,
|
|
345
|
+
name,
|
|
346
|
+
input,
|
|
347
|
+
options,
|
|
348
|
+
});
|
|
522
349
|
let result: unknown = res;
|
|
523
350
|
// If tool returns an async iterable, stream deltas while accumulating
|
|
524
351
|
if (res && typeof res === 'object' && Symbol.asyncIterator in res) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
continue;
|
|
532
|
-
}
|
|
533
|
-
if (
|
|
534
|
-
chunk &&
|
|
535
|
-
typeof chunk === 'object' &&
|
|
536
|
-
'terminalId' in chunk &&
|
|
537
|
-
typeof (chunk as { terminalId?: unknown }).terminalId ===
|
|
538
|
-
'string'
|
|
539
|
-
) {
|
|
540
|
-
publish({
|
|
541
|
-
type: 'tool.delta',
|
|
542
|
-
sessionId: ctx.sessionId,
|
|
543
|
-
payload: {
|
|
544
|
-
name,
|
|
545
|
-
channel: 'terminal',
|
|
546
|
-
delta: (chunk as { terminalId: string }).terminalId,
|
|
547
|
-
stepIndex: stepIndexForEvent,
|
|
548
|
-
callId: callIdFromQueue,
|
|
549
|
-
messageId: ctx.messageId,
|
|
550
|
-
},
|
|
551
|
-
});
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
const delta =
|
|
555
|
-
typeof chunk === 'string'
|
|
556
|
-
? chunk
|
|
557
|
-
: chunk &&
|
|
558
|
-
typeof chunk === 'object' &&
|
|
559
|
-
'delta' in chunk &&
|
|
560
|
-
typeof (chunk as { delta?: unknown }).delta === 'string'
|
|
561
|
-
? ((chunk as { delta: string }).delta ?? '')
|
|
562
|
-
: null;
|
|
563
|
-
if (!delta) continue;
|
|
564
|
-
const channel =
|
|
565
|
-
chunk &&
|
|
566
|
-
typeof chunk === 'object' &&
|
|
567
|
-
'channel' in chunk &&
|
|
568
|
-
typeof (chunk as { channel?: unknown }).channel === 'string'
|
|
569
|
-
? ((chunk as { channel: string }).channel ?? 'output')
|
|
570
|
-
: 'output';
|
|
571
|
-
publish({
|
|
572
|
-
type: 'tool.delta',
|
|
573
|
-
sessionId: ctx.sessionId,
|
|
574
|
-
payload: {
|
|
575
|
-
name,
|
|
576
|
-
channel,
|
|
577
|
-
delta,
|
|
578
|
-
stepIndex: stepIndexForEvent,
|
|
579
|
-
callId: callIdFromQueue,
|
|
580
|
-
messageId: ctx.messageId,
|
|
581
|
-
},
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
result =
|
|
585
|
-
streamedResult ??
|
|
586
|
-
(chunks.length > 0 ? chunks[chunks.length - 1] : null);
|
|
352
|
+
result = await consumeToolStream(ctx, {
|
|
353
|
+
stream: res as AsyncIterable<unknown>,
|
|
354
|
+
name,
|
|
355
|
+
stepIndex: stepIndexForEvent,
|
|
356
|
+
callId: callIdFromQueue,
|
|
357
|
+
});
|
|
587
358
|
} else {
|
|
588
359
|
// Await promise or passthrough value
|
|
589
360
|
result = await Promise.resolve(res as ToolExecuteReturn);
|
|
590
361
|
}
|
|
591
362
|
|
|
592
363
|
if (isToolError(result)) {
|
|
593
|
-
stepState
|
|
594
|
-
stepState.failedToolName = name;
|
|
595
|
-
failureState.active = true;
|
|
596
|
-
failureState.toolName = name;
|
|
364
|
+
markToolFailed(stepState, failureState, name);
|
|
597
365
|
|
|
598
|
-
await persistToolErrorResult(
|
|
366
|
+
await persistToolErrorResult(ctx, {
|
|
367
|
+
name,
|
|
368
|
+
errorResult: result,
|
|
599
369
|
callId: callIdFromQueue,
|
|
600
370
|
startTs: startTsFromQueue,
|
|
601
371
|
stepIndexForEvent,
|
|
602
|
-
|
|
372
|
+
input: meta?.args,
|
|
603
373
|
});
|
|
604
374
|
processedToolErrors.add(result as object);
|
|
605
375
|
return result as ToolExecuteReturn;
|
|
@@ -608,155 +378,66 @@ export function adaptTools(
|
|
|
608
378
|
const resultPartId = crypto.randomUUID();
|
|
609
379
|
const callId = callIdFromQueue;
|
|
610
380
|
const startTs = startTsFromQueue;
|
|
611
|
-
const contentObj
|
|
612
|
-
name: string;
|
|
613
|
-
result: unknown;
|
|
614
|
-
callId?: string;
|
|
615
|
-
artifact?: unknown;
|
|
616
|
-
args?: unknown;
|
|
617
|
-
} = {
|
|
381
|
+
const contentObj = buildToolResultContent({
|
|
618
382
|
name,
|
|
619
383
|
result,
|
|
620
384
|
callId,
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
contentObj.args = meta.args;
|
|
624
|
-
}
|
|
625
|
-
if (result && typeof result === 'object' && 'artifact' in result) {
|
|
626
|
-
try {
|
|
627
|
-
const maybeArtifact = (result as { artifact?: unknown })
|
|
628
|
-
.artifact;
|
|
629
|
-
if (maybeArtifact !== undefined)
|
|
630
|
-
contentObj.artifact = maybeArtifact;
|
|
631
|
-
} catch {}
|
|
632
|
-
}
|
|
385
|
+
input: meta?.args,
|
|
386
|
+
});
|
|
633
387
|
|
|
634
388
|
const index = await ctx.nextIndex();
|
|
635
|
-
const endTs =
|
|
636
|
-
const dur =
|
|
637
|
-
typeof startTs === 'number' ? Math.max(0, endTs - startTs) : null;
|
|
389
|
+
const { endTs, durationMs } = computeToolTiming(startTs);
|
|
638
390
|
|
|
639
391
|
// Special-case: keep progress_update result lightweight; publish first, persist best-effort
|
|
640
392
|
if (name === 'progress_update') {
|
|
641
|
-
stepState
|
|
642
|
-
|
|
643
|
-
if (failureState.active && failureState.toolName === name) {
|
|
644
|
-
failureState.active = false;
|
|
645
|
-
failureState.toolName = undefined;
|
|
646
|
-
}
|
|
647
|
-
publish({
|
|
648
|
-
type: 'tool.result',
|
|
649
|
-
sessionId: ctx.sessionId,
|
|
650
|
-
payload: { ...contentObj, stepIndex: stepIndexForEvent },
|
|
651
|
-
});
|
|
393
|
+
markToolSucceeded(stepState, failureState, name);
|
|
394
|
+
publishToolResult(ctx, contentObj, stepIndexForEvent);
|
|
652
395
|
// Persist without blocking the event loop
|
|
653
396
|
(async () => {
|
|
654
397
|
try {
|
|
655
|
-
await ctx
|
|
656
|
-
|
|
657
|
-
messageId: ctx.messageId,
|
|
398
|
+
await persistToolResultWithIndex(ctx, {
|
|
399
|
+
partId: resultPartId,
|
|
658
400
|
index,
|
|
401
|
+
name,
|
|
402
|
+
content: contentObj,
|
|
403
|
+
startTs,
|
|
404
|
+
callId,
|
|
659
405
|
stepIndex: stepIndexForEvent,
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
agent: ctx.agent,
|
|
663
|
-
provider: ctx.provider,
|
|
664
|
-
model: ctx.model,
|
|
665
|
-
startedAt: startTs,
|
|
666
|
-
completedAt: endTs,
|
|
667
|
-
toolName: name,
|
|
668
|
-
toolCallId: callId,
|
|
669
|
-
toolDurationMs: dur ?? undefined,
|
|
406
|
+
endTs,
|
|
407
|
+
durationMs,
|
|
670
408
|
});
|
|
671
409
|
} catch {}
|
|
672
410
|
})();
|
|
673
411
|
return result as ToolExecuteReturn;
|
|
674
412
|
}
|
|
675
413
|
|
|
676
|
-
stepState
|
|
677
|
-
stepState.failedToolName = undefined;
|
|
678
|
-
if (failureState.active && failureState.toolName === name) {
|
|
679
|
-
failureState.active = false;
|
|
680
|
-
failureState.toolName = undefined;
|
|
681
|
-
}
|
|
414
|
+
markToolSucceeded(stepState, failureState, name);
|
|
682
415
|
|
|
683
|
-
await ctx
|
|
684
|
-
|
|
685
|
-
messageId: ctx.messageId,
|
|
416
|
+
await persistToolResultWithIndex(ctx, {
|
|
417
|
+
partId: resultPartId,
|
|
686
418
|
index,
|
|
419
|
+
name,
|
|
420
|
+
content: contentObj,
|
|
421
|
+
startTs,
|
|
422
|
+
callId,
|
|
687
423
|
stepIndex: stepIndexForEvent,
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
agent: ctx.agent,
|
|
691
|
-
provider: ctx.provider,
|
|
692
|
-
model: ctx.model,
|
|
693
|
-
startedAt: startTs,
|
|
694
|
-
completedAt: endTs,
|
|
695
|
-
toolName: name,
|
|
696
|
-
toolCallId: callId,
|
|
697
|
-
toolDurationMs: dur ?? undefined,
|
|
424
|
+
endTs,
|
|
425
|
+
durationMs,
|
|
698
426
|
});
|
|
699
427
|
// Update session aggregates: total tool time and counts per tool
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
.where(eq(sessions.id, ctx.sessionId));
|
|
705
|
-
if (sessRows.length) {
|
|
706
|
-
const row = sessRows[0] as typeof sessions.$inferSelect;
|
|
707
|
-
const totalToolTimeMs =
|
|
708
|
-
Number(row.totalToolTimeMs || 0) + (dur ?? 0);
|
|
709
|
-
let counts: Record<string, number> = {};
|
|
710
|
-
try {
|
|
711
|
-
counts = row.toolCountsJson
|
|
712
|
-
? JSON.parse(row.toolCountsJson)
|
|
713
|
-
: {};
|
|
714
|
-
} catch {}
|
|
715
|
-
counts[name] = (counts[name] || 0) + 1;
|
|
716
|
-
await ctx.db
|
|
717
|
-
.update(sessions)
|
|
718
|
-
.set({
|
|
719
|
-
totalToolTimeMs,
|
|
720
|
-
toolCountsJson: JSON.stringify(counts),
|
|
721
|
-
lastActiveAt: endTs,
|
|
722
|
-
})
|
|
723
|
-
.where(eq(sessions.id, ctx.sessionId));
|
|
724
|
-
}
|
|
725
|
-
} catch {}
|
|
726
|
-
publish({
|
|
727
|
-
type: 'tool.result',
|
|
728
|
-
sessionId: ctx.sessionId,
|
|
729
|
-
payload: { ...contentObj, stepIndex: stepIndexForEvent },
|
|
730
|
-
});
|
|
731
|
-
logger.debug(`[tools] result ${name}`, {
|
|
732
|
-
sessionId: ctx.sessionId,
|
|
733
|
-
messageId: ctx.messageId,
|
|
734
|
-
toolName: name,
|
|
735
|
-
callId,
|
|
736
|
-
stepIndex: stepIndexForEvent,
|
|
428
|
+
await updateToolSessionStats(ctx, {
|
|
429
|
+
name,
|
|
430
|
+
durationMs,
|
|
431
|
+
endTs,
|
|
737
432
|
});
|
|
433
|
+
publishToolResult(ctx, contentObj, stepIndexForEvent);
|
|
434
|
+
logToolResult(ctx, { name, callId, stepIndex: stepIndexForEvent });
|
|
738
435
|
if (name === 'update_todos') {
|
|
739
|
-
|
|
740
|
-
const resultValue = (contentObj as { result?: unknown })
|
|
741
|
-
.result as { items?: unknown; note?: unknown } | undefined;
|
|
742
|
-
if (resultValue && Array.isArray(resultValue.items)) {
|
|
743
|
-
publish({
|
|
744
|
-
type: 'plan.updated',
|
|
745
|
-
sessionId: ctx.sessionId,
|
|
746
|
-
payload: {
|
|
747
|
-
items: resultValue.items,
|
|
748
|
-
note: resultValue.note,
|
|
749
|
-
},
|
|
750
|
-
});
|
|
751
|
-
}
|
|
752
|
-
} catch {}
|
|
436
|
+
publishPlanUpdated(ctx, contentObj.result);
|
|
753
437
|
}
|
|
754
438
|
return result as ToolExecuteReturn;
|
|
755
439
|
} catch (error) {
|
|
756
|
-
stepState
|
|
757
|
-
stepState.failedToolName = name;
|
|
758
|
-
failureState.active = true;
|
|
759
|
-
failureState.toolName = name;
|
|
440
|
+
markToolFailed(stepState, failureState, name);
|
|
760
441
|
|
|
761
442
|
// Tool execution failed
|
|
762
443
|
if (
|
|
@@ -768,23 +449,15 @@ export function adaptTools(
|
|
|
768
449
|
|
|
769
450
|
const errorResult = isToolError(error)
|
|
770
451
|
? error
|
|
771
|
-
: (
|
|
772
|
-
const errorMessage =
|
|
773
|
-
error instanceof Error ? error.message : String(error);
|
|
774
|
-
const errorStack =
|
|
775
|
-
error instanceof Error ? error.stack : undefined;
|
|
776
|
-
return {
|
|
777
|
-
ok: false,
|
|
778
|
-
error: errorMessage,
|
|
779
|
-
stack: errorStack,
|
|
780
|
-
};
|
|
781
|
-
})();
|
|
452
|
+
: createToolExceptionResult(error);
|
|
782
453
|
|
|
783
|
-
await persistToolErrorResult(
|
|
454
|
+
await persistToolErrorResult(ctx, {
|
|
455
|
+
name,
|
|
456
|
+
errorResult,
|
|
784
457
|
callId: callIdFromQueue,
|
|
785
458
|
startTs: startTsFromQueue,
|
|
786
459
|
stepIndexForEvent,
|
|
787
|
-
|
|
460
|
+
input: meta?.args,
|
|
788
461
|
});
|
|
789
462
|
|
|
790
463
|
if (isToolError(error)) {
|