@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottocode/server",
3
- "version": "0.1.235",
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.235",
53
- "@ottocode/database": "0.1.235",
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
 
@@ -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 { DiscoveredTool } from '@ottocode/sdk';
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 })