@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,99 @@
|
|
|
1
|
+
import { desc, eq } from 'drizzle-orm';
|
|
2
|
+
import type { OttoConfig } from '@ottocode/sdk';
|
|
3
|
+
import type { DB } from '@ottocode/database';
|
|
4
|
+
import { sessions } from '@ottocode/database/schema';
|
|
5
|
+
import {
|
|
6
|
+
validateProviderModel,
|
|
7
|
+
isProviderAuthorized,
|
|
8
|
+
ensureProviderEnv,
|
|
9
|
+
type ProviderId,
|
|
10
|
+
} from '@ottocode/sdk';
|
|
11
|
+
import { publish } from '../../events/bus.ts';
|
|
12
|
+
|
|
13
|
+
type SessionRow = typeof sessions.$inferSelect;
|
|
14
|
+
|
|
15
|
+
type CreateSessionInput = {
|
|
16
|
+
db: DB;
|
|
17
|
+
cfg: OttoConfig;
|
|
18
|
+
agent: string;
|
|
19
|
+
provider: ProviderId;
|
|
20
|
+
model: string;
|
|
21
|
+
title?: string | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export async function createSession({
|
|
25
|
+
db,
|
|
26
|
+
cfg,
|
|
27
|
+
agent,
|
|
28
|
+
provider,
|
|
29
|
+
model,
|
|
30
|
+
title,
|
|
31
|
+
}: CreateSessionInput): Promise<SessionRow> {
|
|
32
|
+
validateProviderModel(provider, model);
|
|
33
|
+
const authorized = await isProviderAuthorized(cfg, provider);
|
|
34
|
+
if (!authorized) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Provider ${provider} is not configured. Run \`otto auth login\` to add credentials.`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
await ensureProviderEnv(cfg, provider);
|
|
40
|
+
const id = crypto.randomUUID();
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
const row = {
|
|
43
|
+
id,
|
|
44
|
+
title: title ?? null,
|
|
45
|
+
agent,
|
|
46
|
+
provider,
|
|
47
|
+
model,
|
|
48
|
+
projectPath: cfg.projectRoot,
|
|
49
|
+
createdAt: now,
|
|
50
|
+
lastActiveAt: now,
|
|
51
|
+
totalInputTokens: null,
|
|
52
|
+
totalOutputTokens: null,
|
|
53
|
+
totalCachedTokens: null,
|
|
54
|
+
totalCacheCreationTokens: null,
|
|
55
|
+
totalReasoningTokens: null,
|
|
56
|
+
totalToolTimeMs: null,
|
|
57
|
+
toolCountsJson: null,
|
|
58
|
+
currentContextTokens: null,
|
|
59
|
+
};
|
|
60
|
+
await db.insert(sessions).values(row);
|
|
61
|
+
publish({ type: 'session.created', sessionId: id, payload: row });
|
|
62
|
+
return row;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type GetSessionInput = {
|
|
66
|
+
db: DB;
|
|
67
|
+
projectPath: string;
|
|
68
|
+
sessionId: string;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export async function getSessionById({
|
|
72
|
+
db,
|
|
73
|
+
projectPath,
|
|
74
|
+
sessionId,
|
|
75
|
+
}: GetSessionInput): Promise<SessionRow | undefined> {
|
|
76
|
+
const rows = await db
|
|
77
|
+
.select()
|
|
78
|
+
.from(sessions)
|
|
79
|
+
.where(eq(sessions.id, sessionId));
|
|
80
|
+
if (!rows.length) return undefined;
|
|
81
|
+
const row = rows[0];
|
|
82
|
+
if (row.projectPath !== projectPath) return undefined;
|
|
83
|
+
return row;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
type GetLastSessionInput = { db: DB; projectPath: string };
|
|
87
|
+
|
|
88
|
+
export async function getLastSession({
|
|
89
|
+
db,
|
|
90
|
+
projectPath,
|
|
91
|
+
}: GetLastSessionInput): Promise<SessionRow | undefined> {
|
|
92
|
+
const rows = await db
|
|
93
|
+
.select()
|
|
94
|
+
.from(sessions)
|
|
95
|
+
.where(eq(sessions.projectPath, projectPath))
|
|
96
|
+
.orderBy(desc(sessions.lastActiveAt), desc(sessions.createdAt))
|
|
97
|
+
.limit(1);
|
|
98
|
+
return rows[0];
|
|
99
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type { ProviderName } from '../provider/index.ts';
|
|
2
|
+
import { publish } from '../../events/bus.ts';
|
|
3
|
+
import type { ToolApprovalMode } from '../tools/approval.ts';
|
|
4
|
+
|
|
5
|
+
export type RunOpts = {
|
|
6
|
+
sessionId: string;
|
|
7
|
+
assistantMessageId: string;
|
|
8
|
+
agent: string;
|
|
9
|
+
provider: ProviderName;
|
|
10
|
+
model: string;
|
|
11
|
+
projectRoot: string;
|
|
12
|
+
oneShot?: boolean;
|
|
13
|
+
userContext?: string;
|
|
14
|
+
reasoningText?: boolean;
|
|
15
|
+
abortSignal?: AbortSignal;
|
|
16
|
+
isCompactCommand?: boolean;
|
|
17
|
+
compactionContext?: string;
|
|
18
|
+
toolApprovalMode?: ToolApprovalMode;
|
|
19
|
+
compactionRetries?: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type QueuedMessage = {
|
|
23
|
+
messageId: string;
|
|
24
|
+
position: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type RunnerState = {
|
|
28
|
+
queue: RunOpts[];
|
|
29
|
+
running: boolean;
|
|
30
|
+
currentMessageId: string | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Global state for session queues
|
|
34
|
+
const runners = new Map<string, RunnerState>();
|
|
35
|
+
|
|
36
|
+
// Track active abort controllers per MESSAGE (not session)
|
|
37
|
+
const messageAbortControllers = new Map<string, AbortController>();
|
|
38
|
+
|
|
39
|
+
function publishQueueState(sessionId: string) {
|
|
40
|
+
const state = runners.get(sessionId);
|
|
41
|
+
if (!state) return;
|
|
42
|
+
|
|
43
|
+
const queuedMessages: QueuedMessage[] = state.queue.map((opts, index) => ({
|
|
44
|
+
messageId: opts.assistantMessageId,
|
|
45
|
+
position: index,
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
publish({
|
|
49
|
+
type: 'queue.updated',
|
|
50
|
+
sessionId,
|
|
51
|
+
payload: {
|
|
52
|
+
currentMessageId: state.currentMessageId,
|
|
53
|
+
queuedMessages,
|
|
54
|
+
queueLength: state.queue.length,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Enqueues an assistant run for a given session.
|
|
61
|
+
* Creates an abort controller per message.
|
|
62
|
+
*/
|
|
63
|
+
export function enqueueAssistantRun(
|
|
64
|
+
opts: Omit<RunOpts, 'abortSignal'>,
|
|
65
|
+
processQueueFn: (sessionId: string) => Promise<void>,
|
|
66
|
+
) {
|
|
67
|
+
const abortController = new AbortController();
|
|
68
|
+
messageAbortControllers.set(opts.assistantMessageId, abortController);
|
|
69
|
+
|
|
70
|
+
const state = runners.get(opts.sessionId) ?? {
|
|
71
|
+
queue: [],
|
|
72
|
+
running: false,
|
|
73
|
+
currentMessageId: null,
|
|
74
|
+
};
|
|
75
|
+
state.queue.push({ ...opts, abortSignal: abortController.signal });
|
|
76
|
+
runners.set(opts.sessionId, state);
|
|
77
|
+
|
|
78
|
+
publishQueueState(opts.sessionId);
|
|
79
|
+
|
|
80
|
+
if (!state.running) void processQueueFn(opts.sessionId);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Aborts the currently running message for a session.
|
|
85
|
+
* Optionally clears the queue.
|
|
86
|
+
*/
|
|
87
|
+
export function abortSession(sessionId: string, clearQueue = false) {
|
|
88
|
+
const state = runners.get(sessionId);
|
|
89
|
+
if (!state) return;
|
|
90
|
+
|
|
91
|
+
// Abort the currently running message
|
|
92
|
+
if (state.currentMessageId) {
|
|
93
|
+
const controller = messageAbortControllers.get(state.currentMessageId);
|
|
94
|
+
if (controller) {
|
|
95
|
+
controller.abort();
|
|
96
|
+
messageAbortControllers.delete(state.currentMessageId);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Optionally clear the queue and abort all queued messages
|
|
101
|
+
if (clearQueue && state.queue.length > 0) {
|
|
102
|
+
for (const opts of state.queue) {
|
|
103
|
+
const controller = messageAbortControllers.get(opts.assistantMessageId);
|
|
104
|
+
if (controller) {
|
|
105
|
+
controller.abort();
|
|
106
|
+
messageAbortControllers.delete(opts.assistantMessageId);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
state.queue = [];
|
|
110
|
+
publishQueueState(sessionId);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Aborts a specific message by its ID.
|
|
116
|
+
* If it's currently running, aborts the stream.
|
|
117
|
+
* If it's queued, removes it from the queue.
|
|
118
|
+
*/
|
|
119
|
+
export function abortMessage(
|
|
120
|
+
sessionId: string,
|
|
121
|
+
messageId: string,
|
|
122
|
+
): { removed: boolean; wasRunning: boolean } {
|
|
123
|
+
const state = runners.get(sessionId);
|
|
124
|
+
if (!state) return { removed: false, wasRunning: false };
|
|
125
|
+
|
|
126
|
+
// Check if this is the currently running message
|
|
127
|
+
if (state.currentMessageId === messageId) {
|
|
128
|
+
const controller = messageAbortControllers.get(messageId);
|
|
129
|
+
if (controller) {
|
|
130
|
+
controller.abort();
|
|
131
|
+
messageAbortControllers.delete(messageId);
|
|
132
|
+
}
|
|
133
|
+
return { removed: true, wasRunning: true };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check if it's in the queue
|
|
137
|
+
const index = state.queue.findIndex(
|
|
138
|
+
(opts) => opts.assistantMessageId === messageId,
|
|
139
|
+
);
|
|
140
|
+
if (index !== -1) {
|
|
141
|
+
state.queue.splice(index, 1);
|
|
142
|
+
const controller = messageAbortControllers.get(messageId);
|
|
143
|
+
if (controller) {
|
|
144
|
+
controller.abort();
|
|
145
|
+
messageAbortControllers.delete(messageId);
|
|
146
|
+
}
|
|
147
|
+
publishQueueState(sessionId);
|
|
148
|
+
return { removed: true, wasRunning: false };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { removed: false, wasRunning: false };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Removes a queued message (not the currently running one).
|
|
156
|
+
*/
|
|
157
|
+
export function removeFromQueue(sessionId: string, messageId: string): boolean {
|
|
158
|
+
const state = runners.get(sessionId);
|
|
159
|
+
if (!state) return false;
|
|
160
|
+
|
|
161
|
+
// Don't allow removing the currently running message via this function
|
|
162
|
+
if (state.currentMessageId === messageId) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const index = state.queue.findIndex(
|
|
167
|
+
(opts) => opts.assistantMessageId === messageId,
|
|
168
|
+
);
|
|
169
|
+
if (index === -1) return false;
|
|
170
|
+
|
|
171
|
+
state.queue.splice(index, 1);
|
|
172
|
+
const controller = messageAbortControllers.get(messageId);
|
|
173
|
+
if (controller) {
|
|
174
|
+
controller.abort();
|
|
175
|
+
messageAbortControllers.delete(messageId);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
publishQueueState(sessionId);
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Gets the current queue state for a session.
|
|
184
|
+
*/
|
|
185
|
+
export function getQueueState(sessionId: string): {
|
|
186
|
+
currentMessageId: string | null;
|
|
187
|
+
queuedMessages: QueuedMessage[];
|
|
188
|
+
isRunning: boolean;
|
|
189
|
+
} | null {
|
|
190
|
+
const state = runners.get(sessionId);
|
|
191
|
+
if (!state) return null;
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
currentMessageId: state.currentMessageId,
|
|
195
|
+
queuedMessages: state.queue.map((opts, index) => ({
|
|
196
|
+
messageId: opts.assistantMessageId,
|
|
197
|
+
position: index,
|
|
198
|
+
})),
|
|
199
|
+
isRunning: state.running,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function getRunnerState(
|
|
204
|
+
sessionId: string,
|
|
205
|
+
): { queue: RunOpts[]; running: boolean } | undefined {
|
|
206
|
+
return runners.get(sessionId);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function setRunning(sessionId: string, running: boolean) {
|
|
210
|
+
const state = runners.get(sessionId);
|
|
211
|
+
if (state) state.running = running;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function setCurrentMessage(sessionId: string, messageId: string | null) {
|
|
215
|
+
const state = runners.get(sessionId);
|
|
216
|
+
if (state) {
|
|
217
|
+
state.currentMessageId = messageId;
|
|
218
|
+
publishQueueState(sessionId);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function dequeueJob(sessionId: string): RunOpts | undefined {
|
|
223
|
+
const state = runners.get(sessionId);
|
|
224
|
+
const job = state?.queue.shift();
|
|
225
|
+
if (job && state) {
|
|
226
|
+
state.currentMessageId = job.assistantMessageId;
|
|
227
|
+
publishQueueState(sessionId);
|
|
228
|
+
}
|
|
229
|
+
return job;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function cleanupSession(sessionId: string) {
|
|
233
|
+
const state = runners.get(sessionId);
|
|
234
|
+
if (state && state.queue.length === 0 && !state.running) {
|
|
235
|
+
// Clean up any lingering abort controller for current message
|
|
236
|
+
if (state.currentMessageId) {
|
|
237
|
+
messageAbortControllers.delete(state.currentMessageId);
|
|
238
|
+
}
|
|
239
|
+
state.currentMessageId = null;
|
|
240
|
+
runners.delete(sessionId);
|
|
241
|
+
publishQueueState(sessionId);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { getDb } from '@ottocode/database';
|
|
2
|
+
import { messages, messageParts } from '@ottocode/database/schema';
|
|
3
|
+
import { eq } from 'drizzle-orm';
|
|
4
|
+
import { publish } from '../../events/bus.ts';
|
|
5
|
+
import type { RunOpts } from '../session/queue.ts';
|
|
6
|
+
import type { ToolAdapterContext } from '../../tools/adapter.ts';
|
|
7
|
+
import type { AbortEvent } from './types.ts';
|
|
8
|
+
|
|
9
|
+
export function createAbortHandler(
|
|
10
|
+
opts: RunOpts,
|
|
11
|
+
db: Awaited<ReturnType<typeof getDb>>,
|
|
12
|
+
getStepIndex: () => number,
|
|
13
|
+
sharedCtx: ToolAdapterContext,
|
|
14
|
+
) {
|
|
15
|
+
return async ({ steps }: AbortEvent) => {
|
|
16
|
+
const stepIndex = getStepIndex();
|
|
17
|
+
|
|
18
|
+
const abortPartId = crypto.randomUUID();
|
|
19
|
+
await db.insert(messageParts).values({
|
|
20
|
+
id: abortPartId,
|
|
21
|
+
messageId: opts.assistantMessageId,
|
|
22
|
+
index: await sharedCtx.nextIndex(),
|
|
23
|
+
stepIndex,
|
|
24
|
+
type: 'error',
|
|
25
|
+
content: JSON.stringify({
|
|
26
|
+
message: 'Generation stopped by user',
|
|
27
|
+
type: 'abort',
|
|
28
|
+
isAborted: true,
|
|
29
|
+
stepsCompleted: steps.length,
|
|
30
|
+
}),
|
|
31
|
+
agent: opts.agent,
|
|
32
|
+
provider: opts.provider,
|
|
33
|
+
model: opts.model,
|
|
34
|
+
startedAt: Date.now(),
|
|
35
|
+
completedAt: Date.now(),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await db
|
|
39
|
+
.update(messages)
|
|
40
|
+
.set({
|
|
41
|
+
status: 'error',
|
|
42
|
+
error: 'Generation stopped by user',
|
|
43
|
+
errorType: 'abort',
|
|
44
|
+
errorDetails: JSON.stringify({
|
|
45
|
+
stepsCompleted: steps.length,
|
|
46
|
+
abortedAt: Date.now(),
|
|
47
|
+
}),
|
|
48
|
+
isAborted: true,
|
|
49
|
+
})
|
|
50
|
+
.where(eq(messages.id, opts.assistantMessageId));
|
|
51
|
+
|
|
52
|
+
publish({
|
|
53
|
+
type: 'error',
|
|
54
|
+
sessionId: opts.sessionId,
|
|
55
|
+
payload: {
|
|
56
|
+
messageId: opts.assistantMessageId,
|
|
57
|
+
partId: abortPartId,
|
|
58
|
+
error: 'Generation stopped by user',
|
|
59
|
+
errorType: 'abort',
|
|
60
|
+
isAborted: true,
|
|
61
|
+
stepsCompleted: steps.length,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
}
|