@ottocode/server 0.1.179 → 0.1.181
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/events/bus.ts +6 -1
- package/src/routes/sessions.ts +16 -3
- package/src/runtime/agent/runner-setup.ts +16 -3
- package/src/runtime/agent/runner.ts +26 -5
- package/src/runtime/message/tool-history-tracker.ts +14 -0
- package/src/runtime/provider/oauth-adapter.ts +16 -7
- package/src/runtime/stream/finish-handler.ts +5 -1
- package/src/runtime/stream/step-finish.ts +26 -5
- package/src/runtime/tools/approval.ts +1 -0
- package/src/runtime/tools/mapping.ts +4 -0
- package/src/tools/adapter.ts +25 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ottocode/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.181",
|
|
4
4
|
"description": "HTTP API server for ottocode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"typecheck": "tsc --noEmit"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@ottocode/sdk": "0.1.
|
|
33
|
-
"@ottocode/database": "0.1.
|
|
32
|
+
"@ottocode/sdk": "0.1.181",
|
|
33
|
+
"@ottocode/database": "0.1.181",
|
|
34
34
|
"drizzle-orm": "^0.44.5",
|
|
35
35
|
"hono": "^4.9.9",
|
|
36
36
|
"zod": "^4.1.8"
|
package/src/events/bus.ts
CHANGED
|
@@ -25,7 +25,12 @@ export function publish(event: OttoEvent) {
|
|
|
25
25
|
for (const sub of subs) {
|
|
26
26
|
try {
|
|
27
27
|
sub(sanitizedEvent);
|
|
28
|
-
} catch {
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error(
|
|
30
|
+
`[bus] Subscriber threw on event ${event.type}:`,
|
|
31
|
+
err instanceof Error ? err.message : String(err),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
}
|
|
31
36
|
|
package/src/routes/sessions.ts
CHANGED
|
@@ -20,6 +20,11 @@ export function registerSessionsRoutes(app: Hono) {
|
|
|
20
20
|
// List sessions
|
|
21
21
|
app.get('/v1/sessions', async (c) => {
|
|
22
22
|
const projectRoot = c.req.query('project') || process.cwd();
|
|
23
|
+
const limit = Math.min(
|
|
24
|
+
Math.max(parseInt(c.req.query('limit') || '50', 10) || 50, 1),
|
|
25
|
+
200,
|
|
26
|
+
);
|
|
27
|
+
const offset = Math.max(parseInt(c.req.query('offset') || '0', 10) || 0, 0);
|
|
23
28
|
const cfg = await loadConfig(projectRoot);
|
|
24
29
|
const db = await getDb(cfg.projectRoot);
|
|
25
30
|
// Only return sessions for this project, excluding research sessions
|
|
@@ -32,8 +37,12 @@ export function registerSessionsRoutes(app: Hono) {
|
|
|
32
37
|
ne(sessions.sessionType, 'research'),
|
|
33
38
|
),
|
|
34
39
|
)
|
|
35
|
-
.orderBy(desc(sessions.lastActiveAt), desc(sessions.createdAt))
|
|
36
|
-
|
|
40
|
+
.orderBy(desc(sessions.lastActiveAt), desc(sessions.createdAt))
|
|
41
|
+
.limit(limit + 1)
|
|
42
|
+
.offset(offset);
|
|
43
|
+
const hasMore = rows.length > limit;
|
|
44
|
+
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
45
|
+
const normalized = page.map((r) => {
|
|
37
46
|
let counts: Record<string, unknown> | undefined;
|
|
38
47
|
if (r.toolCountsJson) {
|
|
39
48
|
try {
|
|
@@ -46,7 +55,11 @@ export function registerSessionsRoutes(app: Hono) {
|
|
|
46
55
|
const { toolCountsJson: _toolCountsJson, ...rest } = r;
|
|
47
56
|
return counts ? { ...rest, toolCounts: counts } : rest;
|
|
48
57
|
});
|
|
49
|
-
return c.json(
|
|
58
|
+
return c.json({
|
|
59
|
+
items: normalized,
|
|
60
|
+
hasMore,
|
|
61
|
+
nextOffset: hasMore ? offset + limit : null,
|
|
62
|
+
});
|
|
50
63
|
});
|
|
51
64
|
|
|
52
65
|
// Create session
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { loadConfig, getUnderlyingProviderKey } from '@ottocode/sdk';
|
|
2
|
+
import { wrapLanguageModel } from 'ai';
|
|
3
|
+
import { devToolsMiddleware } from '@ai-sdk/devtools';
|
|
2
4
|
import { getDb } from '@ottocode/database';
|
|
3
5
|
import { sessions } from '@ottocode/database/schema';
|
|
4
6
|
import { eq } from 'drizzle-orm';
|
|
@@ -8,7 +10,7 @@ import { composeSystemPrompt } from '../prompt/builder.ts';
|
|
|
8
10
|
import { discoverProjectTools } from '@ottocode/sdk';
|
|
9
11
|
import { adaptTools } from '../../tools/adapter.ts';
|
|
10
12
|
import { buildDatabaseTools } from '../../tools/database/index.ts';
|
|
11
|
-
import { debugLog, time } from '../debug/index.ts';
|
|
13
|
+
import { debugLog, time, isDebugEnabled } from '../debug/index.ts';
|
|
12
14
|
import { buildHistoryMessages } from '../message/history-builder.ts';
|
|
13
15
|
import { getMaxOutputTokens } from '../utils/token.ts';
|
|
14
16
|
import { setupToolContext } from '../tools/setup.ts';
|
|
@@ -25,7 +27,9 @@ export interface SetupResult {
|
|
|
25
27
|
system: string;
|
|
26
28
|
systemComponents: string[];
|
|
27
29
|
additionalSystemMessages: Array<{ role: 'system' | 'user'; content: string }>;
|
|
28
|
-
model:
|
|
30
|
+
model:
|
|
31
|
+
| Awaited<ReturnType<typeof resolveModel>>
|
|
32
|
+
| ReturnType<typeof wrapLanguageModel>;
|
|
29
33
|
maxOutputTokens: number | undefined;
|
|
30
34
|
effectiveMaxOutputTokens: number | undefined;
|
|
31
35
|
toolset: ReturnType<typeof adaptTools>;
|
|
@@ -34,6 +38,7 @@ export interface SetupResult {
|
|
|
34
38
|
firstToolSeen: () => boolean;
|
|
35
39
|
providerOptions: Record<string, unknown>;
|
|
36
40
|
needsSpoof: boolean;
|
|
41
|
+
isOpenAIOAuth: boolean;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
const THINKING_BUDGET = 16000;
|
|
@@ -163,6 +168,13 @@ export async function setupRunner(opts: RunOpts): Promise<SetupResult> {
|
|
|
163
168
|
sessionId: opts.sessionId,
|
|
164
169
|
messageId: opts.assistantMessageId,
|
|
165
170
|
});
|
|
171
|
+
const wrappedModel = isDebugEnabled()
|
|
172
|
+
? // biome-ignore lint/suspicious/noExplicitAny: OpenRouter provider uses v2 spec
|
|
173
|
+
wrapLanguageModel({
|
|
174
|
+
model: model as any,
|
|
175
|
+
middleware: devToolsMiddleware(),
|
|
176
|
+
})
|
|
177
|
+
: model;
|
|
166
178
|
debugLog(
|
|
167
179
|
`[RUNNER] Model created: ${JSON.stringify({ id: model.modelId, provider: model.provider })}`,
|
|
168
180
|
);
|
|
@@ -223,7 +235,7 @@ export async function setupRunner(opts: RunOpts): Promise<SetupResult> {
|
|
|
223
235
|
system,
|
|
224
236
|
systemComponents,
|
|
225
237
|
additionalSystemMessages,
|
|
226
|
-
model,
|
|
238
|
+
model: wrappedModel,
|
|
227
239
|
maxOutputTokens,
|
|
228
240
|
effectiveMaxOutputTokens,
|
|
229
241
|
toolset,
|
|
@@ -232,6 +244,7 @@ export async function setupRunner(opts: RunOpts): Promise<SetupResult> {
|
|
|
232
244
|
firstToolSeen,
|
|
233
245
|
providerOptions,
|
|
234
246
|
needsSpoof: oauth.needsSpoof,
|
|
247
|
+
isOpenAIOAuth: oauth.isOpenAIOAuth,
|
|
235
248
|
};
|
|
236
249
|
}
|
|
237
250
|
|
|
@@ -51,7 +51,9 @@ export async function runSessionLoop(sessionId: string) {
|
|
|
51
51
|
try {
|
|
52
52
|
await runAssistant(job);
|
|
53
53
|
} catch (_err) {
|
|
54
|
-
|
|
54
|
+
debugLog(
|
|
55
|
+
`[RUNNER] runAssistant threw (swallowed to keep loop alive): ${_err instanceof Error ? _err.message : String(_err)}`,
|
|
56
|
+
);
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -80,6 +82,7 @@ async function runAssistant(opts: RunOpts) {
|
|
|
80
82
|
firstToolTimer,
|
|
81
83
|
firstToolSeen,
|
|
82
84
|
providerOptions,
|
|
85
|
+
isOpenAIOAuth,
|
|
83
86
|
} = setup;
|
|
84
87
|
|
|
85
88
|
const isFirstMessage = !history.some((m) => m.role === 'assistant');
|
|
@@ -91,7 +94,7 @@ async function runAssistant(opts: RunOpts) {
|
|
|
91
94
|
|
|
92
95
|
if (!isFirstMessage) {
|
|
93
96
|
messagesWithSystemInstructions.push({
|
|
94
|
-
role: 'user',
|
|
97
|
+
role: isOpenAIOAuth ? 'system' : 'user',
|
|
95
98
|
content:
|
|
96
99
|
'SYSTEM REMINDER: You are continuing an existing session. When you have completed the task, you MUST stream a text summary of what you did to the user, and THEN call the `finish` tool. Do not call `finish` without a summary.',
|
|
97
100
|
});
|
|
@@ -107,7 +110,11 @@ async function runAssistant(opts: RunOpts) {
|
|
|
107
110
|
try {
|
|
108
111
|
const name = (evt.payload as { name?: string } | undefined)?.name;
|
|
109
112
|
if (name === 'finish') _finishObserved = true;
|
|
110
|
-
} catch {
|
|
113
|
+
} catch (err) {
|
|
114
|
+
debugLog(
|
|
115
|
+
`[RUNNER] finish observer error: ${err instanceof Error ? err.message : String(err)}`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
111
118
|
});
|
|
112
119
|
|
|
113
120
|
const streamStartTimer = time('runner:first-delta');
|
|
@@ -289,6 +296,12 @@ async function runAssistant(opts: RunOpts) {
|
|
|
289
296
|
debugLog(
|
|
290
297
|
`[RUNNER] Stream finished. finishSeen=${_finishObserved}, firstToolSeen=${fs}`,
|
|
291
298
|
);
|
|
299
|
+
|
|
300
|
+
if (!_finishObserved && fs) {
|
|
301
|
+
debugLog(
|
|
302
|
+
`[RUNNER] WARNING: Stream ended without finish tool being called. Model was mid-execution (tools were used). This is likely an unclean stream termination from the provider.`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
292
305
|
} catch (err) {
|
|
293
306
|
unsubscribeFinish();
|
|
294
307
|
const payload = toErrorPayload(err);
|
|
@@ -334,7 +347,11 @@ async function runAssistant(opts: RunOpts) {
|
|
|
334
347
|
|
|
335
348
|
try {
|
|
336
349
|
await completeAssistantMessage({}, opts, db);
|
|
337
|
-
} catch {
|
|
350
|
+
} catch (err2) {
|
|
351
|
+
debugLog(
|
|
352
|
+
`[RUNNER] completeAssistantMessage failed after prune: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
338
355
|
return;
|
|
339
356
|
} catch (pruneErr) {
|
|
340
357
|
debugLog(
|
|
@@ -364,7 +381,11 @@ async function runAssistant(opts: RunOpts) {
|
|
|
364
381
|
db,
|
|
365
382
|
);
|
|
366
383
|
await completeAssistantMessage({}, opts, db);
|
|
367
|
-
} catch {
|
|
384
|
+
} catch (err2) {
|
|
385
|
+
debugLog(
|
|
386
|
+
`[RUNNER] Failed to complete message after error: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
387
|
+
);
|
|
388
|
+
}
|
|
368
389
|
throw err;
|
|
369
390
|
} finally {
|
|
370
391
|
debugLog(
|
|
@@ -77,6 +77,9 @@ function describeToolResult(info: ToolResultInfo): TargetDescriptor | null {
|
|
|
77
77
|
return describeWrite(info);
|
|
78
78
|
case 'apply_patch':
|
|
79
79
|
return describePatch(info);
|
|
80
|
+
case 'edit':
|
|
81
|
+
case 'multiedit':
|
|
82
|
+
return describeEdit(info);
|
|
80
83
|
default:
|
|
81
84
|
return null;
|
|
82
85
|
}
|
|
@@ -202,3 +205,14 @@ function getNumber(value: unknown): number | undefined {
|
|
|
202
205
|
function normalizePath(path: string): string {
|
|
203
206
|
return path.replace(/\\/g, '/');
|
|
204
207
|
}
|
|
208
|
+
|
|
209
|
+
function describeEdit(info: ToolResultInfo): TargetDescriptor | null {
|
|
210
|
+
const args = getRecord(info.args);
|
|
211
|
+
if (!args) return null;
|
|
212
|
+
const filePath = getString(args.filePath);
|
|
213
|
+
if (!filePath) return null;
|
|
214
|
+
const normalized = normalizePath(filePath);
|
|
215
|
+
const key = `edit:${normalized}`;
|
|
216
|
+
const summary = `[previous edit] ${normalized}`;
|
|
217
|
+
return { keys: [key], summary };
|
|
218
|
+
}
|
|
@@ -86,9 +86,16 @@ export function detectOAuth(
|
|
|
86
86
|
* Used directly by runner-setup.ts (complex flow) and indirectly
|
|
87
87
|
* by adaptSimpleCall (simple flows).
|
|
88
88
|
*/
|
|
89
|
-
|
|
89
|
+
const CODEX_INSTRUCTIONS =
|
|
90
|
+
'You are a coding agent. Follow all developer messages. Use tools to complete tasks.';
|
|
91
|
+
|
|
92
|
+
export function buildCodexProviderOptions() {
|
|
90
93
|
return {
|
|
91
|
-
openai: {
|
|
94
|
+
openai: {
|
|
95
|
+
store: false as const,
|
|
96
|
+
instructions: CODEX_INSTRUCTIONS,
|
|
97
|
+
parallelToolCalls: false,
|
|
98
|
+
},
|
|
92
99
|
};
|
|
93
100
|
}
|
|
94
101
|
|
|
@@ -132,13 +139,14 @@ export function adaptSimpleCall(
|
|
|
132
139
|
): AdaptedLLMCall {
|
|
133
140
|
if (ctx.isOpenAIOAuth) {
|
|
134
141
|
return {
|
|
142
|
+
system: input.instructions,
|
|
135
143
|
messages: [
|
|
136
144
|
{
|
|
137
145
|
role: 'user',
|
|
138
|
-
content:
|
|
146
|
+
content: input.userContent,
|
|
139
147
|
},
|
|
140
148
|
],
|
|
141
|
-
providerOptions: buildCodexProviderOptions(
|
|
149
|
+
providerOptions: buildCodexProviderOptions(),
|
|
142
150
|
forceStream: true,
|
|
143
151
|
};
|
|
144
152
|
}
|
|
@@ -184,7 +192,8 @@ export type AdaptedRunnerSetup = {
|
|
|
184
192
|
* decides WHERE the already-composed system prompt goes:
|
|
185
193
|
*
|
|
186
194
|
* - **OpenAI OAuth (Codex)**: system='', composed prompt sent as a user
|
|
187
|
-
* message in additionalSystemMessages
|
|
195
|
+
* system message in additionalSystemMessages (becomes developer role in
|
|
196
|
+
* Responses API), providerOptions with store=false
|
|
188
197
|
* + instructions, maxOutputTokens stripped.
|
|
189
198
|
*
|
|
190
199
|
* - **Anthropic OAuth**: spoof prompt as system, composed prompt sent as
|
|
@@ -221,9 +230,9 @@ export function adaptRunnerCall(
|
|
|
221
230
|
return {
|
|
222
231
|
system: '',
|
|
223
232
|
systemComponents: composed.components,
|
|
224
|
-
additionalSystemMessages: [{ role: '
|
|
233
|
+
additionalSystemMessages: [{ role: 'system', content: composed.prompt }],
|
|
225
234
|
maxOutputTokens: undefined,
|
|
226
|
-
providerOptions: buildCodexProviderOptions(
|
|
235
|
+
providerOptions: buildCodexProviderOptions(),
|
|
227
236
|
};
|
|
228
237
|
}
|
|
229
238
|
|
|
@@ -24,7 +24,11 @@ export function createFinishHandler(
|
|
|
24
24
|
return async (fin: FinishEvent) => {
|
|
25
25
|
try {
|
|
26
26
|
await completeAssistantMessageFn(fin, opts, db);
|
|
27
|
-
} catch {
|
|
27
|
+
} catch (err) {
|
|
28
|
+
debugLog(
|
|
29
|
+
`[finish-handler] completeAssistantMessage failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
28
32
|
|
|
29
33
|
if (opts.isCompactCommand && fin.finishReason !== 'error') {
|
|
30
34
|
const assistantParts = await db
|
|
@@ -6,6 +6,7 @@ import type { RunOpts } from '../session/queue.ts';
|
|
|
6
6
|
import type { ToolAdapterContext } from '../../tools/adapter.ts';
|
|
7
7
|
import type { UsageData, ProviderMetadata } from '../session/db-operations.ts';
|
|
8
8
|
import type { StepFinishEvent } from './types.ts';
|
|
9
|
+
import { debugLog } from '../debug/index.ts';
|
|
9
10
|
|
|
10
11
|
export function createStepFinishHandler(
|
|
11
12
|
opts: RunOpts,
|
|
@@ -41,7 +42,11 @@ export function createStepFinishHandler(
|
|
|
41
42
|
.set({ completedAt: finishedAt })
|
|
42
43
|
.where(eq(messageParts.id, currentPartId));
|
|
43
44
|
}
|
|
44
|
-
} catch {
|
|
45
|
+
} catch (err) {
|
|
46
|
+
debugLog(
|
|
47
|
+
`[step-finish] Failed to update part completedAt: ${err instanceof Error ? err.message : String(err)}`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
45
50
|
|
|
46
51
|
if (step.usage) {
|
|
47
52
|
try {
|
|
@@ -51,7 +56,11 @@ export function createStepFinishHandler(
|
|
|
51
56
|
opts,
|
|
52
57
|
db,
|
|
53
58
|
);
|
|
54
|
-
} catch {
|
|
59
|
+
} catch (err) {
|
|
60
|
+
debugLog(
|
|
61
|
+
`[step-finish] Failed to update session tokens: ${err instanceof Error ? err.message : String(err)}`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
55
64
|
|
|
56
65
|
try {
|
|
57
66
|
await updateMessageTokensIncrementalFn(
|
|
@@ -60,7 +69,11 @@ export function createStepFinishHandler(
|
|
|
60
69
|
opts,
|
|
61
70
|
db,
|
|
62
71
|
);
|
|
63
|
-
} catch {
|
|
72
|
+
} catch (err) {
|
|
73
|
+
debugLog(
|
|
74
|
+
`[step-finish] Failed to update message tokens: ${err instanceof Error ? err.message : String(err)}`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
try {
|
|
@@ -81,13 +94,21 @@ export function createStepFinishHandler(
|
|
|
81
94
|
payload: { stepIndex, ...step.usage },
|
|
82
95
|
});
|
|
83
96
|
}
|
|
84
|
-
} catch {
|
|
97
|
+
} catch (err) {
|
|
98
|
+
debugLog(
|
|
99
|
+
`[step-finish] Failed to publish finish-step: ${err instanceof Error ? err.message : String(err)}`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
85
102
|
|
|
86
103
|
try {
|
|
87
104
|
const newStepIndex = incrementStepIndex();
|
|
88
105
|
sharedCtx.stepIndex = newStepIndex;
|
|
89
106
|
updateCurrentPartId(null);
|
|
90
107
|
updateAccumulated('');
|
|
91
|
-
} catch {
|
|
108
|
+
} catch (err) {
|
|
109
|
+
debugLog(
|
|
110
|
+
`[step-finish] Failed to increment step: ${err instanceof Error ? err.message : String(err)}`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
92
113
|
};
|
|
93
114
|
}
|
|
@@ -39,6 +39,8 @@ export const CANONICAL_TO_PASCAL: Record<string, string> = {
|
|
|
39
39
|
|
|
40
40
|
// Patch/edit
|
|
41
41
|
apply_patch: 'ApplyPatch',
|
|
42
|
+
edit: 'Edit',
|
|
43
|
+
multiedit: 'MultiEdit',
|
|
42
44
|
|
|
43
45
|
// Task management
|
|
44
46
|
update_todos: 'UpdateTodos',
|
|
@@ -77,6 +79,8 @@ export const PASCAL_TO_CANONICAL: Record<string, string> = {
|
|
|
77
79
|
|
|
78
80
|
// Patch/edit
|
|
79
81
|
ApplyPatch: 'apply_patch',
|
|
82
|
+
Edit: 'edit',
|
|
83
|
+
MultiEdit: 'multiedit',
|
|
80
84
|
|
|
81
85
|
// Task management
|
|
82
86
|
UpdateTodos: 'update_todos',
|
package/src/tools/adapter.ts
CHANGED
|
@@ -52,6 +52,30 @@ function getPendingQueue(
|
|
|
52
52
|
return queue;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function unwrapDoubleWrappedArgs(
|
|
56
|
+
input: unknown,
|
|
57
|
+
expectedName: string,
|
|
58
|
+
): typeof input {
|
|
59
|
+
if (
|
|
60
|
+
input &&
|
|
61
|
+
typeof input === 'object' &&
|
|
62
|
+
'name' in input &&
|
|
63
|
+
'args' in input &&
|
|
64
|
+
typeof (input as Record<string, unknown>).name === 'string' &&
|
|
65
|
+
typeof (input as Record<string, unknown>).args === 'object' &&
|
|
66
|
+
(input as Record<string, unknown>).args !== null
|
|
67
|
+
) {
|
|
68
|
+
const wrapped = input as { name: string; args: Record<string, unknown> };
|
|
69
|
+
if (
|
|
70
|
+
wrapped.name === expectedName ||
|
|
71
|
+
wrapped.name.replace(/[_-]/g, '') === expectedName.replace(/[_-]/g, '')
|
|
72
|
+
) {
|
|
73
|
+
return wrapped.args as typeof input;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return input;
|
|
77
|
+
}
|
|
78
|
+
|
|
55
79
|
export function adaptTools(
|
|
56
80
|
tools: DiscoveredTool[],
|
|
57
81
|
ctx: ToolAdapterContext,
|
|
@@ -318,6 +342,7 @@ export function adaptTools(
|
|
|
318
342
|
}
|
|
319
343
|
},
|
|
320
344
|
async execute(input: ToolExecuteInput, options: ToolExecuteOptions) {
|
|
345
|
+
input = unwrapDoubleWrappedArgs(input, name);
|
|
321
346
|
const queue = pendingCalls.get(name);
|
|
322
347
|
const meta = queue?.shift();
|
|
323
348
|
if (queue && queue.length === 0) pendingCalls.delete(name);
|