remote-codex 0.1.6 → 0.1.7
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/apps/supervisor-api/dist/index.js +6786 -5630
- package/apps/supervisor-web/dist/assets/{highlighted-body-OFNGDK62-DvEvXPo5.js → highlighted-body-OFNGDK62-0cYcfOfd.js} +1 -1
- package/apps/supervisor-web/dist/assets/index-CbIt0KnL.css +32 -0
- package/apps/supervisor-web/dist/assets/index-nH6a8Wwn.js +377 -0
- package/apps/supervisor-web/dist/assets/{xterm-CWQ1ih_R.js → xterm-DisVWgDR.js} +1 -1
- package/apps/supervisor-web/dist/index.html +2 -2
- package/package.json +5 -1
- package/packages/agent-runtime/src/index.ts +2 -0
- package/packages/agent-runtime/src/registry.ts +44 -0
- package/packages/agent-runtime/src/types.ts +531 -0
- package/packages/codex/src/appServerManager.test.ts +328 -0
- package/packages/codex/src/appServerManager.ts +656 -0
- package/packages/codex/src/historyItems.ts +1185 -0
- package/packages/codex/src/hookHistory.ts +224 -0
- package/packages/codex/src/index.ts +6 -0
- package/packages/codex/src/jsonrpc.test.ts +58 -0
- package/packages/codex/src/jsonrpc.ts +198 -0
- package/packages/codex/src/requestMapper.test.ts +127 -0
- package/packages/codex/src/requestMapper.ts +511 -0
- package/packages/codex/src/runtimeAdapter.ts +692 -0
- package/packages/codex/src/types.ts +403 -0
- package/packages/db/migrations/0015_agent_provider_fields.sql +14 -0
- package/packages/db/migrations/0016_remove_codex_thread_goal_id.sql +46 -0
- package/packages/db/migrations/0017_remove_codex_thread_columns.sql +85 -0
- package/packages/db/src/client.ts +53 -0
- package/packages/db/src/index.ts +5 -0
- package/packages/db/src/migrate.test.ts +36 -0
- package/packages/db/src/migrate.ts +84 -0
- package/packages/db/src/repositories.ts +893 -0
- package/packages/db/src/schema.ts +177 -0
- package/packages/db/src/seed.ts +51 -0
- package/packages/shared/src/index.ts +878 -0
- package/apps/supervisor-web/dist/assets/index-CQu6sRq7.css +0 -32
- package/apps/supervisor-web/dist/assets/index-MELw9ga_.js +0 -377
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import { and, desc, eq, inArray } from 'drizzle-orm';
|
|
4
|
+
|
|
5
|
+
import { DatabaseClient } from './client';
|
|
6
|
+
import { getDefaultHostRecord } from './client';
|
|
7
|
+
import {
|
|
8
|
+
notifications,
|
|
9
|
+
shellSessions,
|
|
10
|
+
threadActivityNotes,
|
|
11
|
+
threadForks,
|
|
12
|
+
threadGoals,
|
|
13
|
+
threadHistoryItems,
|
|
14
|
+
threadPendingSteers,
|
|
15
|
+
threadTurnMetadata,
|
|
16
|
+
threads,
|
|
17
|
+
viewerSessions,
|
|
18
|
+
policies,
|
|
19
|
+
workspaces,
|
|
20
|
+
} from './schema';
|
|
21
|
+
|
|
22
|
+
export interface CreateWorkspaceRecordInput {
|
|
23
|
+
absPath: string;
|
|
24
|
+
label: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface CreateThreadRecordInput {
|
|
28
|
+
workspaceId: string;
|
|
29
|
+
title: string;
|
|
30
|
+
provider?: string;
|
|
31
|
+
providerSessionId: string | null;
|
|
32
|
+
providerTurnId?: string | null;
|
|
33
|
+
model?: string | null;
|
|
34
|
+
reasoningEffort?: string | null;
|
|
35
|
+
fastMode?: boolean;
|
|
36
|
+
fastBaseModel?: string | null;
|
|
37
|
+
fastBaseReasoningEffort?: string | null;
|
|
38
|
+
collaborationMode?: string;
|
|
39
|
+
approvalMode: string;
|
|
40
|
+
sandboxMode?: string | null;
|
|
41
|
+
summaryText?: string | null;
|
|
42
|
+
source?: 'supervisor' | 'local_codex_import';
|
|
43
|
+
isConnected?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface UpdateThreadRecordInput {
|
|
47
|
+
provider?: string;
|
|
48
|
+
providerSessionId?: string | null;
|
|
49
|
+
providerTurnId?: string | null;
|
|
50
|
+
title?: string;
|
|
51
|
+
model?: string | null;
|
|
52
|
+
reasoningEffort?: string | null;
|
|
53
|
+
fastMode?: boolean;
|
|
54
|
+
fastBaseModel?: string | null;
|
|
55
|
+
fastBaseReasoningEffort?: string | null;
|
|
56
|
+
collaborationMode?: string;
|
|
57
|
+
approvalMode?: string;
|
|
58
|
+
sandboxMode?: string | null;
|
|
59
|
+
status?: string;
|
|
60
|
+
summaryText?: string | null;
|
|
61
|
+
lastError?: string | null;
|
|
62
|
+
lastTurnStartedAt?: string | null;
|
|
63
|
+
lastTurnCompletedAt?: string | null;
|
|
64
|
+
isConnected?: boolean;
|
|
65
|
+
updatedAt?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface UpsertThreadTurnMetadataInput {
|
|
69
|
+
threadId: string;
|
|
70
|
+
turnId: string;
|
|
71
|
+
model?: string | null;
|
|
72
|
+
reasoningEffort?: string | null;
|
|
73
|
+
reasoningEffortAvailable?: boolean | null;
|
|
74
|
+
pricingModelKey?: string | null;
|
|
75
|
+
pricingTierKey?: string | null;
|
|
76
|
+
tokenUsageJson?: string | null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface CreateThreadPendingSteerRecordInput {
|
|
80
|
+
threadId: string;
|
|
81
|
+
turnId: string;
|
|
82
|
+
clientRequestId?: string | null;
|
|
83
|
+
displayPrompt: string;
|
|
84
|
+
submittedPrompt: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface UpsertThreadHistoryItemRecordInput {
|
|
88
|
+
threadId: string;
|
|
89
|
+
turnId: string;
|
|
90
|
+
itemId: string;
|
|
91
|
+
itemJson: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface CreateThreadActivityNoteRecordInput {
|
|
95
|
+
threadId: string;
|
|
96
|
+
kind: string;
|
|
97
|
+
text: string;
|
|
98
|
+
anchorTurnId?: string | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface CreateThreadForkRecordInput {
|
|
102
|
+
sourceThreadId: string;
|
|
103
|
+
sourceTurnId?: string | null;
|
|
104
|
+
sourceTurnIndex?: number | null;
|
|
105
|
+
forkedThreadId: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface UpsertThreadGoalRecordInput {
|
|
109
|
+
threadId: string;
|
|
110
|
+
providerSessionId: string;
|
|
111
|
+
localGoalId?: string | null;
|
|
112
|
+
objective: string;
|
|
113
|
+
status: string;
|
|
114
|
+
tokenBudget?: number | null;
|
|
115
|
+
tokensUsed?: number;
|
|
116
|
+
timeUsedSeconds?: number;
|
|
117
|
+
startedAt: string;
|
|
118
|
+
completedAt?: string | null;
|
|
119
|
+
createdAt?: string;
|
|
120
|
+
updatedAt?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface CreateShellSessionRecordInput {
|
|
124
|
+
workspaceId: string;
|
|
125
|
+
threadId: string | null;
|
|
126
|
+
tmuxSessionName: string;
|
|
127
|
+
cwd: string;
|
|
128
|
+
status: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface UpdateShellSessionRecordInput {
|
|
132
|
+
tmuxSessionName?: string;
|
|
133
|
+
cwd?: string;
|
|
134
|
+
status?: string;
|
|
135
|
+
updatedAt?: string;
|
|
136
|
+
lastActivityAt?: string | null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface CreateViewerSessionRecordInput {
|
|
140
|
+
threadId: string | null;
|
|
141
|
+
shellId: string | null;
|
|
142
|
+
activeTab?: string | null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface UpdateViewerSessionRecordInput {
|
|
146
|
+
lastHeartbeatAt?: string | null;
|
|
147
|
+
activeTab?: string | null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function getPolicyRecordByKey(db: DatabaseClient, key: string) {
|
|
151
|
+
return db.select().from(policies).where(eq(policies.key, key)).get();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function upsertPolicyRecord(db: DatabaseClient, key: string, valueJson: string) {
|
|
155
|
+
const now = new Date().toISOString();
|
|
156
|
+
const existing = getPolicyRecordByKey(db, key);
|
|
157
|
+
|
|
158
|
+
if (existing) {
|
|
159
|
+
db.update(policies)
|
|
160
|
+
.set({
|
|
161
|
+
valueJson,
|
|
162
|
+
updatedAt: now
|
|
163
|
+
})
|
|
164
|
+
.where(eq(policies.key, key))
|
|
165
|
+
.run();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
db.insert(policies)
|
|
170
|
+
.values({
|
|
171
|
+
id: `policy-${key.replace(/[^a-zA-Z0-9_-]/g, '-')}`,
|
|
172
|
+
key,
|
|
173
|
+
valueJson,
|
|
174
|
+
createdAt: now,
|
|
175
|
+
updatedAt: now
|
|
176
|
+
})
|
|
177
|
+
.run();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function listWorkspaceRecords(db: DatabaseClient) {
|
|
181
|
+
return db.select().from(workspaces).orderBy(desc(workspaces.isFavorite), workspaces.label).all();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function getWorkspaceRecordById(db: DatabaseClient, id: string) {
|
|
185
|
+
return db.select().from(workspaces).where(eq(workspaces.id, id)).get();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function getWorkspaceRecordByPath(db: DatabaseClient, absPath: string) {
|
|
189
|
+
return db.select().from(workspaces).where(eq(workspaces.absPath, absPath)).get();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function createWorkspaceRecord(db: DatabaseClient, input: CreateWorkspaceRecordInput) {
|
|
193
|
+
const now = new Date().toISOString();
|
|
194
|
+
const host = getDefaultHostRecord();
|
|
195
|
+
const record = {
|
|
196
|
+
id: randomUUID(),
|
|
197
|
+
hostId: host.id,
|
|
198
|
+
label: input.label,
|
|
199
|
+
absPath: input.absPath,
|
|
200
|
+
isFavorite: false,
|
|
201
|
+
createdAt: now,
|
|
202
|
+
lastOpenedAt: null as string | null
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
db.insert(workspaces).values(record).run();
|
|
206
|
+
|
|
207
|
+
return record;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function updateWorkspaceFavorite(
|
|
211
|
+
db: DatabaseClient,
|
|
212
|
+
id: string,
|
|
213
|
+
isFavorite: boolean
|
|
214
|
+
) {
|
|
215
|
+
db.update(workspaces).set({ isFavorite }).where(eq(workspaces.id, id)).run();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function updateWorkspaceLabel(db: DatabaseClient, id: string, label: string) {
|
|
219
|
+
db.update(workspaces).set({ label }).where(eq(workspaces.id, id)).run();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function touchWorkspaceOpenedAt(db: DatabaseClient, id: string) {
|
|
223
|
+
db.update(workspaces)
|
|
224
|
+
.set({ lastOpenedAt: new Date().toISOString() })
|
|
225
|
+
.where(eq(workspaces.id, id))
|
|
226
|
+
.run();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function listThreadRecords(db: DatabaseClient) {
|
|
230
|
+
return db.select().from(threads).orderBy(desc(threads.createdAt)).all();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function listThreadRecordsByWorkspaceId(db: DatabaseClient, workspaceId: string) {
|
|
234
|
+
return db.select().from(threads).where(eq(threads.workspaceId, workspaceId)).orderBy(desc(threads.createdAt)).all();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function listThreadRecordsByIds(db: DatabaseClient, ids: string[]) {
|
|
238
|
+
if (ids.length === 0) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return db.select().from(threads).where(inArray(threads.id, ids)).all();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function getThreadRecordById(db: DatabaseClient, id: string) {
|
|
246
|
+
return db.select().from(threads).where(eq(threads.id, id)).get();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function getThreadRecordByProviderSessionId(
|
|
250
|
+
db: DatabaseClient,
|
|
251
|
+
provider: string,
|
|
252
|
+
providerSessionId: string,
|
|
253
|
+
) {
|
|
254
|
+
return db
|
|
255
|
+
.select()
|
|
256
|
+
.from(threads)
|
|
257
|
+
.where(
|
|
258
|
+
and(
|
|
259
|
+
eq(threads.provider, provider),
|
|
260
|
+
eq(threads.providerSessionId, providerSessionId),
|
|
261
|
+
),
|
|
262
|
+
)
|
|
263
|
+
.get();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function createThreadRecord(db: DatabaseClient, input: CreateThreadRecordInput) {
|
|
267
|
+
const now = new Date().toISOString();
|
|
268
|
+
const record = {
|
|
269
|
+
id: randomUUID(),
|
|
270
|
+
workspaceId: input.workspaceId,
|
|
271
|
+
provider: input.provider ?? 'codex',
|
|
272
|
+
providerSessionId: input.providerSessionId,
|
|
273
|
+
providerTurnId: input.providerTurnId ?? null,
|
|
274
|
+
source: input.source ?? 'supervisor',
|
|
275
|
+
title: input.title,
|
|
276
|
+
model: input.model ?? null,
|
|
277
|
+
reasoningEffort: input.reasoningEffort ?? null,
|
|
278
|
+
fastMode: input.fastMode ?? false,
|
|
279
|
+
fastBaseModel: input.fastBaseModel ?? null,
|
|
280
|
+
fastBaseReasoningEffort: input.fastBaseReasoningEffort ?? null,
|
|
281
|
+
collaborationMode: input.collaborationMode ?? 'default',
|
|
282
|
+
approvalMode: input.approvalMode,
|
|
283
|
+
sandboxMode: input.sandboxMode ?? null,
|
|
284
|
+
status: 'idle',
|
|
285
|
+
summaryText: input.summaryText ?? null,
|
|
286
|
+
lastError: null as string | null,
|
|
287
|
+
createdAt: now,
|
|
288
|
+
updatedAt: now,
|
|
289
|
+
lastTurnStartedAt: null as string | null,
|
|
290
|
+
lastTurnCompletedAt: null as string | null,
|
|
291
|
+
lastViewedAt: null as string | null,
|
|
292
|
+
isPinned: false,
|
|
293
|
+
isConnected: input.isConnected ?? true
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
db.insert(threads).values(record).run();
|
|
297
|
+
return record;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function updateThreadRecord(db: DatabaseClient, id: string, input: UpdateThreadRecordInput) {
|
|
301
|
+
const updates = {
|
|
302
|
+
...input,
|
|
303
|
+
updatedAt: input.updatedAt ?? new Date().toISOString()
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
db.update(threads).set(updates).where(eq(threads.id, id)).run();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function deleteThreadRecord(db: DatabaseClient, id: string) {
|
|
310
|
+
db.delete(threads).where(eq(threads.id, id)).run();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function deleteThreadsByWorkspaceId(db: DatabaseClient, workspaceId: string) {
|
|
314
|
+
db.delete(threads).where(eq(threads.workspaceId, workspaceId)).run();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function listThreadTurnMetadataByThreadId(db: DatabaseClient, threadId: string) {
|
|
318
|
+
return db.select().from(threadTurnMetadata).where(eq(threadTurnMetadata.threadId, threadId)).all();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function getLatestThreadTurnMetadataByThreadId(
|
|
322
|
+
db: DatabaseClient,
|
|
323
|
+
threadId: string,
|
|
324
|
+
) {
|
|
325
|
+
return db
|
|
326
|
+
.select()
|
|
327
|
+
.from(threadTurnMetadata)
|
|
328
|
+
.where(eq(threadTurnMetadata.threadId, threadId))
|
|
329
|
+
.orderBy(desc(threadTurnMetadata.createdAt))
|
|
330
|
+
.get();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function getThreadTurnMetadataByThreadAndTurnId(
|
|
334
|
+
db: DatabaseClient,
|
|
335
|
+
threadId: string,
|
|
336
|
+
turnId: string,
|
|
337
|
+
) {
|
|
338
|
+
return db
|
|
339
|
+
.select()
|
|
340
|
+
.from(threadTurnMetadata)
|
|
341
|
+
.where(
|
|
342
|
+
and(
|
|
343
|
+
eq(threadTurnMetadata.threadId, threadId),
|
|
344
|
+
eq(threadTurnMetadata.turnId, turnId),
|
|
345
|
+
),
|
|
346
|
+
)
|
|
347
|
+
.get();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function upsertThreadTurnMetadata(
|
|
351
|
+
db: DatabaseClient,
|
|
352
|
+
input: UpsertThreadTurnMetadataInput,
|
|
353
|
+
) {
|
|
354
|
+
const now = new Date().toISOString();
|
|
355
|
+
const existing = db
|
|
356
|
+
.select()
|
|
357
|
+
.from(threadTurnMetadata)
|
|
358
|
+
.where(
|
|
359
|
+
and(
|
|
360
|
+
eq(threadTurnMetadata.threadId, input.threadId),
|
|
361
|
+
eq(threadTurnMetadata.turnId, input.turnId),
|
|
362
|
+
),
|
|
363
|
+
)
|
|
364
|
+
.get();
|
|
365
|
+
|
|
366
|
+
if (existing) {
|
|
367
|
+
db.update(threadTurnMetadata)
|
|
368
|
+
.set({
|
|
369
|
+
model: input.model !== undefined ? input.model : existing.model,
|
|
370
|
+
reasoningEffort:
|
|
371
|
+
input.reasoningEffort !== undefined
|
|
372
|
+
? input.reasoningEffort
|
|
373
|
+
: existing.reasoningEffort,
|
|
374
|
+
reasoningEffortAvailable:
|
|
375
|
+
input.reasoningEffortAvailable !== undefined
|
|
376
|
+
? input.reasoningEffortAvailable
|
|
377
|
+
: existing.reasoningEffortAvailable,
|
|
378
|
+
pricingModelKey:
|
|
379
|
+
input.pricingModelKey !== undefined
|
|
380
|
+
? input.pricingModelKey
|
|
381
|
+
: existing.pricingModelKey,
|
|
382
|
+
pricingTierKey:
|
|
383
|
+
input.pricingTierKey !== undefined
|
|
384
|
+
? input.pricingTierKey
|
|
385
|
+
: existing.pricingTierKey,
|
|
386
|
+
tokenUsageJson:
|
|
387
|
+
input.tokenUsageJson !== undefined
|
|
388
|
+
? input.tokenUsageJson
|
|
389
|
+
: existing.tokenUsageJson,
|
|
390
|
+
updatedAt: now,
|
|
391
|
+
})
|
|
392
|
+
.where(eq(threadTurnMetadata.id, existing.id))
|
|
393
|
+
.run();
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
db.insert(threadTurnMetadata)
|
|
398
|
+
.values({
|
|
399
|
+
id: randomUUID(),
|
|
400
|
+
threadId: input.threadId,
|
|
401
|
+
turnId: input.turnId,
|
|
402
|
+
model: input.model ?? null,
|
|
403
|
+
reasoningEffort: input.reasoningEffort ?? null,
|
|
404
|
+
reasoningEffortAvailable: input.reasoningEffortAvailable ?? null,
|
|
405
|
+
pricingModelKey: input.pricingModelKey ?? null,
|
|
406
|
+
pricingTierKey: input.pricingTierKey ?? null,
|
|
407
|
+
tokenUsageJson: input.tokenUsageJson ?? null,
|
|
408
|
+
createdAt: now,
|
|
409
|
+
updatedAt: now,
|
|
410
|
+
})
|
|
411
|
+
.run();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function deleteThreadTurnMetadataByThreadId(db: DatabaseClient, threadId: string) {
|
|
415
|
+
db.delete(threadTurnMetadata).where(eq(threadTurnMetadata.threadId, threadId)).run();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function listThreadHistoryItemRecordsByThreadId(
|
|
419
|
+
db: DatabaseClient,
|
|
420
|
+
threadId: string,
|
|
421
|
+
) {
|
|
422
|
+
return db
|
|
423
|
+
.select()
|
|
424
|
+
.from(threadHistoryItems)
|
|
425
|
+
.where(eq(threadHistoryItems.threadId, threadId))
|
|
426
|
+
.orderBy(threadHistoryItems.createdAt)
|
|
427
|
+
.all();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export function upsertThreadHistoryItemRecord(
|
|
431
|
+
db: DatabaseClient,
|
|
432
|
+
input: UpsertThreadHistoryItemRecordInput,
|
|
433
|
+
) {
|
|
434
|
+
const now = new Date().toISOString();
|
|
435
|
+
const existing = db
|
|
436
|
+
.select()
|
|
437
|
+
.from(threadHistoryItems)
|
|
438
|
+
.where(
|
|
439
|
+
and(
|
|
440
|
+
eq(threadHistoryItems.threadId, input.threadId),
|
|
441
|
+
eq(threadHistoryItems.turnId, input.turnId),
|
|
442
|
+
eq(threadHistoryItems.itemId, input.itemId),
|
|
443
|
+
),
|
|
444
|
+
)
|
|
445
|
+
.get();
|
|
446
|
+
|
|
447
|
+
if (existing) {
|
|
448
|
+
db.update(threadHistoryItems)
|
|
449
|
+
.set({
|
|
450
|
+
itemJson: input.itemJson,
|
|
451
|
+
updatedAt: now,
|
|
452
|
+
})
|
|
453
|
+
.where(eq(threadHistoryItems.id, existing.id))
|
|
454
|
+
.run();
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
db.insert(threadHistoryItems)
|
|
459
|
+
.values({
|
|
460
|
+
id: randomUUID(),
|
|
461
|
+
threadId: input.threadId,
|
|
462
|
+
turnId: input.turnId,
|
|
463
|
+
itemId: input.itemId,
|
|
464
|
+
itemJson: input.itemJson,
|
|
465
|
+
createdAt: now,
|
|
466
|
+
updatedAt: now,
|
|
467
|
+
})
|
|
468
|
+
.run();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export function deleteThreadHistoryItemRecordsByThreadId(
|
|
472
|
+
db: DatabaseClient,
|
|
473
|
+
threadId: string,
|
|
474
|
+
) {
|
|
475
|
+
db.delete(threadHistoryItems).where(eq(threadHistoryItems.threadId, threadId)).run();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export function listThreadPendingSteerRecordsByThreadId(
|
|
479
|
+
db: DatabaseClient,
|
|
480
|
+
threadId: string,
|
|
481
|
+
) {
|
|
482
|
+
return db
|
|
483
|
+
.select()
|
|
484
|
+
.from(threadPendingSteers)
|
|
485
|
+
.where(eq(threadPendingSteers.threadId, threadId))
|
|
486
|
+
.orderBy(threadPendingSteers.createdAt)
|
|
487
|
+
.all();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export function createThreadPendingSteerRecord(
|
|
491
|
+
db: DatabaseClient,
|
|
492
|
+
input: CreateThreadPendingSteerRecordInput,
|
|
493
|
+
) {
|
|
494
|
+
const now = new Date().toISOString();
|
|
495
|
+
const record = {
|
|
496
|
+
id: randomUUID(),
|
|
497
|
+
threadId: input.threadId,
|
|
498
|
+
turnId: input.turnId,
|
|
499
|
+
clientRequestId: input.clientRequestId ?? null,
|
|
500
|
+
displayPrompt: input.displayPrompt,
|
|
501
|
+
submittedPrompt: input.submittedPrompt,
|
|
502
|
+
createdAt: now,
|
|
503
|
+
updatedAt: now,
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
db.insert(threadPendingSteers).values(record).run();
|
|
507
|
+
return record;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export function deleteThreadPendingSteerRecordById(db: DatabaseClient, id: string) {
|
|
511
|
+
db.delete(threadPendingSteers).where(eq(threadPendingSteers.id, id)).run();
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export function deleteThreadPendingSteerRecordsByThreadId(
|
|
515
|
+
db: DatabaseClient,
|
|
516
|
+
threadId: string,
|
|
517
|
+
) {
|
|
518
|
+
db.delete(threadPendingSteers).where(eq(threadPendingSteers.threadId, threadId)).run();
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export function listThreadActivityNotesByThreadId(
|
|
522
|
+
db: DatabaseClient,
|
|
523
|
+
threadId: string,
|
|
524
|
+
) {
|
|
525
|
+
return db
|
|
526
|
+
.select()
|
|
527
|
+
.from(threadActivityNotes)
|
|
528
|
+
.where(eq(threadActivityNotes.threadId, threadId))
|
|
529
|
+
.orderBy(threadActivityNotes.createdAt)
|
|
530
|
+
.all();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export function createThreadActivityNoteRecord(
|
|
534
|
+
db: DatabaseClient,
|
|
535
|
+
input: CreateThreadActivityNoteRecordInput,
|
|
536
|
+
) {
|
|
537
|
+
const record = {
|
|
538
|
+
id: randomUUID(),
|
|
539
|
+
threadId: input.threadId,
|
|
540
|
+
kind: input.kind,
|
|
541
|
+
text: input.text,
|
|
542
|
+
anchorTurnId: input.anchorTurnId ?? null,
|
|
543
|
+
createdAt: new Date().toISOString(),
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
db.insert(threadActivityNotes).values(record).run();
|
|
547
|
+
return record;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export function deleteThreadActivityNotesByThreadId(
|
|
551
|
+
db: DatabaseClient,
|
|
552
|
+
threadId: string,
|
|
553
|
+
) {
|
|
554
|
+
db.delete(threadActivityNotes).where(eq(threadActivityNotes.threadId, threadId)).run();
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export function listThreadForkRecordsBySourceThreadId(
|
|
558
|
+
db: DatabaseClient,
|
|
559
|
+
sourceThreadId: string,
|
|
560
|
+
) {
|
|
561
|
+
return db
|
|
562
|
+
.select()
|
|
563
|
+
.from(threadForks)
|
|
564
|
+
.where(eq(threadForks.sourceThreadId, sourceThreadId))
|
|
565
|
+
.orderBy(threadForks.createdAt)
|
|
566
|
+
.all();
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export function listThreadForkRecordsByForkedThreadId(
|
|
570
|
+
db: DatabaseClient,
|
|
571
|
+
forkedThreadId: string,
|
|
572
|
+
) {
|
|
573
|
+
return db
|
|
574
|
+
.select()
|
|
575
|
+
.from(threadForks)
|
|
576
|
+
.where(eq(threadForks.forkedThreadId, forkedThreadId))
|
|
577
|
+
.orderBy(threadForks.createdAt)
|
|
578
|
+
.all();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export function createThreadForkRecord(
|
|
582
|
+
db: DatabaseClient,
|
|
583
|
+
input: CreateThreadForkRecordInput,
|
|
584
|
+
) {
|
|
585
|
+
const record = {
|
|
586
|
+
id: randomUUID(),
|
|
587
|
+
sourceThreadId: input.sourceThreadId,
|
|
588
|
+
sourceTurnId: input.sourceTurnId ?? null,
|
|
589
|
+
sourceTurnIndex: input.sourceTurnIndex ?? null,
|
|
590
|
+
forkedThreadId: input.forkedThreadId,
|
|
591
|
+
createdAt: new Date().toISOString(),
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
db.insert(threadForks).values(record).run();
|
|
595
|
+
return record;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export function deleteThreadForkRecordsBySourceThreadId(
|
|
599
|
+
db: DatabaseClient,
|
|
600
|
+
sourceThreadId: string,
|
|
601
|
+
) {
|
|
602
|
+
db.delete(threadForks).where(eq(threadForks.sourceThreadId, sourceThreadId)).run();
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export function deleteThreadForkRecordsByForkedThreadId(
|
|
606
|
+
db: DatabaseClient,
|
|
607
|
+
forkedThreadId: string,
|
|
608
|
+
) {
|
|
609
|
+
db.delete(threadForks).where(eq(threadForks.forkedThreadId, forkedThreadId)).run();
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export function listThreadGoalRecordsByThreadId(db: DatabaseClient, threadId: string) {
|
|
613
|
+
return db
|
|
614
|
+
.select()
|
|
615
|
+
.from(threadGoals)
|
|
616
|
+
.where(eq(threadGoals.threadId, threadId))
|
|
617
|
+
.orderBy(desc(threadGoals.updatedAt))
|
|
618
|
+
.all();
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export function getActiveThreadGoalRecord(db: DatabaseClient, threadId: string) {
|
|
622
|
+
const records = db
|
|
623
|
+
.select()
|
|
624
|
+
.from(threadGoals)
|
|
625
|
+
.where(eq(threadGoals.threadId, threadId))
|
|
626
|
+
.orderBy(desc(threadGoals.updatedAt))
|
|
627
|
+
.all();
|
|
628
|
+
|
|
629
|
+
return records.find((record) =>
|
|
630
|
+
['active', 'paused', 'budgetLimited'].includes(record.status),
|
|
631
|
+
) ?? null;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function getThreadGoalRecordForUpsert(
|
|
635
|
+
db: DatabaseClient,
|
|
636
|
+
input: UpsertThreadGoalRecordInput,
|
|
637
|
+
) {
|
|
638
|
+
if (input.localGoalId) {
|
|
639
|
+
const byId = db
|
|
640
|
+
.select()
|
|
641
|
+
.from(threadGoals)
|
|
642
|
+
.where(eq(threadGoals.id, input.localGoalId))
|
|
643
|
+
.get();
|
|
644
|
+
if (byId?.threadId === input.threadId) {
|
|
645
|
+
return byId;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const active = getActiveThreadGoalRecord(db, input.threadId);
|
|
650
|
+
if (active) {
|
|
651
|
+
return active;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const matchingObjective = db
|
|
655
|
+
.select()
|
|
656
|
+
.from(threadGoals)
|
|
657
|
+
.where(
|
|
658
|
+
and(
|
|
659
|
+
eq(threadGoals.threadId, input.threadId),
|
|
660
|
+
eq(threadGoals.providerSessionId, input.providerSessionId),
|
|
661
|
+
eq(threadGoals.objective, input.objective),
|
|
662
|
+
),
|
|
663
|
+
)
|
|
664
|
+
.orderBy(desc(threadGoals.updatedAt))
|
|
665
|
+
.get();
|
|
666
|
+
if (matchingObjective) {
|
|
667
|
+
return matchingObjective;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return (
|
|
671
|
+
db
|
|
672
|
+
.select()
|
|
673
|
+
.from(threadGoals)
|
|
674
|
+
.where(
|
|
675
|
+
and(
|
|
676
|
+
eq(threadGoals.threadId, input.threadId),
|
|
677
|
+
eq(threadGoals.providerSessionId, input.providerSessionId),
|
|
678
|
+
eq(threadGoals.objective, input.objective),
|
|
679
|
+
eq(threadGoals.createdAt, input.createdAt ?? input.startedAt),
|
|
680
|
+
),
|
|
681
|
+
)
|
|
682
|
+
.orderBy(desc(threadGoals.updatedAt))
|
|
683
|
+
.get() ?? null
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
export function upsertThreadGoalRecord(
|
|
688
|
+
db: DatabaseClient,
|
|
689
|
+
input: UpsertThreadGoalRecordInput,
|
|
690
|
+
) {
|
|
691
|
+
const now = new Date().toISOString();
|
|
692
|
+
const existing = getThreadGoalRecordForUpsert(db, input);
|
|
693
|
+
const terminalCompletedAt =
|
|
694
|
+
input.completedAt ??
|
|
695
|
+
(['complete', 'terminated'].includes(input.status)
|
|
696
|
+
? input.updatedAt ?? now
|
|
697
|
+
: null);
|
|
698
|
+
|
|
699
|
+
if (existing) {
|
|
700
|
+
const updated = {
|
|
701
|
+
objective: input.objective,
|
|
702
|
+
status: input.status,
|
|
703
|
+
tokenBudget: input.tokenBudget ?? null,
|
|
704
|
+
tokensUsed: input.tokensUsed ?? existing.tokensUsed,
|
|
705
|
+
timeUsedSeconds: input.timeUsedSeconds ?? existing.timeUsedSeconds,
|
|
706
|
+
providerSessionId: input.providerSessionId,
|
|
707
|
+
startedAt: input.startedAt,
|
|
708
|
+
completedAt: terminalCompletedAt,
|
|
709
|
+
updatedAt: input.updatedAt ?? now,
|
|
710
|
+
};
|
|
711
|
+
db.update(threadGoals).set(updated).where(eq(threadGoals.id, existing.id)).run();
|
|
712
|
+
return { ...existing, ...updated };
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const record = {
|
|
716
|
+
id: randomUUID(),
|
|
717
|
+
threadId: input.threadId,
|
|
718
|
+
providerSessionId: input.providerSessionId,
|
|
719
|
+
objective: input.objective,
|
|
720
|
+
status: input.status,
|
|
721
|
+
tokenBudget: input.tokenBudget ?? null,
|
|
722
|
+
tokensUsed: input.tokensUsed ?? 0,
|
|
723
|
+
timeUsedSeconds: input.timeUsedSeconds ?? 0,
|
|
724
|
+
startedAt: input.startedAt,
|
|
725
|
+
completedAt: terminalCompletedAt,
|
|
726
|
+
createdAt: input.createdAt ?? now,
|
|
727
|
+
updatedAt: input.updatedAt ?? now,
|
|
728
|
+
};
|
|
729
|
+
db.insert(threadGoals).values(record).run();
|
|
730
|
+
return record;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
export function markActiveThreadGoalRecordTerminated(
|
|
734
|
+
db: DatabaseClient,
|
|
735
|
+
threadId: string,
|
|
736
|
+
) {
|
|
737
|
+
const existing = getActiveThreadGoalRecord(db, threadId);
|
|
738
|
+
if (!existing) {
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const now = new Date().toISOString();
|
|
743
|
+
const updates = {
|
|
744
|
+
status: 'terminated',
|
|
745
|
+
completedAt: now,
|
|
746
|
+
updatedAt: now,
|
|
747
|
+
};
|
|
748
|
+
db.update(threadGoals).set(updates).where(eq(threadGoals.id, existing.id)).run();
|
|
749
|
+
return { ...existing, ...updates };
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
export function deleteThreadGoalRecordsByThreadId(db: DatabaseClient, threadId: string) {
|
|
753
|
+
db.delete(threadGoals).where(eq(threadGoals.threadId, threadId)).run();
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export function listShellSessionRecords(db: DatabaseClient) {
|
|
757
|
+
return db.select().from(shellSessions).orderBy(desc(shellSessions.updatedAt)).all();
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
export function listShellSessionRecordsByWorkspaceId(db: DatabaseClient, workspaceId: string) {
|
|
761
|
+
return db
|
|
762
|
+
.select()
|
|
763
|
+
.from(shellSessions)
|
|
764
|
+
.where(eq(shellSessions.workspaceId, workspaceId))
|
|
765
|
+
.orderBy(desc(shellSessions.updatedAt))
|
|
766
|
+
.all();
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
export function getShellSessionRecordById(db: DatabaseClient, id: string) {
|
|
770
|
+
return db.select().from(shellSessions).where(eq(shellSessions.id, id)).get();
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
export function getShellSessionRecordByThreadId(db: DatabaseClient, threadId: string) {
|
|
774
|
+
return db.select().from(shellSessions).where(eq(shellSessions.threadId, threadId)).get();
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
export function createShellSessionRecord(
|
|
778
|
+
db: DatabaseClient,
|
|
779
|
+
input: CreateShellSessionRecordInput,
|
|
780
|
+
) {
|
|
781
|
+
const now = new Date().toISOString();
|
|
782
|
+
const record = {
|
|
783
|
+
id: randomUUID(),
|
|
784
|
+
workspaceId: input.workspaceId,
|
|
785
|
+
threadId: input.threadId,
|
|
786
|
+
tmuxSessionName: input.tmuxSessionName,
|
|
787
|
+
cwd: input.cwd,
|
|
788
|
+
status: input.status,
|
|
789
|
+
createdAt: now,
|
|
790
|
+
updatedAt: now,
|
|
791
|
+
lastActivityAt: now,
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
db.insert(shellSessions).values(record).run();
|
|
795
|
+
return record;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export function updateShellSessionRecord(
|
|
799
|
+
db: DatabaseClient,
|
|
800
|
+
id: string,
|
|
801
|
+
input: UpdateShellSessionRecordInput,
|
|
802
|
+
) {
|
|
803
|
+
const updates = {
|
|
804
|
+
...input,
|
|
805
|
+
updatedAt: input.updatedAt ?? new Date().toISOString(),
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
db.update(shellSessions).set(updates).where(eq(shellSessions.id, id)).run();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
export function deleteShellSessionRecord(db: DatabaseClient, id: string) {
|
|
812
|
+
db.delete(shellSessions).where(eq(shellSessions.id, id)).run();
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
export function deleteShellSessionsByThreadId(db: DatabaseClient, threadId: string) {
|
|
816
|
+
db.delete(shellSessions).where(eq(shellSessions.threadId, threadId)).run();
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
export function deleteShellSessionsByWorkspaceId(db: DatabaseClient, workspaceId: string) {
|
|
820
|
+
db.delete(shellSessions).where(eq(shellSessions.workspaceId, workspaceId)).run();
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export function createViewerSessionRecord(
|
|
824
|
+
db: DatabaseClient,
|
|
825
|
+
input: CreateViewerSessionRecordInput,
|
|
826
|
+
) {
|
|
827
|
+
const now = new Date().toISOString();
|
|
828
|
+
const record = {
|
|
829
|
+
id: randomUUID(),
|
|
830
|
+
threadId: input.threadId ?? null,
|
|
831
|
+
shellId: input.shellId ?? null,
|
|
832
|
+
connectedAt: now,
|
|
833
|
+
lastHeartbeatAt: now,
|
|
834
|
+
activeTab: input.activeTab ?? null,
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
db.insert(viewerSessions).values(record).run();
|
|
838
|
+
return record;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
export function getViewerSessionRecordById(db: DatabaseClient, id: string) {
|
|
842
|
+
return db.select().from(viewerSessions).where(eq(viewerSessions.id, id)).get();
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
export function getViewerSessionRecordByShellId(db: DatabaseClient, shellId: string) {
|
|
846
|
+
return db.select().from(viewerSessions).where(eq(viewerSessions.shellId, shellId)).get();
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
export function updateViewerSessionRecord(
|
|
850
|
+
db: DatabaseClient,
|
|
851
|
+
id: string,
|
|
852
|
+
input: UpdateViewerSessionRecordInput,
|
|
853
|
+
) {
|
|
854
|
+
db.update(viewerSessions)
|
|
855
|
+
.set(input)
|
|
856
|
+
.where(eq(viewerSessions.id, id))
|
|
857
|
+
.run();
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
export function clearViewerSessionShell(db: DatabaseClient, id: string) {
|
|
861
|
+
db.update(viewerSessions)
|
|
862
|
+
.set({
|
|
863
|
+
shellId: null,
|
|
864
|
+
lastHeartbeatAt: new Date().toISOString(),
|
|
865
|
+
activeTab: null,
|
|
866
|
+
})
|
|
867
|
+
.where(eq(viewerSessions.id, id))
|
|
868
|
+
.run();
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
export function deleteViewerSessionRecord(db: DatabaseClient, id: string) {
|
|
872
|
+
db.delete(viewerSessions).where(eq(viewerSessions.id, id)).run();
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
export function deleteViewerSessionsByShellId(db: DatabaseClient, shellId: string) {
|
|
876
|
+
db.delete(viewerSessions).where(eq(viewerSessions.shellId, shellId)).run();
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
export function deleteViewerSessionsByThreadId(db: DatabaseClient, threadId: string) {
|
|
880
|
+
db.delete(viewerSessions).where(eq(viewerSessions.threadId, threadId)).run();
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
export function deleteAllViewerSessionRecords(db: DatabaseClient) {
|
|
884
|
+
db.delete(viewerSessions).run();
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
export function deleteNotificationsByThreadId(db: DatabaseClient, threadId: string) {
|
|
888
|
+
db.delete(notifications).where(eq(notifications.threadId, threadId)).run();
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
export function deleteWorkspaceRecord(db: DatabaseClient, id: string) {
|
|
892
|
+
db.delete(workspaces).where(eq(workspaces.id, id)).run();
|
|
893
|
+
}
|