@ottocode/server 0.1.173
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 +42 -0
- package/src/events/bus.ts +43 -0
- package/src/events/types.ts +32 -0
- package/src/index.ts +281 -0
- package/src/openapi/helpers.ts +64 -0
- package/src/openapi/paths/ask.ts +70 -0
- package/src/openapi/paths/config.ts +218 -0
- package/src/openapi/paths/files.ts +72 -0
- package/src/openapi/paths/git.ts +457 -0
- package/src/openapi/paths/messages.ts +92 -0
- package/src/openapi/paths/sessions.ts +90 -0
- package/src/openapi/paths/setu.ts +154 -0
- package/src/openapi/paths/stream.ts +26 -0
- package/src/openapi/paths/terminals.ts +226 -0
- package/src/openapi/schemas.ts +345 -0
- package/src/openapi/spec.ts +49 -0
- package/src/presets.ts +85 -0
- package/src/routes/ask.ts +113 -0
- package/src/routes/auth.ts +592 -0
- package/src/routes/branch.ts +106 -0
- package/src/routes/config/agents.ts +44 -0
- package/src/routes/config/cwd.ts +21 -0
- package/src/routes/config/defaults.ts +45 -0
- package/src/routes/config/index.ts +16 -0
- package/src/routes/config/main.ts +73 -0
- package/src/routes/config/models.ts +139 -0
- package/src/routes/config/providers.ts +46 -0
- package/src/routes/config/utils.ts +120 -0
- package/src/routes/files.ts +218 -0
- package/src/routes/git/branch.ts +75 -0
- package/src/routes/git/commit.ts +209 -0
- package/src/routes/git/diff.ts +137 -0
- package/src/routes/git/index.ts +18 -0
- package/src/routes/git/push.ts +160 -0
- package/src/routes/git/schemas.ts +48 -0
- package/src/routes/git/staging.ts +208 -0
- package/src/routes/git/status.ts +83 -0
- package/src/routes/git/types.ts +31 -0
- package/src/routes/git/utils.ts +249 -0
- package/src/routes/openapi.ts +6 -0
- package/src/routes/research.ts +392 -0
- package/src/routes/root.ts +5 -0
- package/src/routes/session-approval.ts +63 -0
- package/src/routes/session-files.ts +387 -0
- package/src/routes/session-messages.ts +170 -0
- package/src/routes/session-stream.ts +61 -0
- package/src/routes/sessions.ts +814 -0
- package/src/routes/setu.ts +346 -0
- package/src/routes/terminals.ts +227 -0
- package/src/runtime/agent/registry.ts +351 -0
- package/src/runtime/agent/runner-reasoning.ts +108 -0
- package/src/runtime/agent/runner-setup.ts +257 -0
- package/src/runtime/agent/runner.ts +375 -0
- package/src/runtime/agent-registry.ts +6 -0
- package/src/runtime/ask/service.ts +369 -0
- package/src/runtime/context/environment.ts +202 -0
- package/src/runtime/debug/index.ts +117 -0
- package/src/runtime/debug/state.ts +140 -0
- package/src/runtime/errors/api-error.ts +192 -0
- package/src/runtime/errors/handling.ts +199 -0
- package/src/runtime/message/compaction-auto.ts +154 -0
- package/src/runtime/message/compaction-context.ts +101 -0
- package/src/runtime/message/compaction-detect.ts +26 -0
- package/src/runtime/message/compaction-limits.ts +37 -0
- package/src/runtime/message/compaction-mark.ts +111 -0
- package/src/runtime/message/compaction-prune.ts +75 -0
- package/src/runtime/message/compaction.ts +21 -0
- package/src/runtime/message/history-builder.ts +266 -0
- package/src/runtime/message/service.ts +468 -0
- package/src/runtime/message/tool-history-tracker.ts +204 -0
- package/src/runtime/prompt/builder.ts +167 -0
- package/src/runtime/provider/anthropic.ts +50 -0
- package/src/runtime/provider/copilot.ts +12 -0
- package/src/runtime/provider/google.ts +8 -0
- package/src/runtime/provider/index.ts +60 -0
- package/src/runtime/provider/moonshot.ts +8 -0
- package/src/runtime/provider/oauth-adapter.ts +237 -0
- package/src/runtime/provider/openai.ts +18 -0
- package/src/runtime/provider/opencode.ts +7 -0
- package/src/runtime/provider/openrouter.ts +7 -0
- package/src/runtime/provider/selection.ts +118 -0
- package/src/runtime/provider/setu.ts +126 -0
- package/src/runtime/provider/zai.ts +16 -0
- package/src/runtime/session/branch.ts +280 -0
- package/src/runtime/session/db-operations.ts +285 -0
- package/src/runtime/session/manager.ts +99 -0
- package/src/runtime/session/queue.ts +243 -0
- package/src/runtime/stream/abort-handler.ts +65 -0
- package/src/runtime/stream/error-handler.ts +371 -0
- package/src/runtime/stream/finish-handler.ts +101 -0
- package/src/runtime/stream/handlers.ts +5 -0
- package/src/runtime/stream/step-finish.ts +93 -0
- package/src/runtime/stream/types.ts +25 -0
- package/src/runtime/tools/approval.ts +180 -0
- package/src/runtime/tools/context.ts +83 -0
- package/src/runtime/tools/mapping.ts +154 -0
- package/src/runtime/tools/setup.ts +44 -0
- package/src/runtime/topup/manager.ts +110 -0
- package/src/runtime/utils/cwd.ts +69 -0
- package/src/runtime/utils/token.ts +35 -0
- package/src/tools/adapter.ts +634 -0
- package/src/tools/database/get-parent-session.ts +183 -0
- package/src/tools/database/get-session-context.ts +161 -0
- package/src/tools/database/index.ts +42 -0
- package/src/tools/database/present-session-links.ts +47 -0
- package/src/tools/database/query-messages.ts +160 -0
- package/src/tools/database/query-sessions.ts +126 -0
- package/src/tools/database/search-history.ts +135 -0
- package/src/types/sql-imports.d.ts +5 -0
- package/sst-env.d.ts +8 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { hasToolCall, streamText } from 'ai';
|
|
2
|
+
import { messageParts } from '@ottocode/database/schema';
|
|
3
|
+
import { eq } from 'drizzle-orm';
|
|
4
|
+
import { publish, subscribe } from '../../events/bus.ts';
|
|
5
|
+
import { debugLog, time } from '../debug/index.ts';
|
|
6
|
+
import { toErrorPayload } from '../errors/handling.ts';
|
|
7
|
+
import {
|
|
8
|
+
type RunOpts,
|
|
9
|
+
setRunning,
|
|
10
|
+
dequeueJob,
|
|
11
|
+
cleanupSession,
|
|
12
|
+
} from '../session/queue.ts';
|
|
13
|
+
import {
|
|
14
|
+
updateSessionTokensIncremental,
|
|
15
|
+
updateMessageTokensIncremental,
|
|
16
|
+
completeAssistantMessage,
|
|
17
|
+
cleanupEmptyTextParts,
|
|
18
|
+
} from '../session/db-operations.ts';
|
|
19
|
+
import {
|
|
20
|
+
createStepFinishHandler,
|
|
21
|
+
createErrorHandler,
|
|
22
|
+
createAbortHandler,
|
|
23
|
+
createFinishHandler,
|
|
24
|
+
} from '../stream/handlers.ts';
|
|
25
|
+
import { pruneSession } from '../message/compaction.ts';
|
|
26
|
+
import { triggerDeferredTitleGeneration } from '../message/service.ts';
|
|
27
|
+
import { setupRunner } from './runner-setup.ts';
|
|
28
|
+
import {
|
|
29
|
+
type ReasoningState,
|
|
30
|
+
handleReasoningStart,
|
|
31
|
+
handleReasoningDelta,
|
|
32
|
+
handleReasoningEnd,
|
|
33
|
+
} from './runner-reasoning.ts';
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
enqueueAssistantRun,
|
|
37
|
+
abortSession,
|
|
38
|
+
abortMessage,
|
|
39
|
+
removeFromQueue,
|
|
40
|
+
getQueueState,
|
|
41
|
+
getRunnerState,
|
|
42
|
+
} from '../session/queue.ts';
|
|
43
|
+
|
|
44
|
+
export async function runSessionLoop(sessionId: string) {
|
|
45
|
+
setRunning(sessionId, true);
|
|
46
|
+
|
|
47
|
+
while (true) {
|
|
48
|
+
const job = await dequeueJob(sessionId);
|
|
49
|
+
if (!job) break;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
await runAssistant(job);
|
|
53
|
+
} catch (_err) {
|
|
54
|
+
// Swallow to keep the loop alive; event published by runner
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setRunning(sessionId, false);
|
|
59
|
+
cleanupSession(sessionId);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function runAssistant(opts: RunOpts) {
|
|
63
|
+
const separator = '='.repeat(72);
|
|
64
|
+
debugLog(separator);
|
|
65
|
+
debugLog(
|
|
66
|
+
`[RUNNER] Starting turn for session ${opts.sessionId}, message ${opts.assistantMessageId}`,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const setup = await setupRunner(opts);
|
|
70
|
+
const {
|
|
71
|
+
cfg,
|
|
72
|
+
db,
|
|
73
|
+
history,
|
|
74
|
+
system,
|
|
75
|
+
additionalSystemMessages,
|
|
76
|
+
model,
|
|
77
|
+
effectiveMaxOutputTokens,
|
|
78
|
+
toolset,
|
|
79
|
+
sharedCtx,
|
|
80
|
+
firstToolTimer,
|
|
81
|
+
firstToolSeen,
|
|
82
|
+
providerOptions,
|
|
83
|
+
} = setup;
|
|
84
|
+
|
|
85
|
+
const isFirstMessage = !history.some((m) => m.role === 'assistant');
|
|
86
|
+
|
|
87
|
+
const messagesWithSystemInstructions: Array<{
|
|
88
|
+
role: string;
|
|
89
|
+
content: string | Array<unknown>;
|
|
90
|
+
}> = [...additionalSystemMessages, ...history];
|
|
91
|
+
|
|
92
|
+
if (!isFirstMessage) {
|
|
93
|
+
messagesWithSystemInstructions.push({
|
|
94
|
+
role: 'user',
|
|
95
|
+
content:
|
|
96
|
+
'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
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
debugLog(
|
|
101
|
+
`[RUNNER] messagesWithSystemInstructions length: ${messagesWithSystemInstructions.length}`,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
let _finishObserved = false;
|
|
105
|
+
const unsubscribeFinish = subscribe(opts.sessionId, (evt) => {
|
|
106
|
+
if (evt.type !== 'tool.result') return;
|
|
107
|
+
try {
|
|
108
|
+
const name = (evt.payload as { name?: string } | undefined)?.name;
|
|
109
|
+
if (name === 'finish') _finishObserved = true;
|
|
110
|
+
} catch {}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const streamStartTimer = time('runner:first-delta');
|
|
114
|
+
let firstDeltaSeen = false;
|
|
115
|
+
|
|
116
|
+
let currentPartId: string | null = null;
|
|
117
|
+
let accumulated = '';
|
|
118
|
+
let stepIndex = 0;
|
|
119
|
+
|
|
120
|
+
const getCurrentPartId = () => currentPartId;
|
|
121
|
+
const getStepIndex = () => stepIndex;
|
|
122
|
+
const updateCurrentPartId = (id: string | null) => {
|
|
123
|
+
currentPartId = id;
|
|
124
|
+
};
|
|
125
|
+
const updateAccumulated = (text: string) => {
|
|
126
|
+
accumulated = text;
|
|
127
|
+
};
|
|
128
|
+
const incrementStepIndex = () => {
|
|
129
|
+
stepIndex += 1;
|
|
130
|
+
return stepIndex;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const reasoningStates = new Map<string, ReasoningState>();
|
|
134
|
+
|
|
135
|
+
const onStepFinish = createStepFinishHandler(
|
|
136
|
+
opts,
|
|
137
|
+
db,
|
|
138
|
+
getStepIndex,
|
|
139
|
+
incrementStepIndex,
|
|
140
|
+
getCurrentPartId,
|
|
141
|
+
updateCurrentPartId,
|
|
142
|
+
updateAccumulated,
|
|
143
|
+
sharedCtx,
|
|
144
|
+
updateSessionTokensIncremental,
|
|
145
|
+
updateMessageTokensIncremental,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const onError = createErrorHandler(
|
|
149
|
+
opts,
|
|
150
|
+
db,
|
|
151
|
+
getStepIndex,
|
|
152
|
+
sharedCtx,
|
|
153
|
+
runSessionLoop,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const onAbort = createAbortHandler(opts, db, getStepIndex, sharedCtx);
|
|
157
|
+
|
|
158
|
+
const onFinish = createFinishHandler(opts, db, completeAssistantMessage);
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const result = streamText({
|
|
162
|
+
model,
|
|
163
|
+
tools: toolset,
|
|
164
|
+
...(system ? { system } : {}),
|
|
165
|
+
// biome-ignore lint/suspicious/noExplicitAny: AI SDK message types are complex
|
|
166
|
+
messages: messagesWithSystemInstructions as any,
|
|
167
|
+
...(effectiveMaxOutputTokens
|
|
168
|
+
? { maxOutputTokens: effectiveMaxOutputTokens }
|
|
169
|
+
: {}),
|
|
170
|
+
...(Object.keys(providerOptions).length > 0 ? { providerOptions } : {}),
|
|
171
|
+
abortSignal: opts.abortSignal,
|
|
172
|
+
stopWhen: hasToolCall('finish'),
|
|
173
|
+
// biome-ignore lint/suspicious/noExplicitAny: AI SDK callback types mismatch
|
|
174
|
+
onStepFinish: onStepFinish as any,
|
|
175
|
+
// biome-ignore lint/suspicious/noExplicitAny: AI SDK callback types mismatch
|
|
176
|
+
onError: onError as any,
|
|
177
|
+
// biome-ignore lint/suspicious/noExplicitAny: AI SDK callback types mismatch
|
|
178
|
+
onAbort: onAbort as any,
|
|
179
|
+
// biome-ignore lint/suspicious/noExplicitAny: AI SDK callback types mismatch
|
|
180
|
+
onFinish: onFinish as any,
|
|
181
|
+
// biome-ignore lint/suspicious/noExplicitAny: AI SDK streamText options type
|
|
182
|
+
} as any);
|
|
183
|
+
|
|
184
|
+
for await (const part of result.fullStream) {
|
|
185
|
+
if (!part) continue;
|
|
186
|
+
|
|
187
|
+
if (part.type === 'text-delta') {
|
|
188
|
+
const delta = part.text;
|
|
189
|
+
if (!delta) continue;
|
|
190
|
+
|
|
191
|
+
accumulated += delta;
|
|
192
|
+
|
|
193
|
+
if (!currentPartId && !accumulated.trim()) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!firstDeltaSeen) {
|
|
198
|
+
firstDeltaSeen = true;
|
|
199
|
+
streamStartTimer.end();
|
|
200
|
+
if (isFirstMessage) {
|
|
201
|
+
void triggerDeferredTitleGeneration({
|
|
202
|
+
cfg,
|
|
203
|
+
db,
|
|
204
|
+
sessionId: opts.sessionId,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!currentPartId) {
|
|
210
|
+
currentPartId = crypto.randomUUID();
|
|
211
|
+
sharedCtx.assistantPartId = currentPartId;
|
|
212
|
+
await db.insert(messageParts).values({
|
|
213
|
+
id: currentPartId,
|
|
214
|
+
messageId: opts.assistantMessageId,
|
|
215
|
+
index: await sharedCtx.nextIndex(),
|
|
216
|
+
stepIndex: null,
|
|
217
|
+
type: 'text',
|
|
218
|
+
content: JSON.stringify({ text: accumulated }),
|
|
219
|
+
agent: opts.agent,
|
|
220
|
+
provider: opts.provider,
|
|
221
|
+
model: opts.model,
|
|
222
|
+
startedAt: Date.now(),
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
publish({
|
|
227
|
+
type: 'message.part.delta',
|
|
228
|
+
sessionId: opts.sessionId,
|
|
229
|
+
payload: {
|
|
230
|
+
messageId: opts.assistantMessageId,
|
|
231
|
+
partId: currentPartId,
|
|
232
|
+
stepIndex,
|
|
233
|
+
delta,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
await db
|
|
237
|
+
.update(messageParts)
|
|
238
|
+
.set({ content: JSON.stringify({ text: accumulated }) })
|
|
239
|
+
.where(eq(messageParts.id, currentPartId));
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (part.type === 'reasoning-start') {
|
|
244
|
+
const reasoningId = part.id;
|
|
245
|
+
if (!reasoningId) continue;
|
|
246
|
+
await handleReasoningStart(
|
|
247
|
+
reasoningId,
|
|
248
|
+
part.providerMetadata,
|
|
249
|
+
opts,
|
|
250
|
+
db,
|
|
251
|
+
sharedCtx,
|
|
252
|
+
getStepIndex,
|
|
253
|
+
reasoningStates,
|
|
254
|
+
);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (part.type === 'reasoning-delta') {
|
|
259
|
+
await handleReasoningDelta(
|
|
260
|
+
part.id,
|
|
261
|
+
part.text,
|
|
262
|
+
part.providerMetadata,
|
|
263
|
+
opts,
|
|
264
|
+
db,
|
|
265
|
+
getStepIndex,
|
|
266
|
+
reasoningStates,
|
|
267
|
+
);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (part.type === 'reasoning-end') {
|
|
272
|
+
await handleReasoningEnd(part.id, db, reasoningStates);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const fs = firstToolSeen();
|
|
277
|
+
if (!fs && !_finishObserved) {
|
|
278
|
+
publish({
|
|
279
|
+
type: 'finish-step',
|
|
280
|
+
sessionId: opts.sessionId,
|
|
281
|
+
payload: { reason: 'no-tool-calls' },
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
unsubscribeFinish();
|
|
286
|
+
await cleanupEmptyTextParts(opts, db);
|
|
287
|
+
firstToolTimer.end({ seen: firstToolSeen() });
|
|
288
|
+
|
|
289
|
+
debugLog(
|
|
290
|
+
`[RUNNER] Stream finished. finishSeen=${_finishObserved}, firstToolSeen=${fs}`,
|
|
291
|
+
);
|
|
292
|
+
} catch (err) {
|
|
293
|
+
unsubscribeFinish();
|
|
294
|
+
const payload = toErrorPayload(err);
|
|
295
|
+
|
|
296
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
297
|
+
const errorCode = (err as { code?: string })?.code ?? '';
|
|
298
|
+
const responseBody = (err as { responseBody?: string })?.responseBody ?? '';
|
|
299
|
+
const apiErrorType = (err as { apiErrorType?: string })?.apiErrorType ?? '';
|
|
300
|
+
const combinedError = `${errorMessage} ${responseBody}`.toLowerCase();
|
|
301
|
+
|
|
302
|
+
const isPromptTooLong =
|
|
303
|
+
combinedError.includes('prompt is too long') ||
|
|
304
|
+
combinedError.includes('maximum context length') ||
|
|
305
|
+
combinedError.includes('too many tokens') ||
|
|
306
|
+
combinedError.includes('context_length_exceeded') ||
|
|
307
|
+
combinedError.includes('request too large') ||
|
|
308
|
+
combinedError.includes('exceeds the model') ||
|
|
309
|
+
combinedError.includes('input is too long') ||
|
|
310
|
+
errorCode === 'context_length_exceeded' ||
|
|
311
|
+
apiErrorType === 'invalid_request_error';
|
|
312
|
+
|
|
313
|
+
debugLog(
|
|
314
|
+
`[RUNNER] isPromptTooLong: ${isPromptTooLong}, isCompactCommand: ${opts.isCompactCommand}`,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
if (isPromptTooLong && !opts.isCompactCommand) {
|
|
318
|
+
debugLog('[RUNNER] Prompt too long - auto-compacting');
|
|
319
|
+
try {
|
|
320
|
+
const pruneResult = await pruneSession(db, opts.sessionId);
|
|
321
|
+
debugLog(
|
|
322
|
+
`[RUNNER] Auto-pruned ${pruneResult.pruned} parts, saved ~${pruneResult.saved} tokens`,
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
publish({
|
|
326
|
+
type: 'error',
|
|
327
|
+
sessionId: opts.sessionId,
|
|
328
|
+
payload: {
|
|
329
|
+
...payload,
|
|
330
|
+
message: `Context too large. Auto-compacted old tool results. Please retry your message.`,
|
|
331
|
+
name: 'ContextOverflow',
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
await completeAssistantMessage({}, opts, db);
|
|
337
|
+
} catch {}
|
|
338
|
+
return;
|
|
339
|
+
} catch (pruneErr) {
|
|
340
|
+
debugLog(
|
|
341
|
+
`[RUNNER] Auto-prune failed: ${pruneErr instanceof Error ? pruneErr.message : String(pruneErr)}`,
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
debugLog(`[RUNNER] Error during stream: ${payload.message}`);
|
|
347
|
+
publish({
|
|
348
|
+
type: 'error',
|
|
349
|
+
sessionId: opts.sessionId,
|
|
350
|
+
payload,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
await updateSessionTokensIncremental(
|
|
355
|
+
{ inputTokens: 0, outputTokens: 0 },
|
|
356
|
+
undefined,
|
|
357
|
+
opts,
|
|
358
|
+
db,
|
|
359
|
+
);
|
|
360
|
+
await updateMessageTokensIncremental(
|
|
361
|
+
{ inputTokens: 0, outputTokens: 0 },
|
|
362
|
+
undefined,
|
|
363
|
+
opts,
|
|
364
|
+
db,
|
|
365
|
+
);
|
|
366
|
+
await completeAssistantMessage({}, opts, db);
|
|
367
|
+
} catch {}
|
|
368
|
+
throw err;
|
|
369
|
+
} finally {
|
|
370
|
+
debugLog(
|
|
371
|
+
`[RUNNER] Turn complete for session ${opts.sessionId}, message ${opts.assistantMessageId}`,
|
|
372
|
+
);
|
|
373
|
+
debugLog(separator);
|
|
374
|
+
}
|
|
375
|
+
}
|