@ottocode/server 0.1.235 → 0.1.236
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/openapi/paths/config.ts +94 -0
- package/src/routes/config/debug.ts +39 -0
- package/src/routes/config/index.ts +2 -0
- package/src/routes/config/utils.ts +0 -1
- package/src/runtime/agent/runner-setup.ts +72 -1
- package/src/runtime/message/service.ts +17 -0
- package/src/tools/adapter.ts +15 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ottocode/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.236",
|
|
4
4
|
"description": "HTTP API server for ottocode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"typecheck": "tsc --noEmit"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@ottocode/sdk": "0.1.
|
|
53
|
-
"@ottocode/database": "0.1.
|
|
52
|
+
"@ottocode/sdk": "0.1.236",
|
|
53
|
+
"@ottocode/database": "0.1.236",
|
|
54
54
|
"drizzle-orm": "^0.44.5",
|
|
55
55
|
"hono": "^4.9.9",
|
|
56
56
|
"zod": "^4.3.6"
|
|
@@ -234,6 +234,100 @@ export const configPaths = {
|
|
|
234
234
|
},
|
|
235
235
|
},
|
|
236
236
|
},
|
|
237
|
+
'/v1/config/debug': {
|
|
238
|
+
get: {
|
|
239
|
+
tags: ['config'],
|
|
240
|
+
operationId: 'getDebugConfig',
|
|
241
|
+
summary: 'Get debug configuration',
|
|
242
|
+
responses: {
|
|
243
|
+
200: {
|
|
244
|
+
description: 'OK',
|
|
245
|
+
content: {
|
|
246
|
+
'application/json': {
|
|
247
|
+
schema: {
|
|
248
|
+
type: 'object',
|
|
249
|
+
properties: {
|
|
250
|
+
enabled: { type: 'boolean' },
|
|
251
|
+
scopes: {
|
|
252
|
+
type: 'array',
|
|
253
|
+
items: { type: 'string' },
|
|
254
|
+
},
|
|
255
|
+
logPath: { type: 'string' },
|
|
256
|
+
sessionsDir: { type: 'string' },
|
|
257
|
+
debugDir: { type: 'string' },
|
|
258
|
+
},
|
|
259
|
+
required: [
|
|
260
|
+
'enabled',
|
|
261
|
+
'scopes',
|
|
262
|
+
'logPath',
|
|
263
|
+
'sessionsDir',
|
|
264
|
+
'debugDir',
|
|
265
|
+
],
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
patch: {
|
|
273
|
+
tags: ['config'],
|
|
274
|
+
operationId: 'updateDebugConfig',
|
|
275
|
+
summary: 'Update debug configuration',
|
|
276
|
+
requestBody: {
|
|
277
|
+
required: true,
|
|
278
|
+
content: {
|
|
279
|
+
'application/json': {
|
|
280
|
+
schema: {
|
|
281
|
+
type: 'object',
|
|
282
|
+
properties: {
|
|
283
|
+
enabled: { type: 'boolean' },
|
|
284
|
+
scopes: {
|
|
285
|
+
type: 'array',
|
|
286
|
+
items: { type: 'string' },
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
responses: {
|
|
294
|
+
200: {
|
|
295
|
+
description: 'OK',
|
|
296
|
+
content: {
|
|
297
|
+
'application/json': {
|
|
298
|
+
schema: {
|
|
299
|
+
type: 'object',
|
|
300
|
+
properties: {
|
|
301
|
+
success: { type: 'boolean' },
|
|
302
|
+
debug: {
|
|
303
|
+
type: 'object',
|
|
304
|
+
properties: {
|
|
305
|
+
enabled: { type: 'boolean' },
|
|
306
|
+
scopes: {
|
|
307
|
+
type: 'array',
|
|
308
|
+
items: { type: 'string' },
|
|
309
|
+
},
|
|
310
|
+
logPath: { type: 'string' },
|
|
311
|
+
sessionsDir: { type: 'string' },
|
|
312
|
+
debugDir: { type: 'string' },
|
|
313
|
+
},
|
|
314
|
+
required: [
|
|
315
|
+
'enabled',
|
|
316
|
+
'scopes',
|
|
317
|
+
'logPath',
|
|
318
|
+
'sessionsDir',
|
|
319
|
+
'debugDir',
|
|
320
|
+
],
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
required: ['success', 'debug'],
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
237
331
|
'/v1/config/models': {
|
|
238
332
|
get: {
|
|
239
333
|
tags: ['config'],
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import { logger, readDebugConfig, writeDebugConfig } from '@ottocode/sdk';
|
|
3
|
+
import { serializeError } from '../../runtime/errors/api-error.ts';
|
|
4
|
+
|
|
5
|
+
export function registerDebugConfigRoute(app: Hono) {
|
|
6
|
+
app.get('/v1/config/debug', async (c) => {
|
|
7
|
+
try {
|
|
8
|
+
const debug = await readDebugConfig();
|
|
9
|
+
return c.json(debug);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
logger.error('Failed to load debug config', error);
|
|
12
|
+
const errorResponse = serializeError(error);
|
|
13
|
+
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
app.patch('/v1/config/debug', async (c) => {
|
|
18
|
+
try {
|
|
19
|
+
const body = await c.req.json<{
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
scopes?: string[];
|
|
22
|
+
}>();
|
|
23
|
+
|
|
24
|
+
await writeDebugConfig({
|
|
25
|
+
enabled: body.enabled,
|
|
26
|
+
scopes: Array.isArray(body.scopes)
|
|
27
|
+
? body.scopes.map((scope) => scope.trim()).filter(Boolean)
|
|
28
|
+
: body.scopes,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const debug = await readDebugConfig();
|
|
32
|
+
return c.json({ success: true, debug });
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.error('Failed to update debug config', error);
|
|
35
|
+
const errorResponse = serializeError(error);
|
|
36
|
+
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -5,6 +5,7 @@ import { registerAgentsRoute } from './agents.ts';
|
|
|
5
5
|
import { registerProvidersRoute } from './providers.ts';
|
|
6
6
|
import { registerModelsRoutes } from './models.ts';
|
|
7
7
|
import { registerDefaultsRoute } from './defaults.ts';
|
|
8
|
+
import { registerDebugConfigRoute } from './debug.ts';
|
|
8
9
|
|
|
9
10
|
export function registerConfigRoutes(app: Hono) {
|
|
10
11
|
registerCwdRoute(app);
|
|
@@ -13,4 +14,5 @@ export function registerConfigRoutes(app: Hono) {
|
|
|
13
14
|
registerProvidersRoute(app);
|
|
14
15
|
registerModelsRoutes(app);
|
|
15
16
|
registerDefaultsRoute(app);
|
|
17
|
+
registerDebugConfigRoute(app);
|
|
16
18
|
}
|
|
@@ -9,7 +9,6 @@ import { readdir } from 'node:fs/promises';
|
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import type { EmbeddedAppConfig } from '../../index.ts';
|
|
11
11
|
import type { OttoConfig } from '@ottocode/sdk';
|
|
12
|
-
import { logger } from '@ottocode/sdk';
|
|
13
12
|
import { loadAgentsConfig } from '../../runtime/agent/registry.ts';
|
|
14
13
|
|
|
15
14
|
export async function isProviderAuthorizedHybrid(
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { loadConfig } from '@ottocode/sdk';
|
|
1
|
+
import { loadConfig, logger, getSessionSystemPromptPath } from '@ottocode/sdk';
|
|
2
2
|
import { wrapLanguageModel } from 'ai';
|
|
3
3
|
import { devToolsMiddleware } from '@ai-sdk/devtools';
|
|
4
4
|
import { getDb } from '@ottocode/database';
|
|
5
5
|
import { sessions } from '@ottocode/database/schema';
|
|
6
6
|
import { eq } from 'drizzle-orm';
|
|
7
|
+
import { mkdir } from 'node:fs/promises';
|
|
8
|
+
import { dirname } from 'node:path';
|
|
7
9
|
import { resolveModel } from '../provider/index.ts';
|
|
8
10
|
import { resolveAgentConfig } from './registry.ts';
|
|
9
11
|
import { composeSystemPrompt } from '../prompt/builder.ts';
|
|
@@ -133,7 +135,76 @@ export async function setupRunner(opts: RunOpts): Promise<SetupResult> {
|
|
|
133
135
|
|
|
134
136
|
const { system } = adapted;
|
|
135
137
|
const { systemComponents, additionalSystemMessages } = adapted;
|
|
138
|
+
const openAIProviderOptions = adapted.providerOptions.openai as
|
|
139
|
+
| Record<string, unknown>
|
|
140
|
+
| undefined;
|
|
141
|
+
const openAIInstructions =
|
|
142
|
+
typeof openAIProviderOptions?.instructions === 'string'
|
|
143
|
+
? openAIProviderOptions.instructions
|
|
144
|
+
: '';
|
|
145
|
+
const effectiveSystemPrompt = system || openAIInstructions || composed.prompt;
|
|
146
|
+
const promptMode = oauth.isOpenAIOAuth
|
|
147
|
+
? 'openai-oauth'
|
|
148
|
+
: oauth.needsSpoof
|
|
149
|
+
? 'spoof'
|
|
150
|
+
: 'standard';
|
|
136
151
|
systemTimer.end();
|
|
152
|
+
logger.debug('[prompt] system prompt assembled', {
|
|
153
|
+
sessionId: opts.sessionId,
|
|
154
|
+
messageId: opts.assistantMessageId,
|
|
155
|
+
agent: opts.agent,
|
|
156
|
+
provider: opts.provider,
|
|
157
|
+
model: opts.model,
|
|
158
|
+
promptMode,
|
|
159
|
+
components: systemComponents,
|
|
160
|
+
systemLength: effectiveSystemPrompt.length,
|
|
161
|
+
historyMessages: history.length,
|
|
162
|
+
additionalSystemMessages: additionalSystemMessages.length,
|
|
163
|
+
isFirstMessage,
|
|
164
|
+
isOpenAIOAuth: oauth.isOpenAIOAuth,
|
|
165
|
+
needsSpoof: oauth.needsSpoof,
|
|
166
|
+
});
|
|
167
|
+
logger.debug('[prompt] detailed prompt context', {
|
|
168
|
+
sessionId: opts.sessionId,
|
|
169
|
+
messageId: opts.assistantMessageId,
|
|
170
|
+
debugDetail: true,
|
|
171
|
+
agentPromptLength: agentPrompt.length,
|
|
172
|
+
contextSummaryLength: contextSummary?.length ?? 0,
|
|
173
|
+
userContextLength: opts.userContext?.length ?? 0,
|
|
174
|
+
oneShot: Boolean(opts.oneShot),
|
|
175
|
+
guidedMode: Boolean(cfg.defaults.guidedMode),
|
|
176
|
+
isOpenAIOAuth: oauth.isOpenAIOAuth,
|
|
177
|
+
needsSpoof: oauth.needsSpoof,
|
|
178
|
+
promptMode,
|
|
179
|
+
rawSystemLength: system.length,
|
|
180
|
+
openAIInstructionsLength: openAIInstructions.length,
|
|
181
|
+
effectiveSystemPromptLength: effectiveSystemPrompt.length,
|
|
182
|
+
systemComponents,
|
|
183
|
+
additionalSystemMessageRoles: additionalSystemMessages.map(
|
|
184
|
+
(message) => message.role,
|
|
185
|
+
),
|
|
186
|
+
});
|
|
187
|
+
if (effectiveSystemPrompt) {
|
|
188
|
+
const systemPromptPath = getSessionSystemPromptPath(opts.sessionId);
|
|
189
|
+
try {
|
|
190
|
+
await mkdir(dirname(systemPromptPath), { recursive: true });
|
|
191
|
+
await Bun.write(systemPromptPath, effectiveSystemPrompt);
|
|
192
|
+
logger.debug('[prompt] wrote system prompt file', {
|
|
193
|
+
sessionId: opts.sessionId,
|
|
194
|
+
messageId: opts.assistantMessageId,
|
|
195
|
+
path: systemPromptPath,
|
|
196
|
+
debugDetail: true,
|
|
197
|
+
promptMode,
|
|
198
|
+
effectiveSystemPromptLength: effectiveSystemPrompt.length,
|
|
199
|
+
});
|
|
200
|
+
} catch (error) {
|
|
201
|
+
logger.warn('[prompt] failed to write system prompt file', {
|
|
202
|
+
sessionId: opts.sessionId,
|
|
203
|
+
messageId: opts.assistantMessageId,
|
|
204
|
+
error: error instanceof Error ? error.message : String(error),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
137
208
|
|
|
138
209
|
if (opts.isCompactCommand && opts.compactionContext) {
|
|
139
210
|
const compactPrompt = getCompactionSystemPrompt();
|
|
@@ -9,6 +9,7 @@ import { runSessionLoop } from '../agent/runner.ts';
|
|
|
9
9
|
import { resolveModel } from '../provider/index.ts';
|
|
10
10
|
import {
|
|
11
11
|
getFastModelForAuth,
|
|
12
|
+
logger,
|
|
12
13
|
type ProviderId,
|
|
13
14
|
type ReasoningLevel,
|
|
14
15
|
} from '@ottocode/sdk';
|
|
@@ -61,6 +62,14 @@ export async function dispatchAssistantMessage(
|
|
|
61
62
|
const sessionId = session.id;
|
|
62
63
|
const now = Date.now();
|
|
63
64
|
const userMessageId = crypto.randomUUID();
|
|
65
|
+
logger.debug('[agent] dispatching assistant message', {
|
|
66
|
+
sessionId,
|
|
67
|
+
agent,
|
|
68
|
+
provider,
|
|
69
|
+
model,
|
|
70
|
+
oneShot: Boolean(oneShot),
|
|
71
|
+
hasUserContext: Boolean(userContext),
|
|
72
|
+
});
|
|
64
73
|
|
|
65
74
|
await db.insert(messages).values({
|
|
66
75
|
id: userMessageId,
|
|
@@ -194,6 +203,14 @@ export async function dispatchAssistantMessage(
|
|
|
194
203
|
},
|
|
195
204
|
runSessionLoop,
|
|
196
205
|
);
|
|
206
|
+
logger.debug('[agent] assistant run enqueued', {
|
|
207
|
+
sessionId,
|
|
208
|
+
assistantMessageId,
|
|
209
|
+
agent,
|
|
210
|
+
provider,
|
|
211
|
+
model,
|
|
212
|
+
isCompactCommand: isCompact,
|
|
213
|
+
});
|
|
197
214
|
|
|
198
215
|
void touchSessionLastActive({ db, sessionId });
|
|
199
216
|
|
package/src/tools/adapter.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Tool } from 'ai';
|
|
|
2
2
|
import { messageParts, sessions } from '@ottocode/database/schema';
|
|
3
3
|
import { eq } from 'drizzle-orm';
|
|
4
4
|
import { publish } from '../events/bus.ts';
|
|
5
|
-
import type
|
|
5
|
+
import { logger, type DiscoveredTool } from '@ottocode/sdk';
|
|
6
6
|
import { getCwd, setCwd, joinRelative } from '../runtime/utils/cwd.ts';
|
|
7
7
|
import type {
|
|
8
8
|
ToolAdapterContext,
|
|
@@ -312,6 +312,13 @@ export function adaptTools(
|
|
|
312
312
|
messageId: ctx.messageId,
|
|
313
313
|
},
|
|
314
314
|
});
|
|
315
|
+
logger.debug(`[tools] call ${name}`, {
|
|
316
|
+
sessionId: ctx.sessionId,
|
|
317
|
+
messageId: ctx.messageId,
|
|
318
|
+
toolName: name,
|
|
319
|
+
callId,
|
|
320
|
+
stepIndex: ctx.stepIndex,
|
|
321
|
+
});
|
|
315
322
|
// Persist synchronously to maintain correct ordering
|
|
316
323
|
try {
|
|
317
324
|
const index = await ctx.nextIndex();
|
|
@@ -689,6 +696,13 @@ export function adaptTools(
|
|
|
689
696
|
sessionId: ctx.sessionId,
|
|
690
697
|
payload: { ...contentObj, stepIndex: stepIndexForEvent },
|
|
691
698
|
});
|
|
699
|
+
logger.debug(`[tools] result ${name}`, {
|
|
700
|
+
sessionId: ctx.sessionId,
|
|
701
|
+
messageId: ctx.messageId,
|
|
702
|
+
toolName: name,
|
|
703
|
+
callId,
|
|
704
|
+
stepIndex: stepIndexForEvent,
|
|
705
|
+
});
|
|
692
706
|
if (name === 'update_todos') {
|
|
693
707
|
try {
|
|
694
708
|
const resultValue = (contentObj as { result?: unknown })
|