@sentry/junior 0.75.0 → 0.76.1
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/README.md +1 -1
- package/bin/junior.mjs +4 -66
- package/dist/agent-hooks-ZOE7RIED.js +37 -0
- package/dist/api-reference.d.ts +2 -0
- package/dist/app.js +364 -135
- package/dist/build/virtual-config.d.ts +2 -2
- package/dist/chat/agent-dispatch/runner.d.ts +2 -0
- package/dist/chat/config.d.ts +1 -0
- package/dist/chat/credentials/state-adapter-token-store.d.ts +2 -0
- package/dist/chat/credentials/user-token-store.d.ts +17 -12
- package/dist/chat/db.d.ts +8 -0
- package/dist/chat/mcp/auth-store.d.ts +2 -1
- package/dist/chat/mcp/oauth.d.ts +2 -1
- package/dist/chat/oauth-flow.d.ts +3 -1
- package/dist/chat/pi/client.d.ts +15 -7
- package/dist/chat/plugins/agent-hooks.d.ts +7 -0
- package/dist/chat/plugins/auth/oauth-request.d.ts +11 -7
- package/dist/chat/plugins/model.d.ts +9 -0
- package/dist/chat/plugins/prompt.d.ts +5 -0
- package/dist/chat/plugins/task-callback.d.ts +5 -0
- package/dist/chat/plugins/task-message.d.ts +23 -0
- package/dist/chat/plugins/task-queue.d.ts +5 -0
- package/dist/chat/plugins/task-runner.d.ts +12 -0
- package/dist/chat/plugins/task-signing.d.ts +31 -0
- package/dist/chat/prompt.d.ts +4 -0
- package/dist/chat/requester.d.ts +6 -5
- package/dist/chat/respond-helpers.d.ts +2 -0
- package/dist/chat/respond.d.ts +4 -2
- package/dist/chat/runtime/agent-continue-runner.d.ts +4 -0
- package/dist/chat/runtime/reply-executor.d.ts +5 -1
- package/dist/chat/runtime/slack-resume.d.ts +10 -2
- package/dist/chat/sentry.d.ts +1 -0
- package/dist/chat/services/mcp-auth-orchestration.d.ts +2 -1
- package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -1
- package/dist/chat/services/subscribed-decision.d.ts +2 -2
- package/dist/chat/services/turn-session-record.d.ts +11 -7
- package/dist/chat/slack/footer.d.ts +1 -1
- package/dist/chat/state/turn-session.d.ts +8 -5
- package/dist/chat/tools/agent-tools.d.ts +8 -1
- package/dist/chat/tools/slack/context.d.ts +2 -2
- package/dist/chat/tools/types.d.ts +4 -4
- package/dist/chat/vercel-queue-client.d.ts +3 -0
- package/dist/{chunk-C3AM4Z4J.js → chunk-2ECJXSVQ.js} +5 -5
- package/dist/{chunk-OJODNL2P.js → chunk-4SCWV7TJ.js} +2 -2
- package/dist/chunk-4UO6FK4G.js +64 -0
- package/dist/{chunk-BNJIEFQC.js → chunk-56TBVRJG.js} +2 -2
- package/dist/{chunk-OK4KKR7B.js → chunk-EJN6G5A2.js} +28 -12
- package/dist/{chunk-TQ74BATR.js → chunk-FFGXUXMD.js} +435 -111
- package/dist/{chunk-XJHDZUGD.js → chunk-JBASI5VV.js} +4 -4
- package/dist/chunk-KNFROR7R.js +127 -0
- package/dist/{chunk-VNTLUFTY.js → chunk-KOIMO7S3.js} +126 -87
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/{chunk-NPVUAXUE.js → chunk-NFTMTIP3.js} +303 -33
- package/dist/chunk-NYKJ3KON.js +1082 -0
- package/dist/{chunk-SJHUF3DP.js → chunk-OJ53FYVG.js} +2 -10
- package/dist/{chunk-62FUNJYS.js → chunk-Q6XFTRV5.js} +54 -3
- package/dist/{chunk-UJ7OTHPO.js → chunk-R6Z5XWY3.js} +12 -670
- package/dist/chunk-RV5RYIJW.js +56 -0
- package/dist/{chunk-EE6PJWY4.js → chunk-SG5WAA7H.js} +7 -5
- package/dist/chunk-ST6YNAXG.js +54 -0
- package/dist/{chunk-FCZO7LAR.js → chunk-T77LUIX3.js} +139 -153
- package/dist/{chunk-EIYL7I4S.js → chunk-VALUBQ7R.js} +22 -30
- package/dist/{chunk-OZSPLAQ4.js → chunk-XBBC6W45.js} +1 -1
- package/dist/{chunk-ZNNTSPNF.js → chunk-Y5OFBCBZ.js} +1 -1
- package/dist/{chunk-74HO27II.js → chunk-Z4CIQ3EB.js} +5 -1
- package/dist/{chunk-2RWFUS5F.js → chunk-ZLMBNBUG.js} +101 -44
- package/dist/{chunk-JEELK46E.js → chunk-ZQB37HUX.js} +11 -11
- package/dist/cli/chat.js +52 -23
- package/dist/cli/check.js +7 -7
- package/dist/cli/env.js +4 -53
- package/dist/cli/init.js +6 -1
- package/dist/cli/main.js +84 -0
- package/dist/cli/plugins.js +244 -0
- package/dist/cli/run.js +5 -52
- package/dist/cli/snapshot-warmup.js +9 -9
- package/dist/cli/upgrade.js +167 -48
- package/dist/db-7A7PFRGL.js +17 -0
- package/dist/deployment.d.ts +1 -0
- package/dist/instrumentation.js +14 -18
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +43 -22
- package/dist/plugins-PZMDS7AT.js +15 -0
- package/dist/plugins.d.ts +4 -2
- package/dist/{registry-NLZFIW23.js → registry-OIPAJU2O.js} +6 -6
- package/dist/reporting.js +34 -26
- package/dist/{runner-LUQZ5G67.js → runner-7Z4D6AKV.js} +76 -23
- package/dist/sentry-4CP5NNQ5.js +31 -0
- package/dist/validation-SLA6IGF7.js +15 -0
- package/dist/vercel.js +1 -1
- package/package.json +8 -7
- package/dist/agent-hooks-2HEB4C3Q.js +0 -33
- package/dist/chat/conversations/configured.d.ts +0 -7
- package/dist/chat/conversations/state.d.ts +0 -4
- package/dist/chat/plugins/db.d.ts +0 -31
- package/dist/chunk-2KG3PWR4.js +0 -17
- package/dist/chunk-D7NFH5GD.js +0 -570
- package/dist/chunk-MCMROINU.js +0 -12
- package/dist/chunk-WBZ4M5N5.js +0 -59
- package/dist/db-A3ILH67H.js +0 -20
- package/dist/plugins-OMJKLRJ2.js +0 -13
- package/dist/validation-VMCPP3YO.js +0 -15
|
@@ -0,0 +1,1082 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseDestination,
|
|
3
|
+
sameDestination
|
|
4
|
+
} from "./chunk-Q6XFTRV5.js";
|
|
5
|
+
import {
|
|
6
|
+
getChatConfig
|
|
7
|
+
} from "./chunk-T77LUIX3.js";
|
|
8
|
+
import {
|
|
9
|
+
parseStoredSlackRequester
|
|
10
|
+
} from "./chunk-VALUBQ7R.js";
|
|
11
|
+
|
|
12
|
+
// src/chat/conversations/sql/store.ts
|
|
13
|
+
import { randomUUID } from "crypto";
|
|
14
|
+
import { asc, desc, eq, sql as sql2 } from "drizzle-orm";
|
|
15
|
+
|
|
16
|
+
// src/chat/conversations/sql/migrations.ts
|
|
17
|
+
import { createHash } from "crypto";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
|
|
20
|
+
// src/chat/conversations/sql/schema/conversations.ts
|
|
21
|
+
import { sql } from "drizzle-orm";
|
|
22
|
+
import { index as index3, integer, jsonb as jsonb3, pgTable as pgTable3, text as text3 } from "drizzle-orm/pg-core";
|
|
23
|
+
|
|
24
|
+
// src/chat/conversations/sql/schema/destinations.ts
|
|
25
|
+
import { index, jsonb, pgTable, text, uniqueIndex } from "drizzle-orm/pg-core";
|
|
26
|
+
|
|
27
|
+
// src/chat/conversations/sql/schema/timestamps.ts
|
|
28
|
+
import { timestamp } from "drizzle-orm/pg-core";
|
|
29
|
+
var timestamptz = (name) => timestamp(name, { withTimezone: true });
|
|
30
|
+
|
|
31
|
+
// src/chat/conversations/sql/schema/destinations.ts
|
|
32
|
+
var juniorDestinations = pgTable(
|
|
33
|
+
"junior_destinations",
|
|
34
|
+
{
|
|
35
|
+
id: text("id").primaryKey(),
|
|
36
|
+
provider: text("provider").notNull(),
|
|
37
|
+
providerTenantId: text("provider_tenant_id").notNull().default(""),
|
|
38
|
+
providerDestinationId: text("provider_destination_id").notNull(),
|
|
39
|
+
kind: text("kind").$type().notNull(),
|
|
40
|
+
parentDestinationId: text("parent_destination_id"),
|
|
41
|
+
displayName: text("display_name"),
|
|
42
|
+
visibility: text("visibility").$type().notNull().default("unknown"),
|
|
43
|
+
metadata: jsonb("metadata_json"),
|
|
44
|
+
createdAt: timestamptz("created_at").notNull(),
|
|
45
|
+
updatedAt: timestamptz("updated_at").notNull()
|
|
46
|
+
},
|
|
47
|
+
(table) => [
|
|
48
|
+
uniqueIndex("junior_destinations_provider_destination_uidx").on(
|
|
49
|
+
table.provider,
|
|
50
|
+
table.providerTenantId,
|
|
51
|
+
table.providerDestinationId
|
|
52
|
+
),
|
|
53
|
+
index("junior_destinations_provider_kind_idx").on(
|
|
54
|
+
table.provider,
|
|
55
|
+
table.kind
|
|
56
|
+
)
|
|
57
|
+
]
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// src/chat/conversations/sql/schema/identities.ts
|
|
61
|
+
import { index as index2, jsonb as jsonb2, pgTable as pgTable2, text as text2, uniqueIndex as uniqueIndex2 } from "drizzle-orm/pg-core";
|
|
62
|
+
var juniorIdentities = pgTable2(
|
|
63
|
+
"junior_identities",
|
|
64
|
+
{
|
|
65
|
+
id: text2("id").primaryKey(),
|
|
66
|
+
kind: text2("kind").$type().notNull(),
|
|
67
|
+
provider: text2("provider").notNull(),
|
|
68
|
+
providerTenantId: text2("provider_tenant_id").notNull().default(""),
|
|
69
|
+
providerSubjectId: text2("provider_subject_id").notNull(),
|
|
70
|
+
displayName: text2("display_name"),
|
|
71
|
+
handle: text2("handle"),
|
|
72
|
+
email: text2("email"),
|
|
73
|
+
avatarUrl: text2("avatar_url"),
|
|
74
|
+
metadata: jsonb2("metadata_json"),
|
|
75
|
+
createdAt: timestamptz("created_at").notNull(),
|
|
76
|
+
updatedAt: timestamptz("updated_at").notNull()
|
|
77
|
+
},
|
|
78
|
+
(table) => [
|
|
79
|
+
uniqueIndex2("junior_identities_provider_subject_uidx").on(
|
|
80
|
+
table.provider,
|
|
81
|
+
table.providerTenantId,
|
|
82
|
+
table.providerSubjectId
|
|
83
|
+
),
|
|
84
|
+
index2("junior_identities_kind_provider_idx").on(table.kind, table.provider)
|
|
85
|
+
]
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// src/chat/conversations/sql/schema/conversations.ts
|
|
89
|
+
var juniorConversations = pgTable3(
|
|
90
|
+
"junior_conversations",
|
|
91
|
+
{
|
|
92
|
+
conversationId: text3("conversation_id").primaryKey(),
|
|
93
|
+
schemaVersion: integer("schema_version").notNull().default(1),
|
|
94
|
+
source: text3("source").$type(),
|
|
95
|
+
originType: text3("origin_type"),
|
|
96
|
+
originId: text3("origin_id"),
|
|
97
|
+
originRunId: text3("origin_run_id"),
|
|
98
|
+
destinationId: text3("destination_id").references(
|
|
99
|
+
() => juniorDestinations.id
|
|
100
|
+
),
|
|
101
|
+
destination: jsonb3("destination_json").$type(),
|
|
102
|
+
actorIdentityId: text3("actor_identity_id").references(
|
|
103
|
+
() => juniorIdentities.id
|
|
104
|
+
),
|
|
105
|
+
requesterIdentityId: text3("requester_identity_id").references(
|
|
106
|
+
() => juniorIdentities.id
|
|
107
|
+
),
|
|
108
|
+
creatorIdentityId: text3("creator_identity_id").references(
|
|
109
|
+
() => juniorIdentities.id
|
|
110
|
+
),
|
|
111
|
+
credentialSubjectIdentityId: text3(
|
|
112
|
+
"credential_subject_identity_id"
|
|
113
|
+
).references(() => juniorIdentities.id),
|
|
114
|
+
requester: jsonb3("requester_json").$type(),
|
|
115
|
+
channelName: text3("channel_name"),
|
|
116
|
+
title: text3("title"),
|
|
117
|
+
createdAt: timestamptz("created_at").notNull(),
|
|
118
|
+
lastActivityAt: timestamptz("last_activity_at").notNull(),
|
|
119
|
+
updatedAt: timestamptz("updated_at").notNull(),
|
|
120
|
+
executionUpdatedAt: timestamptz("execution_updated_at"),
|
|
121
|
+
executionStatus: text3("execution_status").$type().notNull(),
|
|
122
|
+
runId: text3("run_id"),
|
|
123
|
+
lastCheckpointAt: timestamptz("last_checkpoint_at"),
|
|
124
|
+
lastEnqueuedAt: timestamptz("last_enqueued_at")
|
|
125
|
+
},
|
|
126
|
+
(table) => [
|
|
127
|
+
index3("junior_conversations_last_activity_idx").on(
|
|
128
|
+
table.lastActivityAt.desc(),
|
|
129
|
+
table.conversationId
|
|
130
|
+
),
|
|
131
|
+
index3("junior_conversations_active_idx").using(
|
|
132
|
+
"btree",
|
|
133
|
+
sql`coalesce(${table.executionUpdatedAt}, ${table.updatedAt})`,
|
|
134
|
+
table.conversationId
|
|
135
|
+
).where(sql`${table.executionStatus} <> 'idle'`),
|
|
136
|
+
index3("junior_conversations_destination_activity_idx").on(
|
|
137
|
+
table.destinationId,
|
|
138
|
+
table.lastActivityAt.desc()
|
|
139
|
+
),
|
|
140
|
+
index3("junior_conversations_actor_activity_idx").on(
|
|
141
|
+
table.actorIdentityId,
|
|
142
|
+
table.lastActivityAt.desc()
|
|
143
|
+
),
|
|
144
|
+
index3("junior_conversations_requester_activity_idx").on(
|
|
145
|
+
table.requesterIdentityId,
|
|
146
|
+
table.lastActivityAt.desc()
|
|
147
|
+
),
|
|
148
|
+
index3("junior_conversations_origin_idx").on(
|
|
149
|
+
table.originType,
|
|
150
|
+
table.originId,
|
|
151
|
+
table.lastActivityAt.desc()
|
|
152
|
+
)
|
|
153
|
+
]
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// src/chat/conversations/sql/schema/migrations.ts
|
|
157
|
+
import { pgTable as pgTable4, text as text4 } from "drizzle-orm/pg-core";
|
|
158
|
+
var juniorSchemaMigrations = pgTable4("junior_schema_migrations", {
|
|
159
|
+
id: text4("id").primaryKey(),
|
|
160
|
+
checksum: text4("checksum").notNull(),
|
|
161
|
+
appliedAt: timestamptz("applied_at").notNull().defaultNow()
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// src/chat/conversations/sql/schema.ts
|
|
165
|
+
var schema = {
|
|
166
|
+
juniorConversations,
|
|
167
|
+
juniorDestinations,
|
|
168
|
+
juniorIdentities,
|
|
169
|
+
juniorSchemaMigrations
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// src/chat/conversations/sql/migrations.ts
|
|
173
|
+
var MIGRATION_LOCK_NAME = "junior_conversation_schema";
|
|
174
|
+
var migrationRecordSchema = z.object({
|
|
175
|
+
id: z.string().min(1),
|
|
176
|
+
checksum: z.string().min(1)
|
|
177
|
+
}).strict();
|
|
178
|
+
function checksumStatements(statements) {
|
|
179
|
+
const hash = createHash("sha256");
|
|
180
|
+
for (const statement of statements) {
|
|
181
|
+
hash.update(statement);
|
|
182
|
+
hash.update("\0");
|
|
183
|
+
}
|
|
184
|
+
return hash.digest("hex");
|
|
185
|
+
}
|
|
186
|
+
function defineMigration(id, statements) {
|
|
187
|
+
return {
|
|
188
|
+
id,
|
|
189
|
+
checksum: checksumStatements(statements),
|
|
190
|
+
statements
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
var createMigrationTable = `
|
|
194
|
+
CREATE TABLE IF NOT EXISTS junior_schema_migrations (
|
|
195
|
+
id TEXT PRIMARY KEY,
|
|
196
|
+
checksum TEXT NOT NULL,
|
|
197
|
+
applied_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
198
|
+
)
|
|
199
|
+
`;
|
|
200
|
+
var coreMetadataStatements = [
|
|
201
|
+
`
|
|
202
|
+
CREATE TABLE IF NOT EXISTS junior_identities (
|
|
203
|
+
id TEXT PRIMARY KEY,
|
|
204
|
+
kind TEXT NOT NULL,
|
|
205
|
+
provider TEXT NOT NULL,
|
|
206
|
+
provider_tenant_id TEXT NOT NULL DEFAULT '',
|
|
207
|
+
provider_subject_id TEXT NOT NULL,
|
|
208
|
+
display_name TEXT,
|
|
209
|
+
handle TEXT,
|
|
210
|
+
email TEXT,
|
|
211
|
+
avatar_url TEXT,
|
|
212
|
+
metadata_json JSONB,
|
|
213
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
214
|
+
updated_at TIMESTAMPTZ NOT NULL
|
|
215
|
+
)
|
|
216
|
+
`,
|
|
217
|
+
`
|
|
218
|
+
CREATE UNIQUE INDEX IF NOT EXISTS junior_identities_provider_subject_uidx
|
|
219
|
+
ON junior_identities (provider, provider_tenant_id, provider_subject_id)
|
|
220
|
+
`,
|
|
221
|
+
`
|
|
222
|
+
CREATE INDEX IF NOT EXISTS junior_identities_kind_provider_idx
|
|
223
|
+
ON junior_identities (kind, provider)
|
|
224
|
+
`,
|
|
225
|
+
`
|
|
226
|
+
CREATE TABLE IF NOT EXISTS junior_destinations (
|
|
227
|
+
id TEXT PRIMARY KEY,
|
|
228
|
+
provider TEXT NOT NULL,
|
|
229
|
+
provider_tenant_id TEXT NOT NULL DEFAULT '',
|
|
230
|
+
provider_destination_id TEXT NOT NULL,
|
|
231
|
+
kind TEXT NOT NULL,
|
|
232
|
+
parent_destination_id TEXT,
|
|
233
|
+
display_name TEXT,
|
|
234
|
+
visibility TEXT NOT NULL DEFAULT 'unknown',
|
|
235
|
+
metadata_json JSONB,
|
|
236
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
237
|
+
updated_at TIMESTAMPTZ NOT NULL
|
|
238
|
+
)
|
|
239
|
+
`,
|
|
240
|
+
`
|
|
241
|
+
CREATE UNIQUE INDEX IF NOT EXISTS junior_destinations_provider_destination_uidx
|
|
242
|
+
ON junior_destinations (provider, provider_tenant_id, provider_destination_id)
|
|
243
|
+
`,
|
|
244
|
+
`
|
|
245
|
+
CREATE INDEX IF NOT EXISTS junior_destinations_provider_kind_idx
|
|
246
|
+
ON junior_destinations (provider, kind)
|
|
247
|
+
`,
|
|
248
|
+
`
|
|
249
|
+
CREATE TABLE IF NOT EXISTS junior_conversations (
|
|
250
|
+
conversation_id TEXT PRIMARY KEY,
|
|
251
|
+
schema_version INTEGER NOT NULL DEFAULT 1,
|
|
252
|
+
source TEXT,
|
|
253
|
+
origin_type TEXT,
|
|
254
|
+
origin_id TEXT,
|
|
255
|
+
origin_run_id TEXT,
|
|
256
|
+
destination_id TEXT REFERENCES junior_destinations (id),
|
|
257
|
+
destination_json JSONB,
|
|
258
|
+
actor_identity_id TEXT REFERENCES junior_identities (id),
|
|
259
|
+
requester_identity_id TEXT REFERENCES junior_identities (id),
|
|
260
|
+
creator_identity_id TEXT REFERENCES junior_identities (id),
|
|
261
|
+
credential_subject_identity_id TEXT REFERENCES junior_identities (id),
|
|
262
|
+
requester_json JSONB,
|
|
263
|
+
channel_name TEXT,
|
|
264
|
+
title TEXT,
|
|
265
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
266
|
+
last_activity_at TIMESTAMPTZ NOT NULL,
|
|
267
|
+
updated_at TIMESTAMPTZ NOT NULL,
|
|
268
|
+
execution_updated_at TIMESTAMPTZ,
|
|
269
|
+
execution_status TEXT NOT NULL,
|
|
270
|
+
run_id TEXT,
|
|
271
|
+
last_checkpoint_at TIMESTAMPTZ,
|
|
272
|
+
last_enqueued_at TIMESTAMPTZ
|
|
273
|
+
)
|
|
274
|
+
`,
|
|
275
|
+
`
|
|
276
|
+
CREATE INDEX IF NOT EXISTS junior_conversations_last_activity_idx
|
|
277
|
+
ON junior_conversations (last_activity_at DESC, conversation_id)
|
|
278
|
+
`,
|
|
279
|
+
`
|
|
280
|
+
CREATE INDEX IF NOT EXISTS junior_conversations_active_idx
|
|
281
|
+
ON junior_conversations (coalesce(execution_updated_at, updated_at) ASC, conversation_id)
|
|
282
|
+
WHERE execution_status <> 'idle'
|
|
283
|
+
`,
|
|
284
|
+
`
|
|
285
|
+
CREATE INDEX IF NOT EXISTS junior_conversations_destination_activity_idx
|
|
286
|
+
ON junior_conversations (destination_id, last_activity_at DESC)
|
|
287
|
+
`,
|
|
288
|
+
`
|
|
289
|
+
CREATE INDEX IF NOT EXISTS junior_conversations_actor_activity_idx
|
|
290
|
+
ON junior_conversations (actor_identity_id, last_activity_at DESC)
|
|
291
|
+
`,
|
|
292
|
+
`
|
|
293
|
+
CREATE INDEX IF NOT EXISTS junior_conversations_requester_activity_idx
|
|
294
|
+
ON junior_conversations (requester_identity_id, last_activity_at DESC)
|
|
295
|
+
`,
|
|
296
|
+
`
|
|
297
|
+
CREATE INDEX IF NOT EXISTS junior_conversations_origin_idx
|
|
298
|
+
ON junior_conversations (origin_type, origin_id, last_activity_at DESC)
|
|
299
|
+
`
|
|
300
|
+
];
|
|
301
|
+
var migrations = [
|
|
302
|
+
defineMigration("0001_conversation_core", coreMetadataStatements)
|
|
303
|
+
];
|
|
304
|
+
function parseStoredMigrationRecord(value) {
|
|
305
|
+
return migrationRecordSchema.parse(value);
|
|
306
|
+
}
|
|
307
|
+
async function listAppliedMigrations(executor) {
|
|
308
|
+
const rows = await executor.query(
|
|
309
|
+
"SELECT id, checksum FROM junior_schema_migrations ORDER BY id ASC"
|
|
310
|
+
);
|
|
311
|
+
const records = /* @__PURE__ */ new Map();
|
|
312
|
+
for (const row of rows) {
|
|
313
|
+
const record = parseStoredMigrationRecord(row);
|
|
314
|
+
records.set(record.id, record);
|
|
315
|
+
}
|
|
316
|
+
return records;
|
|
317
|
+
}
|
|
318
|
+
async function applyMigration(executor, migration) {
|
|
319
|
+
await executor.transaction(async () => {
|
|
320
|
+
for (const statement of migration.statements) {
|
|
321
|
+
await executor.execute(statement);
|
|
322
|
+
}
|
|
323
|
+
await executor.execute(
|
|
324
|
+
"INSERT INTO junior_schema_migrations (id, checksum) VALUES ($1, $2)",
|
|
325
|
+
[migration.id, migration.checksum]
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
async function migrateSchema(executor, migrationList = migrations) {
|
|
330
|
+
await executor.withLock(MIGRATION_LOCK_NAME, async () => {
|
|
331
|
+
await executor.execute(createMigrationTable);
|
|
332
|
+
const applied = await listAppliedMigrations(executor);
|
|
333
|
+
for (const migration of migrationList) {
|
|
334
|
+
const existing = applied.get(migration.id);
|
|
335
|
+
if (existing) {
|
|
336
|
+
if (existing.checksum !== migration.checksum) {
|
|
337
|
+
throw new Error(
|
|
338
|
+
`Conversation migration ${migration.id} checksum changed`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
await applyMigration(executor, migration);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/chat/conversations/sql/store.ts
|
|
349
|
+
var CONVERSATION_MUTATION_LOCK_PREFIX = "junior_conversation";
|
|
350
|
+
function now() {
|
|
351
|
+
return Date.now();
|
|
352
|
+
}
|
|
353
|
+
function dateFromMs(ms) {
|
|
354
|
+
return new Date(ms);
|
|
355
|
+
}
|
|
356
|
+
function tenantId(value) {
|
|
357
|
+
return value ?? "";
|
|
358
|
+
}
|
|
359
|
+
function msFromDate(value) {
|
|
360
|
+
if (value === null || value === void 0) {
|
|
361
|
+
return void 0;
|
|
362
|
+
}
|
|
363
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
364
|
+
return date.getTime();
|
|
365
|
+
}
|
|
366
|
+
function requiredMsFromDate(value) {
|
|
367
|
+
const ms = msFromDate(value);
|
|
368
|
+
if (typeof ms !== "number" || Number.isNaN(ms)) {
|
|
369
|
+
throw new Error("Conversation record timestamp is invalid");
|
|
370
|
+
}
|
|
371
|
+
return ms;
|
|
372
|
+
}
|
|
373
|
+
function sourceFromValue(value) {
|
|
374
|
+
if (value === "api" || value === "internal" || value === "local" || value === "plugin" || value === "scheduler" || value === "slack") {
|
|
375
|
+
return value;
|
|
376
|
+
}
|
|
377
|
+
return void 0;
|
|
378
|
+
}
|
|
379
|
+
function identityFromRequester(requester) {
|
|
380
|
+
if (!requester?.slackUserId) {
|
|
381
|
+
return void 0;
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
kind: "user",
|
|
385
|
+
provider: "slack",
|
|
386
|
+
providerTenantId: requester.teamId,
|
|
387
|
+
providerSubjectId: requester.slackUserId,
|
|
388
|
+
...requester.fullName ? { displayName: requester.fullName } : {},
|
|
389
|
+
...requester.slackUserName ? { handle: requester.slackUserName } : {},
|
|
390
|
+
...requester.email ? { email: requester.email } : {},
|
|
391
|
+
metadata: { platform: "slack" }
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function systemIdentityFromSource(source) {
|
|
395
|
+
if (source === "scheduler") {
|
|
396
|
+
return {
|
|
397
|
+
kind: "system",
|
|
398
|
+
provider: "junior",
|
|
399
|
+
providerSubjectId: "scheduler",
|
|
400
|
+
displayName: "Junior Scheduler"
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
if (source === "local") {
|
|
404
|
+
return {
|
|
405
|
+
kind: "system",
|
|
406
|
+
provider: "junior",
|
|
407
|
+
providerSubjectId: "local-cli",
|
|
408
|
+
displayName: "Local CLI"
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
return void 0;
|
|
412
|
+
}
|
|
413
|
+
function actorIdentityForConversation(conversation) {
|
|
414
|
+
return identityFromRequester(conversation.requester) ?? systemIdentityFromSource(conversation.source);
|
|
415
|
+
}
|
|
416
|
+
function originTypeFromSource(source) {
|
|
417
|
+
return source;
|
|
418
|
+
}
|
|
419
|
+
function localWorkspaceFromConversationId(conversationId) {
|
|
420
|
+
const match = /^local:([^:]+):/.exec(conversationId);
|
|
421
|
+
return match?.[1];
|
|
422
|
+
}
|
|
423
|
+
function destinationUpsertFromDestination(args) {
|
|
424
|
+
const { destination } = args;
|
|
425
|
+
if (!destination) {
|
|
426
|
+
return void 0;
|
|
427
|
+
}
|
|
428
|
+
if (destination.platform === "slack") {
|
|
429
|
+
const channelId = destination.channelId;
|
|
430
|
+
const channelKind = channelId.startsWith("D") ? "dm" : channelId.startsWith("G") ? "group" : "channel";
|
|
431
|
+
const visibility = channelId.startsWith("D") ? "direct" : channelId.startsWith("G") ? "private" : "public";
|
|
432
|
+
return {
|
|
433
|
+
kind: channelKind,
|
|
434
|
+
provider: "slack",
|
|
435
|
+
providerTenantId: destination.teamId,
|
|
436
|
+
providerDestinationId: channelId,
|
|
437
|
+
visibility,
|
|
438
|
+
...args.channelName ? { displayName: args.channelName } : {},
|
|
439
|
+
metadata: { platform: "slack" }
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
kind: "local_conversation",
|
|
444
|
+
provider: "local",
|
|
445
|
+
providerTenantId: localWorkspaceFromConversationId(destination.conversationId) ?? localWorkspaceFromConversationId(args.conversationId ?? ""),
|
|
446
|
+
providerDestinationId: destination.conversationId,
|
|
447
|
+
visibility: "direct",
|
|
448
|
+
metadata: { platform: "local" }
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function executionStatusFromValue(value) {
|
|
452
|
+
if (value === "awaiting_resume" || value === "idle" || value === "pending" || value === "running") {
|
|
453
|
+
return value;
|
|
454
|
+
}
|
|
455
|
+
throw new Error("Conversation record execution status is invalid");
|
|
456
|
+
}
|
|
457
|
+
function conversationFromRow(row) {
|
|
458
|
+
if (row.schemaVersion !== 1) {
|
|
459
|
+
throw new Error("Conversation record schema version is invalid");
|
|
460
|
+
}
|
|
461
|
+
const destination = row.destination === void 0 || row.destination === null ? void 0 : parseDestination(row.destination);
|
|
462
|
+
const requester = parseStoredSlackRequester(row.requester);
|
|
463
|
+
if (row.destination !== void 0 && row.destination !== null && !destination) {
|
|
464
|
+
throw new Error("Conversation record destination is invalid");
|
|
465
|
+
}
|
|
466
|
+
if (row.requester !== void 0 && row.requester !== null && !requester) {
|
|
467
|
+
throw new Error("Conversation record requester is invalid");
|
|
468
|
+
}
|
|
469
|
+
const source = row.source === void 0 || row.source === null ? void 0 : sourceFromValue(row.source);
|
|
470
|
+
if (row.source !== void 0 && row.source !== null && !source) {
|
|
471
|
+
throw new Error("Conversation record source is invalid");
|
|
472
|
+
}
|
|
473
|
+
const execution = {
|
|
474
|
+
status: executionStatusFromValue(row.executionStatus),
|
|
475
|
+
lastCheckpointAtMs: msFromDate(row.lastCheckpointAt),
|
|
476
|
+
lastEnqueuedAtMs: msFromDate(row.lastEnqueuedAt),
|
|
477
|
+
...row.runId ? { runId: row.runId } : {},
|
|
478
|
+
updatedAtMs: msFromDate(row.executionUpdatedAt) ?? requiredMsFromDate(row.updatedAt)
|
|
479
|
+
};
|
|
480
|
+
return {
|
|
481
|
+
schemaVersion: 1,
|
|
482
|
+
conversationId: row.conversationId,
|
|
483
|
+
createdAtMs: requiredMsFromDate(row.createdAt),
|
|
484
|
+
lastActivityAtMs: requiredMsFromDate(row.lastActivityAt),
|
|
485
|
+
updatedAtMs: requiredMsFromDate(row.updatedAt),
|
|
486
|
+
execution,
|
|
487
|
+
...destination ? { destination } : {},
|
|
488
|
+
...requester ? { requester } : {},
|
|
489
|
+
...row.channelName ? { channelName: row.channelName } : {},
|
|
490
|
+
...source ? { source } : {},
|
|
491
|
+
...row.title ? { title: row.title } : {}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function emptyConversation(args) {
|
|
495
|
+
return {
|
|
496
|
+
schemaVersion: 1,
|
|
497
|
+
conversationId: args.conversationId,
|
|
498
|
+
createdAtMs: args.nowMs,
|
|
499
|
+
lastActivityAtMs: args.nowMs,
|
|
500
|
+
updatedAtMs: args.nowMs,
|
|
501
|
+
...args.destination ? { destination: args.destination } : {},
|
|
502
|
+
...args.source ? { source: args.source } : {},
|
|
503
|
+
execution: {
|
|
504
|
+
status: "idle",
|
|
505
|
+
updatedAtMs: args.nowMs
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function assertSameConversationDestination(args) {
|
|
510
|
+
if (!args.current || sameDestination(args.current, args.next)) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
throw new Error(
|
|
514
|
+
`Conversation destination changed for ${args.conversationId}`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
var SqlStore = class {
|
|
518
|
+
constructor(executor, migrationExecutor) {
|
|
519
|
+
this.executor = executor;
|
|
520
|
+
this.migrationExecutor = migrationExecutor;
|
|
521
|
+
}
|
|
522
|
+
executor;
|
|
523
|
+
migrationExecutor;
|
|
524
|
+
schemaReady;
|
|
525
|
+
/** Apply SQL schema migrations before runtime uses this store. */
|
|
526
|
+
async migrate() {
|
|
527
|
+
if (!this.schemaReady) {
|
|
528
|
+
this.schemaReady = migrateSchema(this.migrationExecutor);
|
|
529
|
+
}
|
|
530
|
+
const schemaReady = this.schemaReady;
|
|
531
|
+
try {
|
|
532
|
+
await schemaReady;
|
|
533
|
+
} catch (error) {
|
|
534
|
+
if (this.schemaReady === schemaReady) {
|
|
535
|
+
this.schemaReady = void 0;
|
|
536
|
+
}
|
|
537
|
+
throw error;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async get(args) {
|
|
541
|
+
const row = await this.readConversationRow(args.conversationId);
|
|
542
|
+
if (!row) {
|
|
543
|
+
return void 0;
|
|
544
|
+
}
|
|
545
|
+
return conversationFromRow(row);
|
|
546
|
+
}
|
|
547
|
+
async recordActivity(args) {
|
|
548
|
+
const nowMs = args.nowMs ?? now();
|
|
549
|
+
const activityAtMs = args.activityAtMs ?? nowMs;
|
|
550
|
+
await this.withConversationMutation(args.conversationId, async () => {
|
|
551
|
+
const existing = await this.get({
|
|
552
|
+
conversationId: args.conversationId
|
|
553
|
+
});
|
|
554
|
+
if (existing && args.destination) {
|
|
555
|
+
assertSameConversationDestination({
|
|
556
|
+
conversationId: args.conversationId,
|
|
557
|
+
current: existing.destination,
|
|
558
|
+
next: args.destination
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
const current2 = existing ?? emptyConversation({
|
|
562
|
+
conversationId: args.conversationId,
|
|
563
|
+
destination: args.destination,
|
|
564
|
+
nowMs,
|
|
565
|
+
source: args.source
|
|
566
|
+
});
|
|
567
|
+
await this.upsertConversation({
|
|
568
|
+
conversation: {
|
|
569
|
+
...current2,
|
|
570
|
+
destination: current2.destination ?? args.destination,
|
|
571
|
+
source: current2.source ?? args.source,
|
|
572
|
+
channelName: current2.channelName ?? args.channelName,
|
|
573
|
+
requester: current2.requester ?? args.requester,
|
|
574
|
+
title: current2.title ?? args.title,
|
|
575
|
+
lastActivityAtMs: Math.max(current2.lastActivityAtMs, activityAtMs),
|
|
576
|
+
updatedAtMs: nowMs,
|
|
577
|
+
execution: {
|
|
578
|
+
...current2.execution,
|
|
579
|
+
updatedAtMs: current2.execution.updatedAtMs ?? nowMs
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
async recordExecution(args) {
|
|
586
|
+
await this.withConversationMutation(args.conversationId, async () => {
|
|
587
|
+
await this.upsertConversation({
|
|
588
|
+
conversation: {
|
|
589
|
+
schemaVersion: 1,
|
|
590
|
+
conversationId: args.conversationId,
|
|
591
|
+
createdAtMs: args.createdAtMs,
|
|
592
|
+
lastActivityAtMs: args.lastActivityAtMs,
|
|
593
|
+
updatedAtMs: args.updatedAtMs,
|
|
594
|
+
...args.channelName ? { channelName: args.channelName } : {},
|
|
595
|
+
...args.destination ? { destination: args.destination } : {},
|
|
596
|
+
...args.requester ? { requester: args.requester } : {},
|
|
597
|
+
...args.source ? { source: args.source } : {},
|
|
598
|
+
...args.title ? { title: args.title } : {},
|
|
599
|
+
execution: args.execution
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
/** Copy one conversation record into SQL during backfill. */
|
|
605
|
+
async backfillConversation(conversation) {
|
|
606
|
+
await this.withConversationMutation(
|
|
607
|
+
conversation.conversationId,
|
|
608
|
+
async () => {
|
|
609
|
+
const existing = await this.get({
|
|
610
|
+
conversationId: conversation.conversationId
|
|
611
|
+
});
|
|
612
|
+
const sourceExecutionAtMs = conversation.execution.updatedAtMs ?? conversation.updatedAtMs;
|
|
613
|
+
const existingExecutionAtMs = existing === void 0 ? void 0 : existing.execution.updatedAtMs ?? existing.updatedAtMs;
|
|
614
|
+
const refreshExecutionFromSource = existingExecutionAtMs === void 0 || sourceExecutionAtMs >= existingExecutionAtMs;
|
|
615
|
+
const mergedConversation = existing ? {
|
|
616
|
+
...conversation,
|
|
617
|
+
channelName: existing.channelName ?? conversation.channelName,
|
|
618
|
+
createdAtMs: Math.min(
|
|
619
|
+
existing.createdAtMs,
|
|
620
|
+
conversation.createdAtMs
|
|
621
|
+
),
|
|
622
|
+
destination: existing.destination ?? conversation.destination,
|
|
623
|
+
lastActivityAtMs: Math.max(
|
|
624
|
+
existing.lastActivityAtMs,
|
|
625
|
+
conversation.lastActivityAtMs
|
|
626
|
+
),
|
|
627
|
+
requester: existing.requester ?? conversation.requester,
|
|
628
|
+
source: existing.source ?? conversation.source,
|
|
629
|
+
title: existing.title ?? conversation.title,
|
|
630
|
+
updatedAtMs: Math.max(
|
|
631
|
+
existing.updatedAtMs,
|
|
632
|
+
conversation.updatedAtMs
|
|
633
|
+
),
|
|
634
|
+
execution: refreshExecutionFromSource ? conversation.execution : existing.execution
|
|
635
|
+
} : conversation;
|
|
636
|
+
await this.upsertConversation({ conversation: mergedConversation });
|
|
637
|
+
}
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
async listByActivity(args = {}) {
|
|
641
|
+
const rows = await this.executor.db().select().from(juniorConversations).orderBy(
|
|
642
|
+
desc(juniorConversations.lastActivityAt),
|
|
643
|
+
asc(juniorConversations.conversationId)
|
|
644
|
+
).limit(Math.max(0, args.limit ?? 1e4)).offset(Math.max(0, args.offset ?? 0));
|
|
645
|
+
const conversations = [];
|
|
646
|
+
for (const row of rows) {
|
|
647
|
+
conversations.push(conversationFromRow(row));
|
|
648
|
+
}
|
|
649
|
+
return conversations;
|
|
650
|
+
}
|
|
651
|
+
/** Serialize all durable mutations for one conversation inside a SQL transaction. */
|
|
652
|
+
async withConversationMutation(conversationId, callback) {
|
|
653
|
+
return await this.executor.withLock(
|
|
654
|
+
`${CONVERSATION_MUTATION_LOCK_PREFIX}:${conversationId}`,
|
|
655
|
+
async () => await this.executor.transaction(callback)
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
async readConversationRow(conversationId) {
|
|
659
|
+
const rows = await this.executor.db().select().from(juniorConversations).where(eq(juniorConversations.conversationId, conversationId));
|
|
660
|
+
return rows[0];
|
|
661
|
+
}
|
|
662
|
+
/** Upsert the conversation row while preserving previously discovered nullable metadata fields. */
|
|
663
|
+
async upsertConversation(args) {
|
|
664
|
+
const { conversation } = args;
|
|
665
|
+
const incomingExecutionVersion = sql2`coalesce(excluded.execution_updated_at, excluded.updated_at)`;
|
|
666
|
+
const currentExecutionVersion = sql2`coalesce(${juniorConversations.executionUpdatedAt}, ${juniorConversations.updatedAt})`;
|
|
667
|
+
const incomingExecutionIsFresh = sql2`${incomingExecutionVersion} >= ${currentExecutionVersion}`;
|
|
668
|
+
const destinationId = await this.upsertDestination(
|
|
669
|
+
destinationUpsertFromDestination({
|
|
670
|
+
channelName: conversation.channelName,
|
|
671
|
+
conversationId: conversation.conversationId,
|
|
672
|
+
destination: conversation.destination
|
|
673
|
+
}),
|
|
674
|
+
conversation.updatedAtMs
|
|
675
|
+
);
|
|
676
|
+
const requesterIdentityId = await this.upsertIdentity(
|
|
677
|
+
identityFromRequester(conversation.requester),
|
|
678
|
+
conversation.updatedAtMs
|
|
679
|
+
);
|
|
680
|
+
const actorIdentityId = await this.upsertIdentity(
|
|
681
|
+
actorIdentityForConversation(conversation),
|
|
682
|
+
conversation.updatedAtMs
|
|
683
|
+
);
|
|
684
|
+
await this.executor.db().insert(juniorConversations).values({
|
|
685
|
+
conversationId: conversation.conversationId,
|
|
686
|
+
schemaVersion: 1,
|
|
687
|
+
source: conversation.source ?? null,
|
|
688
|
+
originType: originTypeFromSource(conversation.source) ?? null,
|
|
689
|
+
originId: null,
|
|
690
|
+
originRunId: null,
|
|
691
|
+
destinationId: destinationId ?? null,
|
|
692
|
+
destination: conversation.destination ?? null,
|
|
693
|
+
actorIdentityId: actorIdentityId ?? null,
|
|
694
|
+
requesterIdentityId: requesterIdentityId ?? null,
|
|
695
|
+
creatorIdentityId: null,
|
|
696
|
+
credentialSubjectIdentityId: null,
|
|
697
|
+
requester: conversation.requester ?? null,
|
|
698
|
+
channelName: conversation.channelName ?? null,
|
|
699
|
+
title: conversation.title ?? null,
|
|
700
|
+
createdAt: dateFromMs(conversation.createdAtMs),
|
|
701
|
+
lastActivityAt: dateFromMs(conversation.lastActivityAtMs),
|
|
702
|
+
updatedAt: dateFromMs(conversation.updatedAtMs),
|
|
703
|
+
executionUpdatedAt: conversation.execution.updatedAtMs === void 0 ? null : dateFromMs(conversation.execution.updatedAtMs),
|
|
704
|
+
executionStatus: conversation.execution.status,
|
|
705
|
+
runId: conversation.execution.runId ?? null,
|
|
706
|
+
lastCheckpointAt: conversation.execution.lastCheckpointAtMs === void 0 ? null : dateFromMs(conversation.execution.lastCheckpointAtMs),
|
|
707
|
+
lastEnqueuedAt: conversation.execution.lastEnqueuedAtMs === void 0 ? null : dateFromMs(conversation.execution.lastEnqueuedAtMs)
|
|
708
|
+
}).onConflictDoUpdate({
|
|
709
|
+
target: juniorConversations.conversationId,
|
|
710
|
+
set: {
|
|
711
|
+
source: sql2`coalesce(excluded.source, ${juniorConversations.source})`,
|
|
712
|
+
originType: sql2`coalesce(excluded.origin_type, ${juniorConversations.originType})`,
|
|
713
|
+
originId: sql2`coalesce(excluded.origin_id, ${juniorConversations.originId})`,
|
|
714
|
+
originRunId: sql2`coalesce(excluded.origin_run_id, ${juniorConversations.originRunId})`,
|
|
715
|
+
destinationId: sql2`coalesce(excluded.destination_id, ${juniorConversations.destinationId})`,
|
|
716
|
+
destination: sql2`coalesce(excluded.destination_json, ${juniorConversations.destination})`,
|
|
717
|
+
actorIdentityId: sql2`coalesce(excluded.actor_identity_id, ${juniorConversations.actorIdentityId})`,
|
|
718
|
+
requesterIdentityId: sql2`coalesce(excluded.requester_identity_id, ${juniorConversations.requesterIdentityId})`,
|
|
719
|
+
creatorIdentityId: sql2`coalesce(excluded.creator_identity_id, ${juniorConversations.creatorIdentityId})`,
|
|
720
|
+
credentialSubjectIdentityId: sql2`coalesce(excluded.credential_subject_identity_id, ${juniorConversations.credentialSubjectIdentityId})`,
|
|
721
|
+
requester: sql2`coalesce(excluded.requester_json, ${juniorConversations.requester})`,
|
|
722
|
+
channelName: sql2`coalesce(excluded.channel_name, ${juniorConversations.channelName})`,
|
|
723
|
+
title: sql2`coalesce(excluded.title, ${juniorConversations.title})`,
|
|
724
|
+
createdAt: sql2`least(${juniorConversations.createdAt}, excluded.created_at)`,
|
|
725
|
+
lastActivityAt: sql2`greatest(${juniorConversations.lastActivityAt}, excluded.last_activity_at)`,
|
|
726
|
+
updatedAt: sql2`greatest(${juniorConversations.updatedAt}, excluded.updated_at)`,
|
|
727
|
+
executionUpdatedAt: sql2`case when ${incomingExecutionIsFresh} then excluded.execution_updated_at else ${juniorConversations.executionUpdatedAt} end`,
|
|
728
|
+
executionStatus: sql2`case when ${incomingExecutionIsFresh} then excluded.execution_status else ${juniorConversations.executionStatus} end`,
|
|
729
|
+
runId: sql2`case when ${incomingExecutionIsFresh} then excluded.run_id else ${juniorConversations.runId} end`,
|
|
730
|
+
lastCheckpointAt: sql2`case when ${incomingExecutionIsFresh} then excluded.last_checkpoint_at else ${juniorConversations.lastCheckpointAt} end`,
|
|
731
|
+
lastEnqueuedAt: sql2`case when ${incomingExecutionIsFresh} then excluded.last_enqueued_at else ${juniorConversations.lastEnqueuedAt} end`
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
async upsertIdentity(identity, nowMs) {
|
|
736
|
+
if (!identity) {
|
|
737
|
+
return void 0;
|
|
738
|
+
}
|
|
739
|
+
const rows = await this.executor.db().insert(juniorIdentities).values({
|
|
740
|
+
id: randomUUID(),
|
|
741
|
+
kind: identity.kind,
|
|
742
|
+
provider: identity.provider,
|
|
743
|
+
providerTenantId: tenantId(identity.providerTenantId),
|
|
744
|
+
providerSubjectId: identity.providerSubjectId,
|
|
745
|
+
displayName: identity.displayName ?? null,
|
|
746
|
+
handle: identity.handle ?? null,
|
|
747
|
+
email: identity.email ?? null,
|
|
748
|
+
avatarUrl: null,
|
|
749
|
+
metadata: identity.metadata ?? null,
|
|
750
|
+
createdAt: dateFromMs(nowMs),
|
|
751
|
+
updatedAt: dateFromMs(nowMs)
|
|
752
|
+
}).onConflictDoUpdate({
|
|
753
|
+
target: [
|
|
754
|
+
juniorIdentities.provider,
|
|
755
|
+
juniorIdentities.providerTenantId,
|
|
756
|
+
juniorIdentities.providerSubjectId
|
|
757
|
+
],
|
|
758
|
+
set: {
|
|
759
|
+
kind: sql2`excluded.kind`,
|
|
760
|
+
displayName: sql2`coalesce(excluded.display_name, ${juniorIdentities.displayName})`,
|
|
761
|
+
handle: sql2`coalesce(excluded.handle, ${juniorIdentities.handle})`,
|
|
762
|
+
email: sql2`coalesce(excluded.email, ${juniorIdentities.email})`,
|
|
763
|
+
avatarUrl: sql2`coalesce(excluded.avatar_url, ${juniorIdentities.avatarUrl})`,
|
|
764
|
+
metadata: sql2`coalesce(excluded.metadata_json, ${juniorIdentities.metadata})`,
|
|
765
|
+
updatedAt: sql2`excluded.updated_at`
|
|
766
|
+
}
|
|
767
|
+
}).returning({ id: juniorIdentities.id });
|
|
768
|
+
return rows[0]?.id;
|
|
769
|
+
}
|
|
770
|
+
async upsertDestination(destination, nowMs) {
|
|
771
|
+
if (!destination) {
|
|
772
|
+
return void 0;
|
|
773
|
+
}
|
|
774
|
+
const rows = await this.executor.db().insert(juniorDestinations).values({
|
|
775
|
+
id: randomUUID(),
|
|
776
|
+
provider: destination.provider,
|
|
777
|
+
providerTenantId: tenantId(destination.providerTenantId),
|
|
778
|
+
providerDestinationId: destination.providerDestinationId,
|
|
779
|
+
kind: destination.kind,
|
|
780
|
+
parentDestinationId: null,
|
|
781
|
+
displayName: destination.displayName ?? null,
|
|
782
|
+
visibility: destination.visibility,
|
|
783
|
+
metadata: destination.metadata ?? null,
|
|
784
|
+
createdAt: dateFromMs(nowMs),
|
|
785
|
+
updatedAt: dateFromMs(nowMs)
|
|
786
|
+
}).onConflictDoUpdate({
|
|
787
|
+
target: [
|
|
788
|
+
juniorDestinations.provider,
|
|
789
|
+
juniorDestinations.providerTenantId,
|
|
790
|
+
juniorDestinations.providerDestinationId
|
|
791
|
+
],
|
|
792
|
+
set: {
|
|
793
|
+
kind: sql2`excluded.kind`,
|
|
794
|
+
displayName: sql2`coalesce(excluded.display_name, ${juniorDestinations.displayName})`,
|
|
795
|
+
visibility: sql2`excluded.visibility`,
|
|
796
|
+
metadata: sql2`coalesce(excluded.metadata_json, ${juniorDestinations.metadata})`,
|
|
797
|
+
updatedAt: sql2`excluded.updated_at`
|
|
798
|
+
}
|
|
799
|
+
}).returning({ id: juniorDestinations.id });
|
|
800
|
+
return rows[0]?.id;
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
function createSqlStore(executor) {
|
|
804
|
+
return new SqlStore(executor, executor);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// src/chat/sql/neon.ts
|
|
808
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
809
|
+
import {
|
|
810
|
+
Pool
|
|
811
|
+
} from "@neondatabase/serverless";
|
|
812
|
+
import { drizzle } from "drizzle-orm/neon-serverless";
|
|
813
|
+
|
|
814
|
+
// src/chat/sql/schema.ts
|
|
815
|
+
var juniorSqlSchema = {
|
|
816
|
+
...schema
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
// src/chat/sql/neon.ts
|
|
820
|
+
var NeonExecutor = class {
|
|
821
|
+
constructor(pool) {
|
|
822
|
+
this.pool = pool;
|
|
823
|
+
}
|
|
824
|
+
pool;
|
|
825
|
+
transactionClient = new AsyncLocalStorage();
|
|
826
|
+
savepointId = 0;
|
|
827
|
+
db() {
|
|
828
|
+
return drizzle(this.queryClient(), {
|
|
829
|
+
schema: juniorSqlSchema
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
async execute(statement, params = []) {
|
|
833
|
+
await this.queryClient().query(statement, [...params]);
|
|
834
|
+
}
|
|
835
|
+
async query(statement, params = []) {
|
|
836
|
+
const result = await this.queryClient().query(statement, [
|
|
837
|
+
...params
|
|
838
|
+
]);
|
|
839
|
+
return result.rows;
|
|
840
|
+
}
|
|
841
|
+
async transaction(callback) {
|
|
842
|
+
const existingClient = this.transactionClient.getStore();
|
|
843
|
+
if (existingClient) {
|
|
844
|
+
const savepoint = `junior_savepoint_${++this.savepointId}`;
|
|
845
|
+
await existingClient.query(`SAVEPOINT ${savepoint}`);
|
|
846
|
+
try {
|
|
847
|
+
const result = await callback();
|
|
848
|
+
await existingClient.query(`RELEASE SAVEPOINT ${savepoint}`);
|
|
849
|
+
return result;
|
|
850
|
+
} catch (error) {
|
|
851
|
+
await existingClient.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
852
|
+
await existingClient.query(`RELEASE SAVEPOINT ${savepoint}`);
|
|
853
|
+
throw error;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
const client = await this.pool.connect();
|
|
857
|
+
try {
|
|
858
|
+
await client.query("BEGIN");
|
|
859
|
+
const result = await this.transactionClient.run(client, callback);
|
|
860
|
+
await client.query("COMMIT");
|
|
861
|
+
return result;
|
|
862
|
+
} catch (error) {
|
|
863
|
+
await client.query("ROLLBACK");
|
|
864
|
+
throw error;
|
|
865
|
+
} finally {
|
|
866
|
+
client.release();
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
async withLock(lockName, callback) {
|
|
870
|
+
if (!lockName) {
|
|
871
|
+
throw new Error("SQL lock name is required");
|
|
872
|
+
}
|
|
873
|
+
const existingClient = this.transactionClient.getStore();
|
|
874
|
+
if (existingClient) {
|
|
875
|
+
await existingClient.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
|
|
876
|
+
lockName
|
|
877
|
+
]);
|
|
878
|
+
return await callback();
|
|
879
|
+
}
|
|
880
|
+
const client = await this.pool.connect();
|
|
881
|
+
try {
|
|
882
|
+
await client.query("BEGIN");
|
|
883
|
+
return await this.transactionClient.run(client, async () => {
|
|
884
|
+
try {
|
|
885
|
+
await client.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
|
|
886
|
+
lockName
|
|
887
|
+
]);
|
|
888
|
+
const result = await callback();
|
|
889
|
+
await client.query("COMMIT");
|
|
890
|
+
return result;
|
|
891
|
+
} catch (error) {
|
|
892
|
+
await client.query("ROLLBACK");
|
|
893
|
+
throw error;
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
} finally {
|
|
897
|
+
client.release();
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
async close() {
|
|
901
|
+
await this.pool.end();
|
|
902
|
+
}
|
|
903
|
+
queryClient() {
|
|
904
|
+
return this.transactionClient.getStore() ?? this.pool;
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
function createNeonJuniorSqlExecutor(args) {
|
|
908
|
+
return new NeonExecutor(
|
|
909
|
+
new Pool({
|
|
910
|
+
connectionString: args.connectionString,
|
|
911
|
+
max: 3
|
|
912
|
+
})
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// src/chat/sql/postgres.ts
|
|
917
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
918
|
+
import pg from "pg";
|
|
919
|
+
import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
|
|
920
|
+
var { Pool: Pool2 } = pg;
|
|
921
|
+
var PostgresExecutor = class {
|
|
922
|
+
constructor(pool) {
|
|
923
|
+
this.pool = pool;
|
|
924
|
+
}
|
|
925
|
+
pool;
|
|
926
|
+
transactionClient = new AsyncLocalStorage2();
|
|
927
|
+
savepointId = 0;
|
|
928
|
+
db() {
|
|
929
|
+
return drizzle2(this.queryClient(), {
|
|
930
|
+
schema: juniorSqlSchema
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
async execute(statement, params = []) {
|
|
934
|
+
await this.queryClient().query(statement, [...params]);
|
|
935
|
+
}
|
|
936
|
+
async query(statement, params = []) {
|
|
937
|
+
const result = await this.queryClient().query(statement, [
|
|
938
|
+
...params
|
|
939
|
+
]);
|
|
940
|
+
return result.rows;
|
|
941
|
+
}
|
|
942
|
+
async transaction(callback) {
|
|
943
|
+
const existingClient = this.transactionClient.getStore();
|
|
944
|
+
if (existingClient) {
|
|
945
|
+
const savepoint = `junior_savepoint_${++this.savepointId}`;
|
|
946
|
+
await existingClient.query(`SAVEPOINT ${savepoint}`);
|
|
947
|
+
try {
|
|
948
|
+
const result = await callback();
|
|
949
|
+
await existingClient.query(`RELEASE SAVEPOINT ${savepoint}`);
|
|
950
|
+
return result;
|
|
951
|
+
} catch (error) {
|
|
952
|
+
await existingClient.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
953
|
+
await existingClient.query(`RELEASE SAVEPOINT ${savepoint}`);
|
|
954
|
+
throw error;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
const client = await this.pool.connect();
|
|
958
|
+
try {
|
|
959
|
+
await client.query("BEGIN");
|
|
960
|
+
const result = await this.transactionClient.run(client, callback);
|
|
961
|
+
await client.query("COMMIT");
|
|
962
|
+
return result;
|
|
963
|
+
} catch (error) {
|
|
964
|
+
await client.query("ROLLBACK");
|
|
965
|
+
throw error;
|
|
966
|
+
} finally {
|
|
967
|
+
client.release();
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
async withLock(lockName, callback) {
|
|
971
|
+
if (!lockName) {
|
|
972
|
+
throw new Error("SQL lock name is required");
|
|
973
|
+
}
|
|
974
|
+
const existingClient = this.transactionClient.getStore();
|
|
975
|
+
if (existingClient) {
|
|
976
|
+
await existingClient.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
|
|
977
|
+
lockName
|
|
978
|
+
]);
|
|
979
|
+
return await callback();
|
|
980
|
+
}
|
|
981
|
+
const client = await this.pool.connect();
|
|
982
|
+
try {
|
|
983
|
+
await client.query("BEGIN");
|
|
984
|
+
return await this.transactionClient.run(client, async () => {
|
|
985
|
+
try {
|
|
986
|
+
await client.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
|
|
987
|
+
lockName
|
|
988
|
+
]);
|
|
989
|
+
const result = await callback();
|
|
990
|
+
await client.query("COMMIT");
|
|
991
|
+
return result;
|
|
992
|
+
} catch (error) {
|
|
993
|
+
await client.query("ROLLBACK");
|
|
994
|
+
throw error;
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
} finally {
|
|
998
|
+
client.release();
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
async close() {
|
|
1002
|
+
await this.pool.end();
|
|
1003
|
+
}
|
|
1004
|
+
queryClient() {
|
|
1005
|
+
return this.transactionClient.getStore() ?? this.pool;
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
function createPostgresJuniorSqlExecutor(args) {
|
|
1009
|
+
return new PostgresExecutor(
|
|
1010
|
+
new Pool2({
|
|
1011
|
+
application_name: args.applicationName,
|
|
1012
|
+
connectionString: args.connectionString,
|
|
1013
|
+
max: 3
|
|
1014
|
+
})
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// src/chat/sql/executor.ts
|
|
1019
|
+
function createJuniorSqlExecutor(args) {
|
|
1020
|
+
if (args.driver === "postgres") {
|
|
1021
|
+
return createPostgresJuniorSqlExecutor(args);
|
|
1022
|
+
}
|
|
1023
|
+
return createNeonJuniorSqlExecutor(args);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// src/chat/db.ts
|
|
1027
|
+
var current;
|
|
1028
|
+
function createDb(args) {
|
|
1029
|
+
return createJuniorSqlExecutor({
|
|
1030
|
+
connectionString: args.databaseUrl,
|
|
1031
|
+
driver: args.driver
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
function getSqlExecutor() {
|
|
1035
|
+
const { sql: sql3 } = getChatConfig();
|
|
1036
|
+
if (!sql3.databaseUrl) {
|
|
1037
|
+
if (current) {
|
|
1038
|
+
const previous = current;
|
|
1039
|
+
current = void 0;
|
|
1040
|
+
void previous.db.close().catch(() => void 0);
|
|
1041
|
+
}
|
|
1042
|
+
throw new Error("DATABASE_URL or JUNIOR_DATABASE_URL is required");
|
|
1043
|
+
}
|
|
1044
|
+
if (current?.databaseUrl !== sql3.databaseUrl || current.driver !== sql3.driver) {
|
|
1045
|
+
if (current) {
|
|
1046
|
+
const previous = current;
|
|
1047
|
+
current = void 0;
|
|
1048
|
+
void previous.db.close().catch(() => void 0);
|
|
1049
|
+
}
|
|
1050
|
+
const db = createDb({
|
|
1051
|
+
databaseUrl: sql3.databaseUrl,
|
|
1052
|
+
driver: sql3.driver
|
|
1053
|
+
});
|
|
1054
|
+
current = {
|
|
1055
|
+
databaseUrl: sql3.databaseUrl,
|
|
1056
|
+
driver: sql3.driver,
|
|
1057
|
+
db,
|
|
1058
|
+
store: createSqlStore(db)
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
return current.db;
|
|
1062
|
+
}
|
|
1063
|
+
function getDb() {
|
|
1064
|
+
return getSqlExecutor().db();
|
|
1065
|
+
}
|
|
1066
|
+
function getConversationStore() {
|
|
1067
|
+
getSqlExecutor();
|
|
1068
|
+
return current.store;
|
|
1069
|
+
}
|
|
1070
|
+
async function closeDb() {
|
|
1071
|
+
const previous = current;
|
|
1072
|
+
current = void 0;
|
|
1073
|
+
await previous?.db.close();
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
export {
|
|
1077
|
+
createSqlStore,
|
|
1078
|
+
createJuniorSqlExecutor,
|
|
1079
|
+
getDb,
|
|
1080
|
+
getConversationStore,
|
|
1081
|
+
closeDb
|
|
1082
|
+
};
|