@ottocode/server 0.1.237 → 0.1.243

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.
@@ -13,8 +13,9 @@ import {
13
13
  type ProviderId,
14
14
  type ReasoningLevel,
15
15
  } from '@ottocode/sdk';
16
- import { isCompactCommand, buildCompactionContext } from './compaction.ts';
16
+ import { estimateTokens } from './compaction.ts';
17
17
  import { detectOAuth, adaptSimpleCall } from '../provider/oauth-adapter.ts';
18
+ import { prepareBuiltinCommand } from '../commands/builtins.ts';
18
19
 
19
20
  type SessionRow = typeof sessions.$inferSelect;
20
21
 
@@ -61,14 +62,25 @@ export async function dispatchAssistantMessage(
61
62
 
62
63
  const sessionId = session.id;
63
64
  const now = Date.now();
65
+ const builtinCommand = await prepareBuiltinCommand({
66
+ cfg,
67
+ db,
68
+ sessionId,
69
+ provider,
70
+ model,
71
+ content,
72
+ });
73
+ const effectiveAgent = builtinCommand?.agent ?? agent;
74
+ const effectiveOneShot = builtinCommand?.oneShot ?? oneShot;
64
75
  const userMessageId = crypto.randomUUID();
65
76
  logger.debug('[agent] dispatching assistant message', {
66
77
  sessionId,
67
- agent,
78
+ agent: effectiveAgent,
68
79
  provider,
69
80
  model,
70
- oneShot: Boolean(oneShot),
81
+ oneShot: Boolean(effectiveOneShot),
71
82
  hasUserContext: Boolean(userContext),
83
+ builtinCommand: builtinCommand?.id,
72
84
  });
73
85
 
74
86
  await db.insert(messages).values({
@@ -76,7 +88,7 @@ export async function dispatchAssistantMessage(
76
88
  sessionId,
77
89
  role: 'user',
78
90
  status: 'complete',
79
- agent,
91
+ agent: effectiveAgent,
80
92
  provider,
81
93
  model,
82
94
  createdAt: now,
@@ -87,7 +99,7 @@ export async function dispatchAssistantMessage(
87
99
  index: 0,
88
100
  type: 'text',
89
101
  content: JSON.stringify({ text: String(content) }),
90
- agent,
102
+ agent: effectiveAgent,
91
103
  provider,
92
104
  model,
93
105
  });
@@ -101,7 +113,7 @@ export async function dispatchAssistantMessage(
101
113
  index: i + 1,
102
114
  type: 'image',
103
115
  content: JSON.stringify({ data: img.data, mediaType: img.mediaType }),
104
- agent,
116
+ agent: effectiveAgent,
105
117
  provider,
106
118
  model,
107
119
  });
@@ -124,7 +136,7 @@ export async function dispatchAssistantMessage(
124
136
  mediaType: file.mediaType,
125
137
  textContent: file.textContent,
126
138
  }),
127
- agent,
139
+ agent: effectiveAgent,
128
140
  provider,
129
141
  model,
130
142
  });
@@ -137,7 +149,7 @@ export async function dispatchAssistantMessage(
137
149
  payload: {
138
150
  id: userMessageId,
139
151
  role: 'user',
140
- agent,
152
+ agent: effectiveAgent,
141
153
  provider,
142
154
  model,
143
155
  content: String(content),
@@ -150,7 +162,7 @@ export async function dispatchAssistantMessage(
150
162
  sessionId,
151
163
  role: 'assistant',
152
164
  status: 'pending',
153
- agent,
165
+ agent: effectiveAgent,
154
166
  provider,
155
167
  model,
156
168
  createdAt: Date.now(),
@@ -161,27 +173,23 @@ export async function dispatchAssistantMessage(
161
173
  payload: {
162
174
  id: assistantMessageId,
163
175
  role: 'assistant',
164
- agent,
176
+ agent: effectiveAgent,
165
177
  provider,
166
178
  model,
167
179
  },
168
180
  });
169
181
 
170
- const isCompact = isCompactCommand(content);
171
- let compactionContext: string | undefined;
172
-
173
- if (isCompact) {
174
- const { getModelLimits } = await import('./compaction.ts');
175
- const limits = getModelLimits(provider, model);
176
- const contextTokenLimit = limits
177
- ? Math.max(Math.floor(limits.context * 0.5), 15000)
178
- : 15000;
179
- compactionContext = await buildCompactionContext(
180
- db,
181
- sessionId,
182
- contextTokenLimit,
183
- );
184
- }
182
+ const commandPromptText =
183
+ builtinCommand?.additionalPromptMessages
184
+ ?.map((message) => message.content)
185
+ .join('\n\n') ?? content;
186
+ const estimatedInputTokens =
187
+ estimateTokens(commandPromptText) +
188
+ estimateTokens(userContext ?? '') +
189
+ (files?.reduce(
190
+ (total, file) => total + estimateTokens(file.textContent ?? ''),
191
+ 0,
192
+ ) ?? 0);
185
193
 
186
194
  const toolApprovalMode = cfg.defaults.toolApproval ?? 'dangerous';
187
195
 
@@ -189,16 +197,19 @@ export async function dispatchAssistantMessage(
189
197
  {
190
198
  sessionId,
191
199
  assistantMessageId,
192
- agent,
200
+ agent: effectiveAgent,
193
201
  provider,
194
202
  model,
195
203
  projectRoot: cfg.projectRoot,
196
- oneShot: Boolean(oneShot),
204
+ oneShot: Boolean(effectiveOneShot),
197
205
  userContext,
206
+ estimatedInputTokens,
198
207
  reasoningText,
199
208
  reasoningLevel,
200
- isCompactCommand: isCompact,
201
- compactionContext,
209
+ omitHistory: builtinCommand?.omitHistory,
210
+ isCompactCommand: builtinCommand?.isCompactCommand,
211
+ compactionContext: builtinCommand?.compactionContext,
212
+ additionalPromptMessages: builtinCommand?.additionalPromptMessages,
202
213
  toolApprovalMode,
203
214
  },
204
215
  runSessionLoop,
@@ -206,10 +217,11 @@ export async function dispatchAssistantMessage(
206
217
  logger.debug('[agent] assistant run enqueued', {
207
218
  sessionId,
208
219
  assistantMessageId,
209
- agent,
220
+ agent: effectiveAgent,
210
221
  provider,
211
222
  model,
212
- isCompactCommand: isCompact,
223
+ builtinCommand: builtinCommand?.id,
224
+ isCompactCommand: builtinCommand?.isCompactCommand,
213
225
  });
214
226
 
215
227
  void touchSessionLastActive({ db, sessionId });
@@ -134,6 +134,8 @@ function buildSharedProviderOptions(
134
134
  function usesAdaptiveAnthropicThinking(model: string): boolean {
135
135
  const lower = model.toLowerCase();
136
136
  return (
137
+ lower.includes('claude-opus-4-7') ||
138
+ lower.includes('claude-opus-4.7') ||
137
139
  lower.includes('claude-opus-4-6') ||
138
140
  lower.includes('claude-opus-4.6') ||
139
141
  lower.includes('claude-sonnet-4-6') ||
@@ -12,11 +12,21 @@ export type RunOpts = {
12
12
  projectRoot: string;
13
13
  oneShot?: boolean;
14
14
  userContext?: string;
15
+ estimatedInputTokens?: number;
15
16
  reasoningText?: boolean;
16
17
  reasoningLevel?: ReasoningLevel;
17
18
  abortSignal?: AbortSignal;
19
+ /**
20
+ * Omits prior session history from prompt assembly only. The run still emits
21
+ * events, tool calls, and persisted message parts in the current session.
22
+ */
23
+ omitHistory?: boolean;
18
24
  isCompactCommand?: boolean;
19
25
  compactionContext?: string;
26
+ additionalPromptMessages?: Array<{
27
+ role: 'system' | 'user';
28
+ content: string;
29
+ }>;
20
30
  toolApprovalMode?: ToolApprovalMode;
21
31
  compactionRetries?: number;
22
32
  continuationCount?: number;
@@ -1,6 +1,6 @@
1
1
  import { publish } from '../../events/bus.ts';
2
2
 
3
- export type ToolApprovalMode = 'auto' | 'dangerous' | 'all';
3
+ export type ToolApprovalMode = 'auto' | 'dangerous' | 'all' | 'yolo';
4
4
 
5
5
  export const DANGEROUS_TOOLS = new Set([
6
6
  'bash',
@@ -36,12 +36,16 @@ export function requiresApproval(
36
36
  mode: ToolApprovalMode,
37
37
  ): boolean {
38
38
  if (SAFE_TOOLS.has(toolName)) return false;
39
- if (mode === 'auto') return false;
39
+ if (mode === 'auto' || mode === 'yolo') return false;
40
40
  if (mode === 'all') return true;
41
41
  if (mode === 'dangerous') return DANGEROUS_TOOLS.has(toolName);
42
42
  return false;
43
43
  }
44
44
 
45
+ export function skipsGuardApproval(mode?: ToolApprovalMode): boolean {
46
+ return mode === 'yolo';
47
+ }
48
+
45
49
  export async function requestApproval(
46
50
  sessionId: string,
47
51
  messageId: string,
@@ -16,6 +16,7 @@ import {
16
16
  import {
17
17
  requiresApproval,
18
18
  requestApproval,
19
+ skipsGuardApproval,
19
20
  } from '../runtime/tools/approval.ts';
20
21
  import { guardToolCall } from '../runtime/tools/guards.ts';
21
22
 
@@ -398,7 +399,11 @@ export function adaptTools(
398
399
  if (guard.type === 'block') {
399
400
  meta.blocked = true;
400
401
  meta.blockReason = guard.reason;
401
- } else if (guard.type === 'approve' && !meta.approvalPromise) {
402
+ } else if (
403
+ guard.type === 'approve' &&
404
+ !meta.approvalPromise &&
405
+ !skipsGuardApproval(ctx.toolApprovalMode)
406
+ ) {
402
407
  meta.approvalPromise = requestApproval(
403
408
  ctx.sessionId,
404
409
  ctx.messageId,
package/src/ws.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { createBunWebSocket } from 'hono/bun';
2
+
3
+ const { upgradeWebSocket, websocket } = createBunWebSocket();
4
+
5
+ export { upgradeWebSocket, websocket };