@opengeni/db 0.23.0 → 0.26.0
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/dist/{chunk-3UCHDMKG.js → chunk-7WTDI7Y3.js} +109 -2
- package/dist/chunk-7WTDI7Y3.js.map +1 -0
- package/dist/{chunk-L6ADMZHE.js → chunk-KW6U54V2.js} +3 -1
- package/dist/chunk-KW6U54V2.js.map +1 -0
- package/dist/connection-token-resolver.d.ts +24 -2
- package/dist/index.d.ts +68 -2
- package/dist/index.js +1168 -336
- package/dist/index.js.map +1 -1
- package/dist/preference-registry.d.ts +14 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +2 -2
- package/dist/schema.d.ts +262 -4
- package/dist/schema.js +3 -1
- package/dist/session-realtime-mirror.d.ts +22 -1
- package/dist/workspace-instruction-policies-schema.d.ts +68 -0
- package/dist/workspace-instruction-policies.d.ts +53 -7
- package/drizzle/0165_document_authority_foundation.sql +259 -0
- package/drizzle/0166_connection_disconnect_idempotency.sql +49 -0
- package/drizzle/0167_document_index_replay_authority.sql +61 -0
- package/drizzle/0168_workspace_instruction_policy_operation_receipts.sql +44 -0
- package/package.json +3 -3
- package/src/connection-token-resolver.ts +79 -16
- package/src/index.ts +419 -23
- package/src/preference-registry.ts +103 -0
- package/src/runtime-posture.ts +2 -0
- package/src/schema.ts +86 -0
- package/src/session-control.ts +48 -1
- package/src/session-queue-commands.ts +45 -11
- package/src/session-realtime-mirror.ts +117 -1
- package/src/session-realtime.ts +49 -1
- package/src/workspace-instruction-policies-schema.ts +30 -0
- package/src/workspace-instruction-policies.ts +438 -24
- package/dist/chunk-3UCHDMKG.js.map +0 -1
- package/dist/chunk-L6ADMZHE.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
codexRotationSettings,
|
|
12
12
|
codexSubscriptionCredentials,
|
|
13
13
|
composerDrafts,
|
|
14
|
+
connectionDisconnectOperations,
|
|
14
15
|
connections,
|
|
15
16
|
connectorActionPolicies,
|
|
16
17
|
connectorActionRequests,
|
|
@@ -104,6 +105,7 @@ import {
|
|
|
104
105
|
workspaceInstructionPolicyActivationEvents,
|
|
105
106
|
workspaceInstructionPolicyHeads,
|
|
106
107
|
workspaceInstructionPolicyRevisions,
|
|
108
|
+
workspaceInstructionPolicySnapshots,
|
|
107
109
|
workspaceMemberships,
|
|
108
110
|
workspaceModelPolicies,
|
|
109
111
|
workspacePacks,
|
|
@@ -111,7 +113,7 @@ import {
|
|
|
111
113
|
workspaceVariableSetVariables,
|
|
112
114
|
workspaceVariableSets,
|
|
113
115
|
workspaces
|
|
114
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-7WTDI7Y3.js";
|
|
115
117
|
import {
|
|
116
118
|
migrate,
|
|
117
119
|
runMigrations
|
|
@@ -131,7 +133,7 @@ import {
|
|
|
131
133
|
inspectRuntimeDatabasePosture,
|
|
132
134
|
provisionRoles,
|
|
133
135
|
runtimeDatabaseReadyCheck
|
|
134
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-KW6U54V2.js";
|
|
135
137
|
import "./chunk-PZ5AY32C.js";
|
|
136
138
|
|
|
137
139
|
// src/index.ts
|
|
@@ -144,7 +146,8 @@ import {
|
|
|
144
146
|
canonicalModalCheckpointProviderBinding,
|
|
145
147
|
decodeNativeSnapshotRef,
|
|
146
148
|
parseWorkspaceArchiveDescriptor,
|
|
147
|
-
stableJson as stableJson3
|
|
149
|
+
stableJson as stableJson3,
|
|
150
|
+
MODEL_ATTACHMENT_REFS_FIELD
|
|
148
151
|
} from "@opengeni/contracts";
|
|
149
152
|
import {
|
|
150
153
|
approvalIdentifier,
|
|
@@ -191,17 +194,17 @@ import {
|
|
|
191
194
|
desc as desc8,
|
|
192
195
|
eq as eq17,
|
|
193
196
|
getTableColumns,
|
|
194
|
-
gt as
|
|
197
|
+
gt as gt5,
|
|
195
198
|
gte as gte2,
|
|
196
199
|
inArray as inArray11,
|
|
197
200
|
isNotNull,
|
|
198
201
|
ilike,
|
|
199
|
-
isNull as
|
|
202
|
+
isNull as isNull6,
|
|
200
203
|
lt as lt4,
|
|
201
204
|
lte as lte2,
|
|
202
205
|
ne,
|
|
203
206
|
notInArray,
|
|
204
|
-
or as
|
|
207
|
+
or as or6,
|
|
205
208
|
sql as sql14
|
|
206
209
|
} from "drizzle-orm";
|
|
207
210
|
|
|
@@ -1162,13 +1165,199 @@ async function closePendingSessionToolCallsInTransaction(tx, input) {
|
|
|
1162
1165
|
}
|
|
1163
1166
|
|
|
1164
1167
|
// src/session-control.ts
|
|
1165
|
-
import { createHash } from "crypto";
|
|
1168
|
+
import { createHash as createHash2 } from "crypto";
|
|
1166
1169
|
import {
|
|
1167
1170
|
boundWorkspaceControlEvent,
|
|
1168
1171
|
McpPersonalConnectionDelegations,
|
|
1169
1172
|
workspaceControlUtf8Bytes
|
|
1170
1173
|
} from "@opengeni/contracts";
|
|
1171
|
-
import { and as
|
|
1174
|
+
import { and as and5, eq as eq5, inArray, sql as sql3 } from "drizzle-orm";
|
|
1175
|
+
|
|
1176
|
+
// src/session-realtime-mirror.ts
|
|
1177
|
+
import { createHash } from "crypto";
|
|
1178
|
+
import { and as and4, desc, eq as eq4, gt, sql as sql2 } from "drizzle-orm";
|
|
1179
|
+
var SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES = 131072;
|
|
1180
|
+
var SESSION_REALTIME_MIRROR_MAX_PAYLOAD_BYTES = 131072;
|
|
1181
|
+
var TRUNCATION_MARKER = "\n\u2026realtime context truncated\u2026";
|
|
1182
|
+
function deterministicUuid(seed) {
|
|
1183
|
+
const bytes = createHash("sha256").update(seed, "utf8").digest().subarray(0, 16);
|
|
1184
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
1185
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
1186
|
+
const hex = bytes.toString("hex");
|
|
1187
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
1188
|
+
}
|
|
1189
|
+
function takeUtf8Head(value, maximumBytes) {
|
|
1190
|
+
const bytes = Buffer.from(value, "utf8");
|
|
1191
|
+
if (bytes.length <= maximumBytes) return value;
|
|
1192
|
+
let end = maximumBytes;
|
|
1193
|
+
while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
|
|
1194
|
+
return bytes.subarray(0, end).toString("utf8");
|
|
1195
|
+
}
|
|
1196
|
+
function boundedText(value) {
|
|
1197
|
+
if (Buffer.byteLength(value, "utf8") <= SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES) return value;
|
|
1198
|
+
const markerBytes = Buffer.byteLength(TRUNCATION_MARKER, "utf8");
|
|
1199
|
+
return `${takeUtf8Head(value, SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES - markerBytes)}${TRUNCATION_MARKER}`;
|
|
1200
|
+
}
|
|
1201
|
+
function boundedPayload(value) {
|
|
1202
|
+
const payload = sanitizeEventPayload(value);
|
|
1203
|
+
if (Buffer.byteLength(JSON.stringify(payload), "utf8") > SESSION_REALTIME_MIRROR_MAX_PAYLOAD_BYTES) {
|
|
1204
|
+
throw new Error("Realtime mirror payload exceeds the durable ledger limit");
|
|
1205
|
+
}
|
|
1206
|
+
return payload;
|
|
1207
|
+
}
|
|
1208
|
+
async function mirrorSessionRealtimeContextInTransaction(db, input) {
|
|
1209
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
1210
|
+
const modes = await db.select().from(sessionRealtimeModes).where(
|
|
1211
|
+
and4(
|
|
1212
|
+
eq4(sessionRealtimeModes.accountId, input.accountId),
|
|
1213
|
+
eq4(sessionRealtimeModes.workspaceId, input.workspaceId),
|
|
1214
|
+
eq4(sessionRealtimeModes.sessionId, input.sessionId),
|
|
1215
|
+
eq4(sessionRealtimeModes.state, "active"),
|
|
1216
|
+
gt(sessionRealtimeModes.leaseExpiresAt, now)
|
|
1217
|
+
)
|
|
1218
|
+
).orderBy(desc(sessionRealtimeModes.startedAt)).for("update").limit(2);
|
|
1219
|
+
if (modes.length === 0) return null;
|
|
1220
|
+
if (modes.length !== 1) {
|
|
1221
|
+
throw new Error(`Session ${input.sessionId} has multiple active realtime modes`);
|
|
1222
|
+
}
|
|
1223
|
+
const mode = modes[0];
|
|
1224
|
+
const operationId = deterministicUuid(
|
|
1225
|
+
`opengeni:session-realtime-mirror:${mode.id}:${input.sourceKind}:${input.sourceId}`
|
|
1226
|
+
);
|
|
1227
|
+
const [existing] = await db.select().from(sessionRealtimeEntries).where(
|
|
1228
|
+
and4(
|
|
1229
|
+
eq4(sessionRealtimeEntries.realtimeId, mode.id),
|
|
1230
|
+
eq4(sessionRealtimeEntries.operationId, operationId)
|
|
1231
|
+
)
|
|
1232
|
+
).limit(1);
|
|
1233
|
+
if (existing) return { entry: existing, replay: true };
|
|
1234
|
+
const [sequenceRow] = await db.select({ next: sql2`coalesce(max(${sessionRealtimeEntries.sequence}), 0) + 1` }).from(sessionRealtimeEntries).where(eq4(sessionRealtimeEntries.realtimeId, mode.id));
|
|
1235
|
+
const payload = boundedPayload({
|
|
1236
|
+
...input.payload ?? {},
|
|
1237
|
+
source: input.sourceKind,
|
|
1238
|
+
sourceId: input.sourceId,
|
|
1239
|
+
channel: input.channel,
|
|
1240
|
+
...input.turnId ? { sourceTurnId: input.turnId } : {}
|
|
1241
|
+
});
|
|
1242
|
+
const [entry] = await db.insert(sessionRealtimeEntries).values({
|
|
1243
|
+
accountId: input.accountId,
|
|
1244
|
+
workspaceId: input.workspaceId,
|
|
1245
|
+
sessionId: input.sessionId,
|
|
1246
|
+
realtimeId: mode.id,
|
|
1247
|
+
operationId,
|
|
1248
|
+
connectionEpoch: mode.connectionEpoch,
|
|
1249
|
+
sequence: Number(sequenceRow?.next ?? 1),
|
|
1250
|
+
direction: "provider_out",
|
|
1251
|
+
kind: "session_update",
|
|
1252
|
+
text: boundedText(input.text),
|
|
1253
|
+
payload,
|
|
1254
|
+
createdAt: now,
|
|
1255
|
+
updatedAt: now
|
|
1256
|
+
}).returning();
|
|
1257
|
+
if (!entry) throw new Error("Failed to append realtime session context");
|
|
1258
|
+
return { entry, replay: false };
|
|
1259
|
+
}
|
|
1260
|
+
function renderRealtimeHumanInputContext(input) {
|
|
1261
|
+
return [
|
|
1262
|
+
"<session_user_message>",
|
|
1263
|
+
` <status>${input.routing}</status>`,
|
|
1264
|
+
` <delivery>${input.delivery}</delivery>`,
|
|
1265
|
+
` <text>${escapeXmlText(input.text)}</text>`,
|
|
1266
|
+
" <instruction>Already routed by OpenGeni; do not delegate this message again.</instruction>",
|
|
1267
|
+
"</session_user_message>"
|
|
1268
|
+
].join("\n");
|
|
1269
|
+
}
|
|
1270
|
+
function renderRealtimeHumanInputRequestContext(input) {
|
|
1271
|
+
const lines = ["<session_human_input_request>", " <status>waiting_for_user</status>"];
|
|
1272
|
+
for (const request of input.requests) {
|
|
1273
|
+
lines.push(
|
|
1274
|
+
" <request>",
|
|
1275
|
+
` <id>${escapeXmlText(request.id)}</id>`,
|
|
1276
|
+
` <allow_skip>${String(request.allowSkip)}</allow_skip>`,
|
|
1277
|
+
` <expires_at>${escapeXmlText(renderExpiry(request.expiresAt))}</expires_at>`
|
|
1278
|
+
);
|
|
1279
|
+
for (const question of request.questions) {
|
|
1280
|
+
lines.push(
|
|
1281
|
+
" <question>",
|
|
1282
|
+
` <id>${escapeXmlText(question.id)}</id>`,
|
|
1283
|
+
` <kind>${escapeXmlText(question.kind)}</kind>`,
|
|
1284
|
+
` <required>${String(question.required)}</required>`
|
|
1285
|
+
);
|
|
1286
|
+
if (question.label) lines.push(` <label>${escapeXmlText(question.label)}</label>`);
|
|
1287
|
+
lines.push(` <prompt>${escapeXmlText(question.prompt)}</prompt>`);
|
|
1288
|
+
if (question.helpText) {
|
|
1289
|
+
lines.push(` <help_text>${escapeXmlText(question.helpText)}</help_text>`);
|
|
1290
|
+
}
|
|
1291
|
+
if (question.options.length > 0) {
|
|
1292
|
+
lines.push(" <options>");
|
|
1293
|
+
for (const option of question.options) {
|
|
1294
|
+
lines.push(
|
|
1295
|
+
" <option>",
|
|
1296
|
+
` <id>${escapeXmlText(option.id)}</id>`,
|
|
1297
|
+
` <label>${escapeXmlText(option.label)}</label>`
|
|
1298
|
+
);
|
|
1299
|
+
if (option.description) {
|
|
1300
|
+
lines.push(` <description>${escapeXmlText(option.description)}</description>`);
|
|
1301
|
+
}
|
|
1302
|
+
lines.push(" </option>");
|
|
1303
|
+
}
|
|
1304
|
+
lines.push(" </options>");
|
|
1305
|
+
}
|
|
1306
|
+
lines.push(
|
|
1307
|
+
` <allow_other>${String(question.allowOther)}</allow_other>`,
|
|
1308
|
+
" </question>"
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1311
|
+
lines.push(" </request>");
|
|
1312
|
+
}
|
|
1313
|
+
lines.push(
|
|
1314
|
+
" <instruction>The current work is waiting for the user's input. Ask the questions naturally, one at a time when useful, without changing their meaning or answering for the user. The user may answer in the visible form or answer conversationally here. If the user answers here, delegate exactly once with a complete message containing the relevant question and the user's answer. If the user changes direction instead, delegate that new direction normally. Do not claim the work resumed until a later session update confirms it.</instruction>",
|
|
1315
|
+
"</session_human_input_request>"
|
|
1316
|
+
);
|
|
1317
|
+
return lines.join("\n");
|
|
1318
|
+
}
|
|
1319
|
+
function renderRealtimeHumanInputResponseContext(input) {
|
|
1320
|
+
const lines = [
|
|
1321
|
+
"<session_human_input_response>",
|
|
1322
|
+
` <request_id>${escapeXmlText(input.requestId)}</request_id>`,
|
|
1323
|
+
` <outcome>${escapeXmlText(input.response.outcome)}</outcome>`
|
|
1324
|
+
];
|
|
1325
|
+
if (input.response.outcome === "answered") {
|
|
1326
|
+
const questions = new Map(input.questions.map((question) => [question.id, question]));
|
|
1327
|
+
lines.push(" <answers>");
|
|
1328
|
+
for (const answer of input.response.answers) {
|
|
1329
|
+
const question = questions.get(answer.questionId);
|
|
1330
|
+
const optionLabels = new Map(
|
|
1331
|
+
question?.options.map((option) => [option.id, option.label]) ?? []
|
|
1332
|
+
);
|
|
1333
|
+
lines.push(
|
|
1334
|
+
" <answer>",
|
|
1335
|
+
` <question_id>${escapeXmlText(answer.questionId)}</question_id>`
|
|
1336
|
+
);
|
|
1337
|
+
if (question) lines.push(` <question>${escapeXmlText(question.prompt)}</question>`);
|
|
1338
|
+
for (const value of answer.values) {
|
|
1339
|
+
lines.push(` <value>${escapeXmlText(optionLabels.get(value) ?? value)}</value>`);
|
|
1340
|
+
}
|
|
1341
|
+
if (answer.other) lines.push(` <other>${escapeXmlText(answer.other)}</other>`);
|
|
1342
|
+
lines.push(" </answer>");
|
|
1343
|
+
}
|
|
1344
|
+
lines.push(" </answers>");
|
|
1345
|
+
}
|
|
1346
|
+
lines.push(
|
|
1347
|
+
input.response.outcome === "answered" || input.response.outcome === "skipped" ? " <instruction>This structured response was accepted through the session UI and the same work can resume. Treat it as authoritative user context, do not delegate it again, and acknowledge briefly only if useful.</instruction>" : " <instruction>This pending question is no longer active. Do not ask it again unless the user raises it.</instruction>",
|
|
1348
|
+
"</session_human_input_response>"
|
|
1349
|
+
);
|
|
1350
|
+
return lines.join("\n");
|
|
1351
|
+
}
|
|
1352
|
+
function renderExpiry(value) {
|
|
1353
|
+
if (value === null || value === void 0) return "none";
|
|
1354
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
1355
|
+
}
|
|
1356
|
+
function escapeXmlText(value) {
|
|
1357
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// src/session-control.ts
|
|
1172
1361
|
var SESSION_ANCESTRY_LIMIT = 1e4;
|
|
1173
1362
|
var NO_SETTLEMENT_ATTEMPTS = {
|
|
1174
1363
|
attemptCount: 0,
|
|
@@ -1239,7 +1428,7 @@ async function assertAgentCommandAuthorityInTransaction(db, input) {
|
|
|
1239
1428
|
if (!lockedSessions.some((row) => row.id === input.targetSessionId)) {
|
|
1240
1429
|
throw new SessionControlInvariantError(`Target session not found: ${input.targetSessionId}`);
|
|
1241
1430
|
}
|
|
1242
|
-
const rows = await db.execute(
|
|
1431
|
+
const rows = await db.execute(sql3`
|
|
1243
1432
|
select
|
|
1244
1433
|
attempt.id as "attemptId",
|
|
1245
1434
|
attempt.state as "attemptState",
|
|
@@ -1281,7 +1470,7 @@ async function assertAgentCommandAuthorityInTransaction(db, input) {
|
|
|
1281
1470
|
throw new AgentCommandAuthorityError("SELF_STEER", "An agent cannot steer its own session");
|
|
1282
1471
|
}
|
|
1283
1472
|
if (input.action !== "pause") return;
|
|
1284
|
-
const ancestry = await db.execute(
|
|
1473
|
+
const ancestry = await db.execute(sql3`
|
|
1285
1474
|
with recursive caller_ancestry(id, parent_id, depth, path, cycle) as (
|
|
1286
1475
|
select session.id, session.parent_session_id, 0, array[session.id], false
|
|
1287
1476
|
from ${sessions} session
|
|
@@ -1333,17 +1522,17 @@ function canonicalize(value) {
|
|
|
1333
1522
|
return value;
|
|
1334
1523
|
}
|
|
1335
1524
|
function canonicalSessionCommandHash(value) {
|
|
1336
|
-
return
|
|
1525
|
+
return createHash2("sha256").update(JSON.stringify(canonicalize(value))).digest("hex");
|
|
1337
1526
|
}
|
|
1338
1527
|
function controlEtag(value) {
|
|
1339
1528
|
return `sc1:${canonicalSessionCommandHash(value)}`;
|
|
1340
1529
|
}
|
|
1341
1530
|
function lockClause(mode) {
|
|
1342
|
-
if (mode === "none") return
|
|
1343
|
-
return mode === "update" ?
|
|
1531
|
+
if (mode === "none") return sql3.empty();
|
|
1532
|
+
return mode === "update" ? sql3.raw("for update") : sql3.raw("for share");
|
|
1344
1533
|
}
|
|
1345
1534
|
async function lockWorkspaceInferenceControl(db, workspaceId, mode) {
|
|
1346
|
-
const rows = await db.execute(
|
|
1535
|
+
const rows = await db.execute(sql3`
|
|
1347
1536
|
select
|
|
1348
1537
|
workspace_id as "workspaceId",
|
|
1349
1538
|
account_id as "accountId",
|
|
@@ -1370,27 +1559,27 @@ async function lockSessionEventWriteRows(db, input) {
|
|
|
1370
1559
|
const control = controlLock === "share" || controlLock === "update" ? await lockWorkspaceInferenceControl(db, input.workspaceId, controlLock) : null;
|
|
1371
1560
|
let workspace = null;
|
|
1372
1561
|
if ((input.workspaceLock ?? "key_share") === "key_share") {
|
|
1373
|
-
const [lockedWorkspace] = await db.select().from(workspaces).where(
|
|
1562
|
+
const [lockedWorkspace] = await db.select().from(workspaces).where(eq5(workspaces.id, input.workspaceId)).for("key share").limit(1);
|
|
1374
1563
|
workspace = lockedWorkspace ?? null;
|
|
1375
1564
|
}
|
|
1376
1565
|
const sessionIds = [...new Set(input.sessionIds ?? [])].sort();
|
|
1377
1566
|
const sessions2 = sessionIds.length > 0 ? await db.select().from(sessions).where(
|
|
1378
|
-
|
|
1379
|
-
|
|
1567
|
+
and5(
|
|
1568
|
+
eq5(sessions.workspaceId, input.workspaceId),
|
|
1380
1569
|
inArray(sessions.id, sessionIds)
|
|
1381
1570
|
)
|
|
1382
1571
|
).orderBy(sessions.id).for("update") : [];
|
|
1383
1572
|
const turnIds = [...new Set(input.turnIds ?? [])].sort();
|
|
1384
1573
|
const turns = turnIds.length > 0 ? await db.select().from(sessionTurns).where(
|
|
1385
|
-
|
|
1386
|
-
|
|
1574
|
+
and5(
|
|
1575
|
+
eq5(sessionTurns.workspaceId, input.workspaceId),
|
|
1387
1576
|
inArray(sessionTurns.id, turnIds)
|
|
1388
1577
|
)
|
|
1389
1578
|
).orderBy(sessionTurns.id).for("update") : [];
|
|
1390
1579
|
const attemptIds = [...new Set(input.attemptIds ?? [])].sort();
|
|
1391
1580
|
const attempts = attemptIds.length > 0 ? await db.select().from(sessionTurnAttempts).where(
|
|
1392
|
-
|
|
1393
|
-
|
|
1581
|
+
and5(
|
|
1582
|
+
eq5(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
1394
1583
|
inArray(sessionTurnAttempts.id, attemptIds)
|
|
1395
1584
|
)
|
|
1396
1585
|
).orderBy(sessionTurnAttempts.id).for("update") : [];
|
|
@@ -1403,9 +1592,9 @@ async function registerSessionTurnAttemptClaim(db, input) {
|
|
|
1403
1592
|
}).onConflictDoNothing({ target: sessionTurnAttempts.id }).returning();
|
|
1404
1593
|
if (inserted) return inserted;
|
|
1405
1594
|
const [existing] = await db.select().from(sessionTurnAttempts).where(
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1595
|
+
and5(
|
|
1596
|
+
eq5(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
1597
|
+
eq5(sessionTurnAttempts.id, input.id)
|
|
1409
1598
|
)
|
|
1410
1599
|
).for("update").limit(1);
|
|
1411
1600
|
if (!existing || existing.accountId !== input.accountId || existing.sessionId !== input.sessionId || existing.turnId !== input.turnId || existing.executionGeneration !== input.executionGeneration || existing.temporalWorkflowId !== input.temporalWorkflowId || existing.temporalWorkflowRunId !== input.temporalWorkflowRunId || existing.temporalActivityId !== input.temporalActivityId || JSON.stringify(existing.connectorActionPolicies) !== JSON.stringify(input.connectorActionPolicies) || existing.state === "closed") {
|
|
@@ -1417,9 +1606,9 @@ async function registerSessionTurnAttemptClaim(db, input) {
|
|
|
1417
1606
|
}
|
|
1418
1607
|
async function closeSessionTurnAttemptInTransaction(db, input) {
|
|
1419
1608
|
const [attempt] = await db.select().from(sessionTurnAttempts).where(
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1609
|
+
and5(
|
|
1610
|
+
eq5(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
1611
|
+
eq5(sessionTurnAttempts.id, input.id)
|
|
1423
1612
|
)
|
|
1424
1613
|
).for("update").limit(1);
|
|
1425
1614
|
if (!attempt || attempt.accountId !== input.accountId || attempt.sessionId !== input.sessionId || attempt.turnId !== input.turnId || attempt.executionGeneration !== input.executionGeneration) {
|
|
@@ -1450,10 +1639,10 @@ async function closeSessionTurnAttemptInTransaction(db, input) {
|
|
|
1450
1639
|
closedAt: now,
|
|
1451
1640
|
updatedAt: now
|
|
1452
1641
|
}).where(
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1642
|
+
and5(
|
|
1643
|
+
eq5(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
1644
|
+
eq5(sessionTurnAttempts.id, input.id),
|
|
1645
|
+
sql3`${sessionTurnAttempts.state} in ('claimed', 'running')`
|
|
1457
1646
|
)
|
|
1458
1647
|
).returning({ id: sessionTurnAttempts.id });
|
|
1459
1648
|
if (!closed) {
|
|
@@ -1465,14 +1654,14 @@ function targetValues(sessionIds) {
|
|
|
1465
1654
|
if (sessionIds.length === 0) {
|
|
1466
1655
|
throw new SessionControlInvariantError("At least one target session is required");
|
|
1467
1656
|
}
|
|
1468
|
-
return
|
|
1469
|
-
sessionIds.map((id) =>
|
|
1470
|
-
|
|
1657
|
+
return sql3.join(
|
|
1658
|
+
sessionIds.map((id) => sql3`(${id}::uuid)`),
|
|
1659
|
+
sql3`, `
|
|
1471
1660
|
);
|
|
1472
1661
|
}
|
|
1473
1662
|
var TARGET_PATH_PROJECTION_LIMIT = 128;
|
|
1474
1663
|
async function loadTargetAncestryRows(db, workspaceId, sessionIds) {
|
|
1475
|
-
return await db.execute(
|
|
1664
|
+
return await db.execute(sql3`
|
|
1476
1665
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1477
1666
|
ancestry as (
|
|
1478
1667
|
select
|
|
@@ -1535,7 +1724,7 @@ async function loadTargetAncestryRows(db, workspaceId, sessionIds) {
|
|
|
1535
1724
|
`);
|
|
1536
1725
|
}
|
|
1537
1726
|
async function loadAncestryNodes(db, workspaceId, sessionIds) {
|
|
1538
|
-
const rows = await db.execute(
|
|
1727
|
+
const rows = await db.execute(sql3`
|
|
1539
1728
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1540
1729
|
ancestry_ids(id) as (
|
|
1541
1730
|
select session.id
|
|
@@ -1728,7 +1917,7 @@ function projectEffectiveControl(workspace, targetId, rows, settlementAttempts)
|
|
|
1728
1917
|
};
|
|
1729
1918
|
}
|
|
1730
1919
|
async function settlementAttemptCounts(db, workspaceId, sessionIds) {
|
|
1731
|
-
const rows = await db.execute(
|
|
1920
|
+
const rows = await db.execute(sql3`
|
|
1732
1921
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1733
1922
|
interruptions as (
|
|
1734
1923
|
select
|
|
@@ -1875,7 +2064,7 @@ async function evaluateSessionDiscoveryControls(db, workspaceId, sessionIds) {
|
|
|
1875
2064
|
if (workspace.workspaceState === "paused" && workspacePauseRevision === null) {
|
|
1876
2065
|
throw new SessionControlInvariantError("Paused workspace is missing its pause revision");
|
|
1877
2066
|
}
|
|
1878
|
-
const rows = await db.execute(
|
|
2067
|
+
const rows = await db.execute(sql3`
|
|
1879
2068
|
with recursive targets(id) as (values ${targetValues(uniqueIds)}),
|
|
1880
2069
|
ancestry as (
|
|
1881
2070
|
select
|
|
@@ -2042,22 +2231,22 @@ async function evaluateSessionControl(db, workspaceId, sessionId, options = {})
|
|
|
2042
2231
|
async function findCommandReceipt(db, input) {
|
|
2043
2232
|
const actorSubjectId = input.actor.type === "agent_attempt" ? null : input.actor.subjectId;
|
|
2044
2233
|
const actorAttemptId = input.actor.type === "agent_attempt" ? input.actor.attemptId : null;
|
|
2045
|
-
const identity = input.identityScope === "goal_operation" ?
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
) :
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2234
|
+
const identity = input.identityScope === "goal_operation" ? and5(
|
|
2235
|
+
eq5(sessionCommandReceipts.workspaceId, input.workspaceId),
|
|
2236
|
+
eq5(sessionCommandReceipts.actorType, "agent_attempt"),
|
|
2237
|
+
eq5(sessionCommandReceipts.action, input.action),
|
|
2238
|
+
eq5(sessionCommandReceipts.targetSessionId, input.targetSessionId),
|
|
2239
|
+
sql3`${sessionCommandReceipts.targetTurnId} is null`,
|
|
2240
|
+
eq5(sessionCommandReceipts.operationKey, input.operationKey)
|
|
2241
|
+
) : and5(
|
|
2242
|
+
eq5(sessionCommandReceipts.workspaceId, input.workspaceId),
|
|
2243
|
+
eq5(sessionCommandReceipts.actorType, input.actor.type),
|
|
2244
|
+
sql3`${sessionCommandReceipts.actorSubjectId} is not distinct from ${actorSubjectId}`,
|
|
2245
|
+
sql3`${sessionCommandReceipts.actorAttemptId} is not distinct from ${actorAttemptId}::uuid`,
|
|
2246
|
+
eq5(sessionCommandReceipts.action, input.action),
|
|
2247
|
+
sql3`${sessionCommandReceipts.targetSessionId} is not distinct from ${input.targetSessionId}::uuid`,
|
|
2248
|
+
sql3`${sessionCommandReceipts.targetTurnId} is not distinct from ${input.targetTurnId}::uuid`,
|
|
2249
|
+
eq5(sessionCommandReceipts.operationKey, input.operationKey)
|
|
2061
2250
|
);
|
|
2062
2251
|
const rows = await db.select().from(sessionCommandReceipts).where(identity).for("update");
|
|
2063
2252
|
return rows[0] ?? null;
|
|
@@ -2108,9 +2297,9 @@ function nextRevision(workspace) {
|
|
|
2108
2297
|
}
|
|
2109
2298
|
async function advanceWorkspaceRevision(db, workspaceId, revision) {
|
|
2110
2299
|
const rows = await db.update(workspaceInferenceControls).set({ revision, updatedAt: /* @__PURE__ */ new Date() }).where(
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2300
|
+
and5(
|
|
2301
|
+
eq5(workspaceInferenceControls.workspaceId, workspaceId),
|
|
2302
|
+
eq5(workspaceInferenceControls.revision, revision - 1)
|
|
2114
2303
|
)
|
|
2115
2304
|
).returning({ workspaceId: workspaceInferenceControls.workspaceId });
|
|
2116
2305
|
if (rows.length !== 1) {
|
|
@@ -2120,7 +2309,7 @@ async function advanceWorkspaceRevision(db, workspaceId, revision) {
|
|
|
2120
2309
|
}
|
|
2121
2310
|
}
|
|
2122
2311
|
async function registerContinuableWakes(db, input) {
|
|
2123
|
-
const rows = await db.execute(
|
|
2312
|
+
const rows = await db.execute(sql3`
|
|
2124
2313
|
with eligible as (
|
|
2125
2314
|
select *
|
|
2126
2315
|
from opengeni_private.list_continuable_sessions(
|
|
@@ -2162,7 +2351,7 @@ async function registerWorkspaceWakes(db, input) {
|
|
|
2162
2351
|
});
|
|
2163
2352
|
}
|
|
2164
2353
|
async function registerInterruptionWakes(db, input) {
|
|
2165
|
-
const rows = await db.execute(
|
|
2354
|
+
const rows = await db.execute(sql3`
|
|
2166
2355
|
with eligible as (
|
|
2167
2356
|
select distinct
|
|
2168
2357
|
session.id as session_id,
|
|
@@ -2198,11 +2387,11 @@ async function registerInterruptionWakes(db, input) {
|
|
|
2198
2387
|
}
|
|
2199
2388
|
async function registerCancellationWakes(db, input) {
|
|
2200
2389
|
if (input.sessionIds.length === 0) return 0;
|
|
2201
|
-
const sessionIds =
|
|
2202
|
-
input.sessionIds.map((id) =>
|
|
2203
|
-
|
|
2390
|
+
const sessionIds = sql3.join(
|
|
2391
|
+
input.sessionIds.map((id) => sql3`${id}::uuid`),
|
|
2392
|
+
sql3`, `
|
|
2204
2393
|
);
|
|
2205
|
-
const rows = await db.execute(
|
|
2394
|
+
const rows = await db.execute(sql3`
|
|
2206
2395
|
with eligible as (
|
|
2207
2396
|
select
|
|
2208
2397
|
session.id as session_id,
|
|
@@ -2244,7 +2433,7 @@ async function registerSessionWorkflowWakeInTransaction(db, input) {
|
|
|
2244
2433
|
target: sessionWorkflowWakeOutbox.sessionId,
|
|
2245
2434
|
set: {
|
|
2246
2435
|
temporalWorkflowId: input.temporalWorkflowId,
|
|
2247
|
-
wakeRevision:
|
|
2436
|
+
wakeRevision: sql3`${sessionWorkflowWakeOutbox.wakeRevision} + 1`,
|
|
2248
2437
|
reason: input.reason,
|
|
2249
2438
|
attempts: 0,
|
|
2250
2439
|
nextAttemptAt: /* @__PURE__ */ new Date(),
|
|
@@ -2259,9 +2448,9 @@ async function registerSessionWorkflowWakeInTransaction(db, input) {
|
|
|
2259
2448
|
}
|
|
2260
2449
|
async function registerInternalUpdateWakeInTransaction(db, input) {
|
|
2261
2450
|
const [existing] = await db.select().from(sessionWorkflowWakeOutbox).where(
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2451
|
+
and5(
|
|
2452
|
+
eq5(sessionWorkflowWakeOutbox.workspaceId, input.workspaceId),
|
|
2453
|
+
eq5(sessionWorkflowWakeOutbox.sessionId, input.sessionId)
|
|
2265
2454
|
)
|
|
2266
2455
|
).for("update").limit(1);
|
|
2267
2456
|
if (existing && existing.wakeRevision > existing.deliveredRevision) {
|
|
@@ -2271,7 +2460,7 @@ async function registerInternalUpdateWakeInTransaction(db, input) {
|
|
|
2271
2460
|
nextAttemptAt: /* @__PURE__ */ new Date(),
|
|
2272
2461
|
lastError: null,
|
|
2273
2462
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2274
|
-
}).where(
|
|
2463
|
+
}).where(eq5(sessionWorkflowWakeOutbox.sessionId, input.sessionId));
|
|
2275
2464
|
return { wakeRevision: existing.wakeRevision, shouldSignal: false };
|
|
2276
2465
|
}
|
|
2277
2466
|
return {
|
|
@@ -2283,7 +2472,7 @@ async function registerInternalUpdateWakeInTransaction(db, input) {
|
|
|
2283
2472
|
};
|
|
2284
2473
|
}
|
|
2285
2474
|
async function interruptDescendantAttempts(db, input) {
|
|
2286
|
-
const rows = await db.execute(
|
|
2475
|
+
const rows = await db.execute(sql3`
|
|
2287
2476
|
with recursive live_attempts as (
|
|
2288
2477
|
select attempt.id, attempt.account_id, attempt.workspace_id, attempt.session_id
|
|
2289
2478
|
from ${sessionTurnAttempts} attempt
|
|
@@ -2331,7 +2520,7 @@ async function interruptDescendantAttempts(db, input) {
|
|
|
2331
2520
|
return Number(rows[0]?.count ?? 0);
|
|
2332
2521
|
}
|
|
2333
2522
|
async function interruptWorkspaceAttempts(db, input) {
|
|
2334
|
-
const rows = await db.execute(
|
|
2523
|
+
const rows = await db.execute(sql3`
|
|
2335
2524
|
with inserted as (
|
|
2336
2525
|
insert into ${sessionAttemptInterruptions} (
|
|
2337
2526
|
account_id, workspace_id, session_id, operation_id, attempt_id,
|
|
@@ -2358,12 +2547,12 @@ async function updateSessionCommandReceiptResult(db, receiptId, input) {
|
|
|
2358
2547
|
...input.draftRevision !== void 0 ? { appliedDraftRevision: input.draftRevision } : {},
|
|
2359
2548
|
result: input.result,
|
|
2360
2549
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2361
|
-
}).where(
|
|
2550
|
+
}).where(eq5(sessionCommandReceipts.id, receiptId)).returning();
|
|
2362
2551
|
if (!receipt) throw new SessionControlInvariantError("Command receipt disappeared");
|
|
2363
2552
|
return receipt;
|
|
2364
2553
|
}
|
|
2365
2554
|
async function loadSessionSubtreeIds(db, workspaceId, rootSessionId) {
|
|
2366
|
-
const rows = await db.execute(
|
|
2555
|
+
const rows = await db.execute(sql3`
|
|
2367
2556
|
with recursive subtree(id, root_parent_session_id, depth, path, cycle) as (
|
|
2368
2557
|
select session.id, session.parent_session_id, 0::integer, array[session.id]::uuid[], false
|
|
2369
2558
|
from ${sessions} session
|
|
@@ -2400,8 +2589,8 @@ var TERMINAL_CANCELLATION_LIVE_TURN_STATUSES = [
|
|
|
2400
2589
|
];
|
|
2401
2590
|
async function loadCancellationTurnIds(db, workspaceId, sessionIds) {
|
|
2402
2591
|
const rows = await db.select({ id: sessionTurns.id }).from(sessionTurns).where(
|
|
2403
|
-
|
|
2404
|
-
|
|
2592
|
+
and5(
|
|
2593
|
+
eq5(sessionTurns.workspaceId, workspaceId),
|
|
2405
2594
|
inArray(sessionTurns.sessionId, sessionIds),
|
|
2406
2595
|
inArray(sessionTurns.status, TERMINAL_CANCELLATION_LIVE_TURN_STATUSES)
|
|
2407
2596
|
)
|
|
@@ -2420,10 +2609,10 @@ async function enqueueCancelledChildOutboxInTransaction(db, input) {
|
|
|
2420
2609
|
let personalConnectionDelegations = [];
|
|
2421
2610
|
if (input.rootSession.parentTurnId) {
|
|
2422
2611
|
const [parentTurn] = await db.select({ delegations: sessionTurns.personalConnectionDelegations }).from(sessionTurns).where(
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2612
|
+
and5(
|
|
2613
|
+
eq5(sessionTurns.workspaceId, input.workspaceId),
|
|
2614
|
+
eq5(sessionTurns.sessionId, parentSessionId),
|
|
2615
|
+
eq5(sessionTurns.id, input.rootSession.parentTurnId)
|
|
2427
2616
|
)
|
|
2428
2617
|
).limit(1);
|
|
2429
2618
|
if (parentTurn) {
|
|
@@ -2465,7 +2654,7 @@ async function enqueueCancelledChildOutboxInTransaction(db, input) {
|
|
|
2465
2654
|
});
|
|
2466
2655
|
}
|
|
2467
2656
|
async function assertSessionBranchIsNotCancelled(db, workspaceId, sessionId) {
|
|
2468
|
-
const rows = await db.execute(
|
|
2657
|
+
const rows = await db.execute(sql3`
|
|
2469
2658
|
with recursive ancestry(id, parent_id, status, depth, path, cycle) as (
|
|
2470
2659
|
select session.id, session.parent_session_id, session.status, 0::integer,
|
|
2471
2660
|
array[session.id]::uuid[], false
|
|
@@ -2540,7 +2729,7 @@ async function cancelSessionSubtreeInTransaction(db, input) {
|
|
|
2540
2729
|
activeAttemptId: null,
|
|
2541
2730
|
cancelledBy: input.actor,
|
|
2542
2731
|
cancelReason: input.reason ?? "session_cancelled",
|
|
2543
|
-
version:
|
|
2732
|
+
version: sql3`${sessionTurns.version} + 1`,
|
|
2544
2733
|
finishedAt: now,
|
|
2545
2734
|
updatedAt: now
|
|
2546
2735
|
}).where(inArray(sessionTurns.id, immediatelyCancelledTurnIds));
|
|
@@ -2551,29 +2740,30 @@ async function cancelSessionSubtreeInTransaction(db, input) {
|
|
|
2551
2740
|
respondedAt: now,
|
|
2552
2741
|
updatedAt: now
|
|
2553
2742
|
}).where(
|
|
2554
|
-
|
|
2555
|
-
|
|
2743
|
+
and5(
|
|
2744
|
+
eq5(sessionHumanInputRequests.workspaceId, input.workspaceId),
|
|
2556
2745
|
inArray(sessionHumanInputRequests.turnId, immediatelyCancelledTurnIds),
|
|
2557
|
-
|
|
2746
|
+
eq5(sessionHumanInputRequests.status, "pending")
|
|
2558
2747
|
)
|
|
2559
2748
|
).returning({
|
|
2560
2749
|
id: sessionHumanInputRequests.id,
|
|
2561
2750
|
sessionId: sessionHumanInputRequests.sessionId,
|
|
2562
|
-
turnId: sessionHumanInputRequests.turnId
|
|
2751
|
+
turnId: sessionHumanInputRequests.turnId,
|
|
2752
|
+
questions: sessionHumanInputRequests.questions
|
|
2563
2753
|
});
|
|
2564
2754
|
await db.update(codexCapacityWaiters).set({ status: "superseded", lastWakeReason: "session_cancelled", updatedAt: now }).where(
|
|
2565
|
-
|
|
2566
|
-
|
|
2755
|
+
and5(
|
|
2756
|
+
eq5(codexCapacityWaiters.workspaceId, input.workspaceId),
|
|
2567
2757
|
inArray(codexCapacityWaiters.blockedTurnId, immediatelyCancelledTurnIds),
|
|
2568
|
-
|
|
2758
|
+
eq5(codexCapacityWaiters.status, "waiting")
|
|
2569
2759
|
)
|
|
2570
2760
|
);
|
|
2571
2761
|
}
|
|
2572
2762
|
const cancelledSystemUpdates = await db.update(sessionSystemUpdates).set({ state: "cancelled" }).where(
|
|
2573
|
-
|
|
2574
|
-
|
|
2763
|
+
and5(
|
|
2764
|
+
eq5(sessionSystemUpdates.workspaceId, input.workspaceId),
|
|
2575
2765
|
inArray(sessionSystemUpdates.sessionId, input.sessionIds),
|
|
2576
|
-
|
|
2766
|
+
eq5(sessionSystemUpdates.state, "pending")
|
|
2577
2767
|
)
|
|
2578
2768
|
).returning({
|
|
2579
2769
|
id: sessionSystemUpdates.id,
|
|
@@ -2674,19 +2864,51 @@ async function cancelSessionSubtreeInTransaction(db, input) {
|
|
|
2674
2864
|
}
|
|
2675
2865
|
const inserted = eventValues.length > 0 ? await db.insert(sessionEvents).values(eventValues).returning({
|
|
2676
2866
|
id: sessionEvents.id,
|
|
2677
|
-
sequence: sessionEvents.sequence
|
|
2867
|
+
sequence: sessionEvents.sequence,
|
|
2868
|
+
type: sessionEvents.type,
|
|
2869
|
+
turnId: sessionEvents.turnId,
|
|
2870
|
+
payload: sessionEvents.payload
|
|
2678
2871
|
}) : [];
|
|
2872
|
+
const cancelledHumanInputsById = new Map(
|
|
2873
|
+
cancelledHumanInputs.filter((request) => request.sessionId === session.id).map((request) => [request.id, request])
|
|
2874
|
+
);
|
|
2875
|
+
for (const event of inserted) {
|
|
2876
|
+
if (event.type !== "user.humanInputResponse") continue;
|
|
2877
|
+
const payload = event.payload;
|
|
2878
|
+
const request = typeof payload.requestId === "string" ? cancelledHumanInputsById.get(payload.requestId) : null;
|
|
2879
|
+
if (!request) continue;
|
|
2880
|
+
await mirrorSessionRealtimeContextInTransaction(db, {
|
|
2881
|
+
accountId: input.accountId,
|
|
2882
|
+
workspaceId: input.workspaceId,
|
|
2883
|
+
sessionId: session.id,
|
|
2884
|
+
sourceKind: "human_input_response",
|
|
2885
|
+
sourceId: event.id,
|
|
2886
|
+
turnId: event.turnId,
|
|
2887
|
+
channel: null,
|
|
2888
|
+
text: renderRealtimeHumanInputResponseContext({
|
|
2889
|
+
requestId: request.id,
|
|
2890
|
+
questions: request.questions,
|
|
2891
|
+
response: { outcome: "cancelled" }
|
|
2892
|
+
}),
|
|
2893
|
+
payload: {
|
|
2894
|
+
requestId: request.id,
|
|
2895
|
+
outcome: "cancelled",
|
|
2896
|
+
sourceEventId: event.id
|
|
2897
|
+
},
|
|
2898
|
+
now
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2679
2901
|
const liveTurnId = session.activeTurnId && liveTurnIds.has(session.activeTurnId) ? session.activeTurnId : null;
|
|
2680
2902
|
await db.update(sessions).set({
|
|
2681
2903
|
status: "cancelled",
|
|
2682
2904
|
activeTurnId: liveTurnId,
|
|
2683
|
-
queueVersion:
|
|
2905
|
+
queueVersion: sql3`${sessions.queueVersion} + 1`,
|
|
2684
2906
|
queueHeadPosition: 0,
|
|
2685
2907
|
queueTailPosition: 0,
|
|
2686
2908
|
lastSequence: sequence,
|
|
2687
2909
|
updatedAt: now
|
|
2688
2910
|
}).where(
|
|
2689
|
-
|
|
2911
|
+
and5(eq5(sessions.workspaceId, input.workspaceId), eq5(sessions.id, session.id))
|
|
2690
2912
|
);
|
|
2691
2913
|
const eventIds = [...preinsertedEvents, ...inserted].sort((left, right) => left.sequence - right.sequence).map((event) => event.id);
|
|
2692
2914
|
if (session.id === input.rootSessionId) eventIds.unshift(input.rootControlEventId);
|
|
@@ -2810,9 +3032,9 @@ async function mutateSessionControlInTransaction(db, input) {
|
|
|
2810
3032
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2811
3033
|
}
|
|
2812
3034
|
).where(
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
input.action === "cancel" ? inArray(sessions.id, cancellationSessionIds) :
|
|
3035
|
+
and5(
|
|
3036
|
+
eq5(sessions.workspaceId, input.workspaceId),
|
|
3037
|
+
input.action === "cancel" ? inArray(sessions.id, cancellationSessionIds) : eq5(sessions.id, input.sessionId)
|
|
2816
3038
|
)
|
|
2817
3039
|
).returning({
|
|
2818
3040
|
id: sessions.id,
|
|
@@ -2869,7 +3091,7 @@ async function mutateSessionControlInTransaction(db, input) {
|
|
|
2869
3091
|
}).returning({ id: sessionEvents.id });
|
|
2870
3092
|
if (!controlEvent)
|
|
2871
3093
|
throw new SessionControlInvariantError("Session control event was not inserted");
|
|
2872
|
-
await db.update(sessions).set({ lastSequence: updated.lastSequence + 1, updatedAt: /* @__PURE__ */ new Date() }).where(
|
|
3094
|
+
await db.update(sessions).set({ lastSequence: updated.lastSequence + 1, updatedAt: /* @__PURE__ */ new Date() }).where(eq5(sessions.id, input.sessionId));
|
|
2873
3095
|
await db.insert(auditEvents).values({
|
|
2874
3096
|
accountId: input.accountId,
|
|
2875
3097
|
workspaceId: input.workspaceId,
|
|
@@ -2963,9 +3185,9 @@ async function autoResumeSessionBranchInTransaction(db, input) {
|
|
|
2963
3185
|
directControlChangedAt: /* @__PURE__ */ new Date(),
|
|
2964
3186
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2965
3187
|
}).where(
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
3188
|
+
and5(
|
|
3189
|
+
eq5(sessions.workspaceId, input.workspaceId),
|
|
3190
|
+
eq5(sessions.id, input.sessionId)
|
|
2969
3191
|
)
|
|
2970
3192
|
);
|
|
2971
3193
|
const workspaceControlEventId = await insertWorkspaceControlEventInTransaction(db, {
|
|
@@ -3035,9 +3257,9 @@ async function mutateWorkspaceControlInTransaction(db, input) {
|
|
|
3035
3257
|
changedAt: /* @__PURE__ */ new Date(),
|
|
3036
3258
|
updatedAt: /* @__PURE__ */ new Date()
|
|
3037
3259
|
}).where(
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3260
|
+
and5(
|
|
3261
|
+
eq5(workspaceInferenceControls.workspaceId, input.workspaceId),
|
|
3262
|
+
eq5(workspaceInferenceControls.revision, currentRevision)
|
|
3041
3263
|
)
|
|
3042
3264
|
).returning({ workspaceId: workspaceInferenceControls.workspaceId });
|
|
3043
3265
|
if (!updated) {
|
|
@@ -3114,7 +3336,7 @@ async function insertWorkspaceControlEventInTransaction(db, input) {
|
|
|
3114
3336
|
|
|
3115
3337
|
// src/session-realtime.ts
|
|
3116
3338
|
import { createHash as createHash4, timingSafeEqual } from "crypto";
|
|
3117
|
-
import { and as and9, asc as asc4, eq as eq9, inArray as inArray4 } from "drizzle-orm";
|
|
3339
|
+
import { and as and9, asc as asc4, eq as eq9, gt as gt3, inArray as inArray4, isNull as isNull2, or } from "drizzle-orm";
|
|
3118
3340
|
|
|
3119
3341
|
// src/session-realtime-context.ts
|
|
3120
3342
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -3134,115 +3356,17 @@ import {
|
|
|
3134
3356
|
import { and as and7, asc as asc2, eq as eq7, inArray as inArray2, sql as sql4 } from "drizzle-orm";
|
|
3135
3357
|
|
|
3136
3358
|
// src/session-realtime-state.ts
|
|
3137
|
-
import { and as
|
|
3359
|
+
import { and as and6, eq as eq6, gt as gt2 } from "drizzle-orm";
|
|
3138
3360
|
async function sessionRealtimeIsActiveInTransaction(db, workspaceId, sessionId, now = /* @__PURE__ */ new Date()) {
|
|
3139
3361
|
const [row] = await db.select({ id: sessionRealtimeModes.id }).from(sessionRealtimeModes).where(
|
|
3140
|
-
and5(
|
|
3141
|
-
eq5(sessionRealtimeModes.workspaceId, workspaceId),
|
|
3142
|
-
eq5(sessionRealtimeModes.sessionId, sessionId),
|
|
3143
|
-
eq5(sessionRealtimeModes.state, "active"),
|
|
3144
|
-
gt(sessionRealtimeModes.leaseExpiresAt, now)
|
|
3145
|
-
)
|
|
3146
|
-
).limit(1);
|
|
3147
|
-
return Boolean(row);
|
|
3148
|
-
}
|
|
3149
|
-
|
|
3150
|
-
// src/session-realtime-mirror.ts
|
|
3151
|
-
import { createHash as createHash2 } from "crypto";
|
|
3152
|
-
import { and as and6, desc, eq as eq6, gt as gt2, sql as sql3 } from "drizzle-orm";
|
|
3153
|
-
var SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES = 131072;
|
|
3154
|
-
var SESSION_REALTIME_MIRROR_MAX_PAYLOAD_BYTES = 131072;
|
|
3155
|
-
var TRUNCATION_MARKER = "\n\u2026realtime context truncated\u2026";
|
|
3156
|
-
function deterministicUuid(seed) {
|
|
3157
|
-
const bytes = createHash2("sha256").update(seed, "utf8").digest().subarray(0, 16);
|
|
3158
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
3159
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
3160
|
-
const hex = bytes.toString("hex");
|
|
3161
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
3162
|
-
}
|
|
3163
|
-
function takeUtf8Head(value, maximumBytes) {
|
|
3164
|
-
const bytes = Buffer.from(value, "utf8");
|
|
3165
|
-
if (bytes.length <= maximumBytes) return value;
|
|
3166
|
-
let end = maximumBytes;
|
|
3167
|
-
while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
|
|
3168
|
-
return bytes.subarray(0, end).toString("utf8");
|
|
3169
|
-
}
|
|
3170
|
-
function boundedText(value) {
|
|
3171
|
-
if (Buffer.byteLength(value, "utf8") <= SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES) return value;
|
|
3172
|
-
const markerBytes = Buffer.byteLength(TRUNCATION_MARKER, "utf8");
|
|
3173
|
-
return `${takeUtf8Head(value, SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES - markerBytes)}${TRUNCATION_MARKER}`;
|
|
3174
|
-
}
|
|
3175
|
-
function boundedPayload(value) {
|
|
3176
|
-
const payload = sanitizeEventPayload(value);
|
|
3177
|
-
if (Buffer.byteLength(JSON.stringify(payload), "utf8") > SESSION_REALTIME_MIRROR_MAX_PAYLOAD_BYTES) {
|
|
3178
|
-
throw new Error("Realtime mirror payload exceeds the durable ledger limit");
|
|
3179
|
-
}
|
|
3180
|
-
return payload;
|
|
3181
|
-
}
|
|
3182
|
-
async function mirrorSessionRealtimeContextInTransaction(db, input) {
|
|
3183
|
-
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
3184
|
-
const modes = await db.select().from(sessionRealtimeModes).where(
|
|
3185
3362
|
and6(
|
|
3186
|
-
eq6(sessionRealtimeModes.
|
|
3187
|
-
eq6(sessionRealtimeModes.
|
|
3188
|
-
eq6(sessionRealtimeModes.sessionId, input.sessionId),
|
|
3363
|
+
eq6(sessionRealtimeModes.workspaceId, workspaceId),
|
|
3364
|
+
eq6(sessionRealtimeModes.sessionId, sessionId),
|
|
3189
3365
|
eq6(sessionRealtimeModes.state, "active"),
|
|
3190
3366
|
gt2(sessionRealtimeModes.leaseExpiresAt, now)
|
|
3191
3367
|
)
|
|
3192
|
-
).orderBy(desc(sessionRealtimeModes.startedAt)).for("update").limit(2);
|
|
3193
|
-
if (modes.length === 0) return null;
|
|
3194
|
-
if (modes.length !== 1) {
|
|
3195
|
-
throw new Error(`Session ${input.sessionId} has multiple active realtime modes`);
|
|
3196
|
-
}
|
|
3197
|
-
const mode = modes[0];
|
|
3198
|
-
const operationId = deterministicUuid(
|
|
3199
|
-
`opengeni:session-realtime-mirror:${mode.id}:${input.sourceKind}:${input.sourceId}`
|
|
3200
|
-
);
|
|
3201
|
-
const [existing] = await db.select().from(sessionRealtimeEntries).where(
|
|
3202
|
-
and6(
|
|
3203
|
-
eq6(sessionRealtimeEntries.realtimeId, mode.id),
|
|
3204
|
-
eq6(sessionRealtimeEntries.operationId, operationId)
|
|
3205
|
-
)
|
|
3206
3368
|
).limit(1);
|
|
3207
|
-
|
|
3208
|
-
const [sequenceRow] = await db.select({ next: sql3`coalesce(max(${sessionRealtimeEntries.sequence}), 0) + 1` }).from(sessionRealtimeEntries).where(eq6(sessionRealtimeEntries.realtimeId, mode.id));
|
|
3209
|
-
const payload = boundedPayload({
|
|
3210
|
-
...input.payload ?? {},
|
|
3211
|
-
source: input.sourceKind,
|
|
3212
|
-
sourceId: input.sourceId,
|
|
3213
|
-
channel: input.channel,
|
|
3214
|
-
...input.turnId ? { sourceTurnId: input.turnId } : {}
|
|
3215
|
-
});
|
|
3216
|
-
const [entry] = await db.insert(sessionRealtimeEntries).values({
|
|
3217
|
-
accountId: input.accountId,
|
|
3218
|
-
workspaceId: input.workspaceId,
|
|
3219
|
-
sessionId: input.sessionId,
|
|
3220
|
-
realtimeId: mode.id,
|
|
3221
|
-
operationId,
|
|
3222
|
-
connectionEpoch: mode.connectionEpoch,
|
|
3223
|
-
sequence: Number(sequenceRow?.next ?? 1),
|
|
3224
|
-
direction: "provider_out",
|
|
3225
|
-
kind: "session_update",
|
|
3226
|
-
text: boundedText(input.text),
|
|
3227
|
-
payload,
|
|
3228
|
-
createdAt: now,
|
|
3229
|
-
updatedAt: now
|
|
3230
|
-
}).returning();
|
|
3231
|
-
if (!entry) throw new Error("Failed to append realtime session context");
|
|
3232
|
-
return { entry, replay: false };
|
|
3233
|
-
}
|
|
3234
|
-
function renderRealtimeHumanInputContext(input) {
|
|
3235
|
-
return [
|
|
3236
|
-
"<session_user_message>",
|
|
3237
|
-
` <status>${input.routing}</status>`,
|
|
3238
|
-
` <delivery>${input.delivery}</delivery>`,
|
|
3239
|
-
` <text>${escapeXmlText(input.text)}</text>`,
|
|
3240
|
-
" <instruction>Already routed by OpenGeni; do not delegate this message again.</instruction>",
|
|
3241
|
-
"</session_user_message>"
|
|
3242
|
-
].join("\n");
|
|
3243
|
-
}
|
|
3244
|
-
function escapeXmlText(value) {
|
|
3245
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
3369
|
+
return Boolean(row);
|
|
3246
3370
|
}
|
|
3247
3371
|
|
|
3248
3372
|
// src/session-queue-commands.ts
|
|
@@ -3370,10 +3494,13 @@ async function supersedeSessionCurrentDirectionInTransaction(db, input) {
|
|
|
3370
3494
|
eq7(sessionHumanInputRequests.turnId, current.id),
|
|
3371
3495
|
eq7(sessionHumanInputRequests.status, "pending")
|
|
3372
3496
|
)
|
|
3373
|
-
).returning({
|
|
3497
|
+
).returning({
|
|
3498
|
+
id: sessionHumanInputRequests.id,
|
|
3499
|
+
questions: sessionHumanInputRequests.questions
|
|
3500
|
+
});
|
|
3374
3501
|
let lastSequence = closedTools.sequence;
|
|
3375
3502
|
if (cancelledHumanInputs.length > 0) {
|
|
3376
|
-
await db.insert(sessionEvents).values(
|
|
3503
|
+
const cancelledHumanInputEvents = await db.insert(sessionEvents).values(
|
|
3377
3504
|
cancelledHumanInputs.map((request) => ({
|
|
3378
3505
|
accountId: input.accountId,
|
|
3379
3506
|
workspaceId: input.workspaceId,
|
|
@@ -3386,7 +3513,33 @@ async function supersedeSessionCurrentDirectionInTransaction(db, input) {
|
|
|
3386
3513
|
payload: { requestId: request.id, response: { outcome: "cancelled" } },
|
|
3387
3514
|
occurredAt: now
|
|
3388
3515
|
}))
|
|
3389
|
-
);
|
|
3516
|
+
).returning();
|
|
3517
|
+
const requestsById = new Map(cancelledHumanInputs.map((request) => [request.id, request]));
|
|
3518
|
+
for (const event of cancelledHumanInputEvents) {
|
|
3519
|
+
const payload = event.payload;
|
|
3520
|
+
const request = typeof payload.requestId === "string" ? requestsById.get(payload.requestId) : null;
|
|
3521
|
+
if (!request) continue;
|
|
3522
|
+
await mirrorSessionRealtimeContextInTransaction(db, {
|
|
3523
|
+
accountId: input.accountId,
|
|
3524
|
+
workspaceId: input.workspaceId,
|
|
3525
|
+
sessionId: input.sessionId,
|
|
3526
|
+
sourceKind: "human_input_response",
|
|
3527
|
+
sourceId: event.id,
|
|
3528
|
+
turnId: current.id,
|
|
3529
|
+
channel: null,
|
|
3530
|
+
text: renderRealtimeHumanInputResponseContext({
|
|
3531
|
+
requestId: request.id,
|
|
3532
|
+
questions: request.questions,
|
|
3533
|
+
response: { outcome: "cancelled" }
|
|
3534
|
+
}),
|
|
3535
|
+
payload: {
|
|
3536
|
+
requestId: request.id,
|
|
3537
|
+
outcome: "cancelled",
|
|
3538
|
+
sourceEventId: event.id
|
|
3539
|
+
},
|
|
3540
|
+
now
|
|
3541
|
+
});
|
|
3542
|
+
}
|
|
3390
3543
|
}
|
|
3391
3544
|
await db.update(sessionTurns).set({
|
|
3392
3545
|
status: "superseded",
|
|
@@ -5435,6 +5588,46 @@ async function beginSessionRealtimeInTransaction(db, input) {
|
|
|
5435
5588
|
now
|
|
5436
5589
|
);
|
|
5437
5590
|
eventIds.push(event.id);
|
|
5591
|
+
const pendingHumanInputs = await db.select({
|
|
5592
|
+
id: sessionHumanInputRequests.id,
|
|
5593
|
+
turnId: sessionHumanInputRequests.turnId,
|
|
5594
|
+
questions: sessionHumanInputRequests.questions,
|
|
5595
|
+
allowSkip: sessionHumanInputRequests.allowSkip,
|
|
5596
|
+
expiresAt: sessionHumanInputRequests.expiresAt
|
|
5597
|
+
}).from(sessionHumanInputRequests).where(
|
|
5598
|
+
and9(
|
|
5599
|
+
eq9(sessionHumanInputRequests.accountId, input.accountId),
|
|
5600
|
+
eq9(sessionHumanInputRequests.workspaceId, input.workspaceId),
|
|
5601
|
+
eq9(sessionHumanInputRequests.sessionId, input.sessionId),
|
|
5602
|
+
eq9(sessionHumanInputRequests.status, "pending"),
|
|
5603
|
+
or(
|
|
5604
|
+
isNull2(sessionHumanInputRequests.expiresAt),
|
|
5605
|
+
gt3(sessionHumanInputRequests.expiresAt, now)
|
|
5606
|
+
)
|
|
5607
|
+
)
|
|
5608
|
+
).orderBy(
|
|
5609
|
+
asc4(sessionHumanInputRequests.createdAt),
|
|
5610
|
+
asc4(sessionHumanInputRequests.id)
|
|
5611
|
+
);
|
|
5612
|
+
if (pendingHumanInputs.length > 0) {
|
|
5613
|
+
const sourceTurnIds = new Set(pendingHumanInputs.map((request) => request.turnId));
|
|
5614
|
+
await mirrorSessionRealtimeContextInTransaction(db, {
|
|
5615
|
+
accountId: input.accountId,
|
|
5616
|
+
workspaceId: input.workspaceId,
|
|
5617
|
+
sessionId: input.sessionId,
|
|
5618
|
+
sourceKind: "human_input_request",
|
|
5619
|
+
sourceId: `startup:${pendingHumanInputs.map((request) => request.id).join(":")}`,
|
|
5620
|
+
turnId: sourceTurnIds.size === 1 ? pendingHumanInputs[0].turnId : null,
|
|
5621
|
+
channel: "speakable",
|
|
5622
|
+
text: renderRealtimeHumanInputRequestContext({ requests: pendingHumanInputs }),
|
|
5623
|
+
payload: {
|
|
5624
|
+
status: "waiting_for_user",
|
|
5625
|
+
requestIds: pendingHumanInputs.map((request) => request.id),
|
|
5626
|
+
trigger: "realtime_start"
|
|
5627
|
+
},
|
|
5628
|
+
now
|
|
5629
|
+
});
|
|
5630
|
+
}
|
|
5438
5631
|
return {
|
|
5439
5632
|
mode: mapRealtimeMode(row),
|
|
5440
5633
|
replay: false,
|
|
@@ -5566,7 +5759,7 @@ async function settleExpiredSessionRealtimeInTransaction(db, session, now = /* @
|
|
|
5566
5759
|
// src/session-realtime-ledger.ts
|
|
5567
5760
|
import { createHash as createHash6 } from "crypto";
|
|
5568
5761
|
import { LatencyMode, ReasoningEffort as ReasoningEffort2 } from "@opengeni/contracts";
|
|
5569
|
-
import { and as and11, asc as asc5, desc as desc3, eq as eq11, inArray as inArray6, isNull as
|
|
5762
|
+
import { and as and11, asc as asc5, desc as desc3, eq as eq11, inArray as inArray6, isNull as isNull3, or as or2, sql as sql7 } from "drizzle-orm";
|
|
5570
5763
|
|
|
5571
5764
|
// src/session-realtime-terminal.ts
|
|
5572
5765
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -6016,7 +6209,7 @@ async function claimSessionRealtimeConnectionInTransaction(db, input) {
|
|
|
6016
6209
|
and11(
|
|
6017
6210
|
eq11(sessionRealtimeEntries.realtimeId, input.realtimeId),
|
|
6018
6211
|
eq11(sessionRealtimeEntries.direction, "provider_out"),
|
|
6019
|
-
|
|
6212
|
+
isNull3(sessionRealtimeEntries.providerAckedAt)
|
|
6020
6213
|
)
|
|
6021
6214
|
).orderBy(asc5(sessionRealtimeEntries.sequence)).limit(SESSION_REALTIME_STARTUP_MAX_ENTRIES);
|
|
6022
6215
|
const startupFenceSequence = startupEntries.at(-1)?.sequence ?? 0;
|
|
@@ -6048,7 +6241,7 @@ async function startupEntriesForConnection(db, connection) {
|
|
|
6048
6241
|
and11(
|
|
6049
6242
|
eq11(sessionRealtimeEntries.realtimeId, connection.realtimeId),
|
|
6050
6243
|
eq11(sessionRealtimeEntries.direction, "provider_out"),
|
|
6051
|
-
|
|
6244
|
+
isNull3(sessionRealtimeEntries.providerAckedAt),
|
|
6052
6245
|
sql7`${sessionRealtimeEntries.sequence} <= ${connection.startupFenceSequence}`
|
|
6053
6246
|
)
|
|
6054
6247
|
).orderBy(asc5(sessionRealtimeEntries.sequence)).limit(SESSION_REALTIME_STARTUP_MAX_ENTRIES);
|
|
@@ -6470,7 +6663,7 @@ async function materializeRealtimeUpdates(db, input, accountId, allocateSequence
|
|
|
6470
6663
|
eq11(sessionSystemUpdates.sessionId, input.sessionId),
|
|
6471
6664
|
inArray6(sessionSystemUpdates.state, ["pending", "deferred"]),
|
|
6472
6665
|
inArray6(sessionSystemUpdates.kind, ["agent_message", "child_terminal_result"]),
|
|
6473
|
-
|
|
6666
|
+
isNull3(sessionRealtimeEntries.id)
|
|
6474
6667
|
)
|
|
6475
6668
|
).orderBy(asc5(sessionSystemUpdates.createdAt), asc5(sessionSystemUpdates.id)).limit(SESSION_REALTIME_LEDGER_MAX_OUTBOUND);
|
|
6476
6669
|
const updates = rows.map(({ update }) => update);
|
|
@@ -6579,7 +6772,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6579
6772
|
}).where(
|
|
6580
6773
|
and11(
|
|
6581
6774
|
eq11(sessionRealtimeConnections.id, connection.id),
|
|
6582
|
-
|
|
6775
|
+
isNull3(sessionRealtimeConnections.startupAcknowledgedAt)
|
|
6583
6776
|
)
|
|
6584
6777
|
).returning({ id: sessionRealtimeConnections.id });
|
|
6585
6778
|
if (!acknowledged) {
|
|
@@ -6616,7 +6809,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6616
6809
|
const [existing] = await db.select().from(sessionRealtimeEntries).where(
|
|
6617
6810
|
and11(
|
|
6618
6811
|
eq11(sessionRealtimeEntries.realtimeId, input.realtimeId),
|
|
6619
|
-
incoming.kind === "delegation_call" && incoming.delegationItemId ?
|
|
6812
|
+
incoming.kind === "delegation_call" && incoming.delegationItemId ? or2(
|
|
6620
6813
|
eq11(sessionRealtimeEntries.operationId, incoming.operationId),
|
|
6621
6814
|
and11(
|
|
6622
6815
|
eq11(sessionRealtimeEntries.kind, "delegation_call"),
|
|
@@ -6695,7 +6888,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6695
6888
|
eq11(sessionRealtimeEntries.sessionId, input.sessionId),
|
|
6696
6889
|
eq11(sessionRealtimeEntries.realtimeId, input.realtimeId),
|
|
6697
6890
|
eq11(sessionRealtimeEntries.direction, "provider_out"),
|
|
6698
|
-
|
|
6891
|
+
isNull3(sessionRealtimeEntries.clientAckedAt),
|
|
6699
6892
|
sql7`${sessionRealtimeEntries.sequence} <= ${clientAck}`
|
|
6700
6893
|
)
|
|
6701
6894
|
);
|
|
@@ -6730,7 +6923,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6730
6923
|
sessionRealtimeEntries.id,
|
|
6731
6924
|
ackable.map((entry) => entry.id)
|
|
6732
6925
|
),
|
|
6733
|
-
|
|
6926
|
+
isNull3(sessionRealtimeEntries.providerAckedAt)
|
|
6734
6927
|
)
|
|
6735
6928
|
);
|
|
6736
6929
|
}
|
|
@@ -6740,7 +6933,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6740
6933
|
eq11(sessionRealtimeEntries.sessionId, input.sessionId),
|
|
6741
6934
|
eq11(sessionRealtimeEntries.realtimeId, input.realtimeId),
|
|
6742
6935
|
eq11(sessionRealtimeEntries.direction, "provider_out"),
|
|
6743
|
-
|
|
6936
|
+
isNull3(sessionRealtimeEntries.providerAckedAt)
|
|
6744
6937
|
)
|
|
6745
6938
|
).orderBy(
|
|
6746
6939
|
sql7`case when ${sessionRealtimeEntries.clientAckedAt} is null then 0 else 1 end`,
|
|
@@ -7300,13 +7493,14 @@ function renderMemoryEntry(record) {
|
|
|
7300
7493
|
import { sql as sql15 } from "drizzle-orm";
|
|
7301
7494
|
|
|
7302
7495
|
// src/workspace-instruction-policies.ts
|
|
7303
|
-
import { createHash as createHash8 } from "crypto";
|
|
7496
|
+
import { createHash as createHash8, randomUUID } from "crypto";
|
|
7304
7497
|
import {
|
|
7498
|
+
PreferenceRegistrySnapshot,
|
|
7305
7499
|
ResolvedWorkspaceInstructionPolicySnapshot,
|
|
7306
7500
|
WORKSPACE_INSTRUCTION_POLICY_CONTENT_MAX_CHARS,
|
|
7307
7501
|
WorkspaceInstructionPolicySnapshot
|
|
7308
7502
|
} from "@opengeni/contracts";
|
|
7309
|
-
import { and as and12, asc as asc6, desc as desc4, eq as eq12, inArray as inArray7, isNull as
|
|
7503
|
+
import { and as and12, asc as asc6, desc as desc4, eq as eq12, inArray as inArray7, isNull as isNull4, lt, or as or3, sql as sql8 } from "drizzle-orm";
|
|
7310
7504
|
var WorkspaceInstructionPolicyConflictError = class extends Error {
|
|
7311
7505
|
constructor(currentHead) {
|
|
7312
7506
|
super("The active workspace instruction policy changed in another request");
|
|
@@ -7315,6 +7509,13 @@ var WorkspaceInstructionPolicyConflictError = class extends Error {
|
|
|
7315
7509
|
name = "WorkspaceInstructionPolicyConflictError";
|
|
7316
7510
|
code = "WORKSPACE_INSTRUCTION_POLICY_CONFLICT";
|
|
7317
7511
|
};
|
|
7512
|
+
var WorkspaceInstructionPolicyOperationReuseError = class extends Error {
|
|
7513
|
+
name = "WorkspaceInstructionPolicyOperationReuseError";
|
|
7514
|
+
code = "WORKSPACE_INSTRUCTION_POLICY_OPERATION_REUSED";
|
|
7515
|
+
constructor() {
|
|
7516
|
+
super("The workspace instruction-policy operation id was already used for another request");
|
|
7517
|
+
}
|
|
7518
|
+
};
|
|
7318
7519
|
var WorkspaceInstructionPolicyNotFoundError = class extends Error {
|
|
7319
7520
|
name = "WorkspaceInstructionPolicyNotFoundError";
|
|
7320
7521
|
constructor(message = "Workspace instruction-policy revision was not found") {
|
|
@@ -7336,12 +7537,169 @@ var WorkspaceInstructionPolicySnapshotAuthorityError = class extends Error {
|
|
|
7336
7537
|
function contentHash(content) {
|
|
7337
7538
|
return createHash8("sha256").update(content, "utf8").digest("hex");
|
|
7338
7539
|
}
|
|
7540
|
+
function hasOwnField(value, field) {
|
|
7541
|
+
return Object.prototype.hasOwnProperty.call(value, field);
|
|
7542
|
+
}
|
|
7543
|
+
function operationRequestFingerprint(operation, fields) {
|
|
7544
|
+
const canonicalRequest = JSON.stringify([
|
|
7545
|
+
"workspace_instruction_policy_operation",
|
|
7546
|
+
1,
|
|
7547
|
+
operation,
|
|
7548
|
+
fields
|
|
7549
|
+
]);
|
|
7550
|
+
return createHash8("sha256").update(canonicalRequest, "utf8").digest("hex");
|
|
7551
|
+
}
|
|
7552
|
+
function draftRequestFingerprint(input) {
|
|
7553
|
+
return operationRequestFingerprint("create_draft", [
|
|
7554
|
+
["accountId", true, input.accountId],
|
|
7555
|
+
["workspaceId", true, input.workspaceId],
|
|
7556
|
+
["kind", true, input.kind],
|
|
7557
|
+
["scope", true, input.scope],
|
|
7558
|
+
["roleKey", true, input.roleKey],
|
|
7559
|
+
["content", true, input.content],
|
|
7560
|
+
["provenanceSource", true, input.provenanceSource],
|
|
7561
|
+
["provenanceSourceId", true, input.provenanceSourceId],
|
|
7562
|
+
["supersedesRevisionId", true, input.supersedesRevisionId],
|
|
7563
|
+
["createdBySubjectId", true, input.createdBySubjectId]
|
|
7564
|
+
]);
|
|
7565
|
+
}
|
|
7566
|
+
function importLegacyRequestFingerprint(input) {
|
|
7567
|
+
return operationRequestFingerprint("import_legacy", [
|
|
7568
|
+
["accountId", true, input.accountId],
|
|
7569
|
+
["workspaceId", true, input.workspaceId],
|
|
7570
|
+
["createdBySubjectId", true, input.createdBySubjectId],
|
|
7571
|
+
["supersedesRevisionId", true, input.supersedesRevisionId]
|
|
7572
|
+
]);
|
|
7573
|
+
}
|
|
7574
|
+
function activeRevisionRequestFingerprint(input) {
|
|
7575
|
+
const expectedCurrentRevisionIdPresent = hasOwnField(input, "expectedCurrentRevisionId");
|
|
7576
|
+
const expectedActivationVersionPresent = hasOwnField(input, "expectedActivationVersion");
|
|
7577
|
+
return operationRequestFingerprint(`change_active_revision:${input.type}`, [
|
|
7578
|
+
["accountId", true, input.accountId],
|
|
7579
|
+
["workspaceId", true, input.workspaceId],
|
|
7580
|
+
["targetRevisionId", true, input.targetRevisionId],
|
|
7581
|
+
[
|
|
7582
|
+
"expectedCurrentRevisionId",
|
|
7583
|
+
expectedCurrentRevisionIdPresent,
|
|
7584
|
+
input.expectedCurrentRevisionId ?? null
|
|
7585
|
+
],
|
|
7586
|
+
[
|
|
7587
|
+
"expectedActivationVersion",
|
|
7588
|
+
expectedActivationVersionPresent,
|
|
7589
|
+
input.expectedActivationVersion ?? null
|
|
7590
|
+
],
|
|
7591
|
+
["actorSubjectId", true, input.actorSubjectId],
|
|
7592
|
+
["reason", true, input.reason]
|
|
7593
|
+
]);
|
|
7594
|
+
}
|
|
7339
7595
|
function iso(value) {
|
|
7340
7596
|
return (value instanceof Date ? value : new Date(value)).toISOString();
|
|
7341
7597
|
}
|
|
7598
|
+
async function getWorkspaceStateAcceptedAttemptGovernance(db, input) {
|
|
7599
|
+
return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
|
|
7600
|
+
const initiatingSubject = sql8`coalesce(
|
|
7601
|
+
${sessionTurns.initiatingHumanSubjectId},
|
|
7602
|
+
case when ${sessionTurns.initiatorKind} = 'subject'
|
|
7603
|
+
then ${sessionTurns.initiatorSubjectId}
|
|
7604
|
+
end
|
|
7605
|
+
)`;
|
|
7606
|
+
const [attempt] = await scopedDb.select({
|
|
7607
|
+
attemptId: sessionTurnAttempts.id,
|
|
7608
|
+
executionGeneration: sessionTurnAttempts.executionGeneration,
|
|
7609
|
+
acceptedAt: sessionTurns.createdAt
|
|
7610
|
+
}).from(sessionTurnAttempts).innerJoin(
|
|
7611
|
+
sessionTurns,
|
|
7612
|
+
and12(
|
|
7613
|
+
eq12(sessionTurns.accountId, sessionTurnAttempts.accountId),
|
|
7614
|
+
eq12(sessionTurns.workspaceId, sessionTurnAttempts.workspaceId),
|
|
7615
|
+
eq12(sessionTurns.sessionId, sessionTurnAttempts.sessionId),
|
|
7616
|
+
eq12(sessionTurns.id, sessionTurnAttempts.turnId)
|
|
7617
|
+
)
|
|
7618
|
+
).where(
|
|
7619
|
+
and12(
|
|
7620
|
+
eq12(sessionTurnAttempts.accountId, input.accountId),
|
|
7621
|
+
eq12(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
7622
|
+
eq12(sessionTurnAttempts.id, input.attemptId),
|
|
7623
|
+
eq12(initiatingSubject, input.subjectId)
|
|
7624
|
+
)
|
|
7625
|
+
).limit(1);
|
|
7626
|
+
if (!attempt) return null;
|
|
7627
|
+
const [policyRows, preferenceRows] = await Promise.all([
|
|
7628
|
+
scopedDb.select().from(workspaceInstructionPolicySnapshots).where(
|
|
7629
|
+
and12(
|
|
7630
|
+
eq12(workspaceInstructionPolicySnapshots.accountId, input.accountId),
|
|
7631
|
+
eq12(workspaceInstructionPolicySnapshots.workspaceId, input.workspaceId),
|
|
7632
|
+
eq12(workspaceInstructionPolicySnapshots.attemptId, input.attemptId),
|
|
7633
|
+
eq12(
|
|
7634
|
+
workspaceInstructionPolicySnapshots.executionGeneration,
|
|
7635
|
+
attempt.executionGeneration
|
|
7636
|
+
)
|
|
7637
|
+
)
|
|
7638
|
+
).limit(1),
|
|
7639
|
+
scopedDb.select().from(preferenceRegistrySnapshots).where(
|
|
7640
|
+
and12(
|
|
7641
|
+
eq12(preferenceRegistrySnapshots.accountId, input.accountId),
|
|
7642
|
+
eq12(preferenceRegistrySnapshots.workspaceId, input.workspaceId),
|
|
7643
|
+
eq12(preferenceRegistrySnapshots.attemptId, input.attemptId),
|
|
7644
|
+
eq12(preferenceRegistrySnapshots.executionGeneration, attempt.executionGeneration),
|
|
7645
|
+
eq12(preferenceRegistrySnapshots.initiatingHumanSubjectId, input.subjectId)
|
|
7646
|
+
)
|
|
7647
|
+
).limit(1)
|
|
7648
|
+
]);
|
|
7649
|
+
const policyRow = policyRows[0] ?? null;
|
|
7650
|
+
const preferenceRow = preferenceRows[0] ?? null;
|
|
7651
|
+
return {
|
|
7652
|
+
attemptId: attempt.attemptId,
|
|
7653
|
+
executionGeneration: attempt.executionGeneration,
|
|
7654
|
+
acceptedAt: iso(attempt.acceptedAt),
|
|
7655
|
+
policySnapshot: policyRow ? WorkspaceInstructionPolicySnapshot.parse({
|
|
7656
|
+
id: policyRow.id,
|
|
7657
|
+
workspaceId: policyRow.workspaceId,
|
|
7658
|
+
sessionId: policyRow.sessionId,
|
|
7659
|
+
turnId: policyRow.turnId,
|
|
7660
|
+
attemptId: policyRow.attemptId,
|
|
7661
|
+
executionGeneration: policyRow.executionGeneration,
|
|
7662
|
+
policyRole: policyRow.policyRole,
|
|
7663
|
+
roleSource: policyRow.roleSource,
|
|
7664
|
+
entries: policyRow.entries,
|
|
7665
|
+
entryHash: policyRow.entryHash,
|
|
7666
|
+
createdAt: iso(policyRow.createdAt)
|
|
7667
|
+
}) : null,
|
|
7668
|
+
preferenceSnapshot: preferenceRow ? (() => {
|
|
7669
|
+
const snapshot = PreferenceRegistrySnapshot.parse({
|
|
7670
|
+
id: preferenceRow.id,
|
|
7671
|
+
workspaceId: preferenceRow.workspaceId,
|
|
7672
|
+
sessionId: preferenceRow.sessionId,
|
|
7673
|
+
turnId: preferenceRow.turnId,
|
|
7674
|
+
attemptId: preferenceRow.attemptId,
|
|
7675
|
+
executionGeneration: preferenceRow.executionGeneration,
|
|
7676
|
+
initiatingHumanSubjectId: preferenceRow.initiatingHumanSubjectId,
|
|
7677
|
+
descriptorHash: preferenceRow.descriptorHash,
|
|
7678
|
+
descriptors: preferenceRow.descriptors,
|
|
7679
|
+
truncated: preferenceRow.truncated,
|
|
7680
|
+
createdAt: iso(preferenceRow.createdAt)
|
|
7681
|
+
});
|
|
7682
|
+
return {
|
|
7683
|
+
id: snapshot.id,
|
|
7684
|
+
descriptorHash: snapshot.descriptorHash,
|
|
7685
|
+
descriptors: snapshot.descriptors.map((descriptor) => ({
|
|
7686
|
+
id: descriptor.id,
|
|
7687
|
+
revisionId: descriptor.revisionId,
|
|
7688
|
+
contentHash: descriptor.contentHash,
|
|
7689
|
+
activeVersion: descriptor.activeVersion,
|
|
7690
|
+
scope: descriptor.scope
|
|
7691
|
+
})),
|
|
7692
|
+
truncated: snapshot.truncated,
|
|
7693
|
+
createdAt: snapshot.createdAt
|
|
7694
|
+
};
|
|
7695
|
+
})() : null
|
|
7696
|
+
};
|
|
7697
|
+
});
|
|
7698
|
+
}
|
|
7342
7699
|
function revisionFromRow(row) {
|
|
7343
7700
|
return {
|
|
7344
7701
|
id: row.id,
|
|
7702
|
+
operationId: row.operationId ?? row.id,
|
|
7345
7703
|
accountId: row.accountId,
|
|
7346
7704
|
workspaceId: row.workspaceId,
|
|
7347
7705
|
revision: row.revision,
|
|
@@ -7375,6 +7733,7 @@ function headFromRow(row) {
|
|
|
7375
7733
|
function eventFromRow(row) {
|
|
7376
7734
|
return {
|
|
7377
7735
|
id: row.id,
|
|
7736
|
+
operationId: row.operationId ?? row.id,
|
|
7378
7737
|
accountId: row.accountId,
|
|
7379
7738
|
workspaceId: row.workspaceId,
|
|
7380
7739
|
kind: row.kind,
|
|
@@ -7397,12 +7756,25 @@ function eventFromRow(row) {
|
|
|
7397
7756
|
createdAt: iso(row.createdAt)
|
|
7398
7757
|
};
|
|
7399
7758
|
}
|
|
7759
|
+
function headFromEventRow(row) {
|
|
7760
|
+
return {
|
|
7761
|
+
workspaceId: row.workspaceId,
|
|
7762
|
+
kind: row.kind,
|
|
7763
|
+
scope: row.scope,
|
|
7764
|
+
roleKey: row.roleKey,
|
|
7765
|
+
revisionId: row.newRevisionId,
|
|
7766
|
+
revision: row.newRevision,
|
|
7767
|
+
contentHash: row.newContentHash,
|
|
7768
|
+
activationVersion: row.activationVersion,
|
|
7769
|
+
activatedAt: iso(row.createdAt)
|
|
7770
|
+
};
|
|
7771
|
+
}
|
|
7400
7772
|
function headTargetConditions(workspaceId, target) {
|
|
7401
7773
|
return [
|
|
7402
7774
|
eq12(workspaceInstructionPolicyHeads.workspaceId, workspaceId),
|
|
7403
7775
|
eq12(workspaceInstructionPolicyHeads.kind, target.kind),
|
|
7404
7776
|
eq12(workspaceInstructionPolicyHeads.scope, target.scope),
|
|
7405
|
-
target.roleKey === null ?
|
|
7777
|
+
target.roleKey === null ? isNull4(workspaceInstructionPolicyHeads.roleKey) : eq12(workspaceInstructionPolicyHeads.roleKey, target.roleKey)
|
|
7406
7778
|
];
|
|
7407
7779
|
}
|
|
7408
7780
|
function eventTargetConditions(workspaceId, target) {
|
|
@@ -7410,7 +7782,7 @@ function eventTargetConditions(workspaceId, target) {
|
|
|
7410
7782
|
eq12(workspaceInstructionPolicyActivationEvents.workspaceId, workspaceId),
|
|
7411
7783
|
eq12(workspaceInstructionPolicyActivationEvents.kind, target.kind),
|
|
7412
7784
|
eq12(workspaceInstructionPolicyActivationEvents.scope, target.scope),
|
|
7413
|
-
target.roleKey === null ?
|
|
7785
|
+
target.roleKey === null ? isNull4(workspaceInstructionPolicyActivationEvents.roleKey) : eq12(workspaceInstructionPolicyActivationEvents.roleKey, target.roleKey)
|
|
7414
7786
|
];
|
|
7415
7787
|
}
|
|
7416
7788
|
async function lockWorkspace(db, input) {
|
|
@@ -7434,6 +7806,36 @@ async function getRevisionInTransaction(db, workspaceId, revisionId) {
|
|
|
7434
7806
|
).limit(1);
|
|
7435
7807
|
return row ?? null;
|
|
7436
7808
|
}
|
|
7809
|
+
async function getRevisionByOperationInTransaction(db, workspaceId, operationId) {
|
|
7810
|
+
const [row] = await db.select().from(workspaceInstructionPolicyRevisions).where(
|
|
7811
|
+
and12(
|
|
7812
|
+
eq12(workspaceInstructionPolicyRevisions.workspaceId, workspaceId),
|
|
7813
|
+
or3(
|
|
7814
|
+
eq12(workspaceInstructionPolicyRevisions.operationId, operationId),
|
|
7815
|
+
and12(
|
|
7816
|
+
isNull4(workspaceInstructionPolicyRevisions.operationId),
|
|
7817
|
+
eq12(workspaceInstructionPolicyRevisions.id, operationId)
|
|
7818
|
+
)
|
|
7819
|
+
)
|
|
7820
|
+
)
|
|
7821
|
+
).limit(1);
|
|
7822
|
+
return row ?? null;
|
|
7823
|
+
}
|
|
7824
|
+
async function getEventByOperationInTransaction(db, workspaceId, operationId) {
|
|
7825
|
+
const [row] = await db.select().from(workspaceInstructionPolicyActivationEvents).where(
|
|
7826
|
+
and12(
|
|
7827
|
+
eq12(workspaceInstructionPolicyActivationEvents.workspaceId, workspaceId),
|
|
7828
|
+
or3(
|
|
7829
|
+
eq12(workspaceInstructionPolicyActivationEvents.operationId, operationId),
|
|
7830
|
+
and12(
|
|
7831
|
+
isNull4(workspaceInstructionPolicyActivationEvents.operationId),
|
|
7832
|
+
eq12(workspaceInstructionPolicyActivationEvents.id, operationId)
|
|
7833
|
+
)
|
|
7834
|
+
)
|
|
7835
|
+
)
|
|
7836
|
+
).limit(1);
|
|
7837
|
+
return row ?? null;
|
|
7838
|
+
}
|
|
7437
7839
|
async function getHeadInTransaction(db, workspaceId, target) {
|
|
7438
7840
|
const [row] = await db.select().from(workspaceInstructionPolicyHeads).where(and12(...headTargetConditions(workspaceId, target))).for("update").limit(1);
|
|
7439
7841
|
return row ?? null;
|
|
@@ -7441,7 +7843,24 @@ async function getHeadInTransaction(db, workspaceId, target) {
|
|
|
7441
7843
|
function sameTarget(left, right) {
|
|
7442
7844
|
return left.kind === right.kind && left.scope === right.scope && left.roleKey === right.roleKey;
|
|
7443
7845
|
}
|
|
7846
|
+
function draftReceiptMatches(row, input) {
|
|
7847
|
+
return row.createdBySubjectId === input.createdBySubjectId && row.kind === input.kind && row.scope === input.scope && row.roleKey === input.roleKey && row.content === input.content && row.provenanceSource === input.provenanceSource && row.provenanceSourceId === input.provenanceSourceId && row.supersedesRevisionId === input.supersedesRevisionId;
|
|
7848
|
+
}
|
|
7444
7849
|
async function createDraftInTransaction(db, input) {
|
|
7850
|
+
if (await getEventByOperationInTransaction(db, input.workspaceId, input.operationId)) {
|
|
7851
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
7852
|
+
}
|
|
7853
|
+
const existing = await getRevisionByOperationInTransaction(
|
|
7854
|
+
db,
|
|
7855
|
+
input.workspaceId,
|
|
7856
|
+
input.operationId
|
|
7857
|
+
);
|
|
7858
|
+
if (existing) {
|
|
7859
|
+
if (existing.requestFingerprint === null ? !draftReceiptMatches(existing, input) : existing.requestFingerprint !== input.requestFingerprint) {
|
|
7860
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
7861
|
+
}
|
|
7862
|
+
return revisionFromRow(existing);
|
|
7863
|
+
}
|
|
7445
7864
|
if (input.supersedesRevisionId !== null) {
|
|
7446
7865
|
const superseded = await getRevisionInTransaction(
|
|
7447
7866
|
db,
|
|
@@ -7455,6 +7874,8 @@ async function createDraftInTransaction(db, input) {
|
|
|
7455
7874
|
}
|
|
7456
7875
|
}
|
|
7457
7876
|
const [created] = await db.insert(workspaceInstructionPolicyRevisions).values({
|
|
7877
|
+
operationId: input.operationId,
|
|
7878
|
+
requestFingerprint: input.requestFingerprint,
|
|
7458
7879
|
accountId: input.accountId,
|
|
7459
7880
|
workspaceId: input.workspaceId,
|
|
7460
7881
|
kind: input.kind,
|
|
@@ -7471,21 +7892,42 @@ async function createDraftInTransaction(db, input) {
|
|
|
7471
7892
|
return revisionFromRow(created);
|
|
7472
7893
|
}
|
|
7473
7894
|
async function createWorkspaceInstructionPolicyDraft(db, input) {
|
|
7895
|
+
const request = { ...input, operationId: input.operationId ?? randomUUID() };
|
|
7896
|
+
const normalized = { ...request, requestFingerprint: draftRequestFingerprint(request) };
|
|
7474
7897
|
return await withRlsContext(
|
|
7475
7898
|
db,
|
|
7476
7899
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
7477
7900
|
async (scopedDb) => {
|
|
7478
7901
|
await lockWorkspace(scopedDb, input);
|
|
7479
|
-
return await createDraftInTransaction(scopedDb,
|
|
7902
|
+
return await createDraftInTransaction(scopedDb, normalized);
|
|
7480
7903
|
}
|
|
7481
7904
|
);
|
|
7482
7905
|
}
|
|
7483
7906
|
async function importLegacyWorkspaceInstructionPolicyDraft(db, input) {
|
|
7907
|
+
const request = { ...input, operationId: input.operationId ?? randomUUID() };
|
|
7908
|
+
const normalized = {
|
|
7909
|
+
...request,
|
|
7910
|
+
requestFingerprint: importLegacyRequestFingerprint(request)
|
|
7911
|
+
};
|
|
7484
7912
|
return await withRlsContext(
|
|
7485
7913
|
db,
|
|
7486
7914
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
7487
7915
|
async (scopedDb) => {
|
|
7488
7916
|
const workspace = await lockWorkspace(scopedDb, input);
|
|
7917
|
+
if (await getEventByOperationInTransaction(scopedDb, input.workspaceId, normalized.operationId)) {
|
|
7918
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
7919
|
+
}
|
|
7920
|
+
const existing = await getRevisionByOperationInTransaction(
|
|
7921
|
+
scopedDb,
|
|
7922
|
+
input.workspaceId,
|
|
7923
|
+
normalized.operationId
|
|
7924
|
+
);
|
|
7925
|
+
if (existing) {
|
|
7926
|
+
if (existing.requestFingerprint === null ? existing.createdBySubjectId !== input.createdBySubjectId || existing.kind !== "charter" || existing.scope !== "global" || existing.roleKey !== null || existing.provenanceSource !== "legacy_import" || existing.provenanceSourceId !== "workspaces.agent_instructions" || existing.supersedesRevisionId !== input.supersedesRevisionId : existing.requestFingerprint !== normalized.requestFingerprint) {
|
|
7927
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
7928
|
+
}
|
|
7929
|
+
return revisionFromRow(existing);
|
|
7930
|
+
}
|
|
7489
7931
|
if (workspace.agentInstructions === null) {
|
|
7490
7932
|
throw new WorkspaceInstructionPolicyLegacyUnavailableError();
|
|
7491
7933
|
}
|
|
@@ -7495,7 +7937,7 @@ async function importLegacyWorkspaceInstructionPolicyDraft(db, input) {
|
|
|
7495
7937
|
);
|
|
7496
7938
|
}
|
|
7497
7939
|
return await createDraftInTransaction(scopedDb, {
|
|
7498
|
-
...
|
|
7940
|
+
...normalized,
|
|
7499
7941
|
kind: "charter",
|
|
7500
7942
|
scope: "global",
|
|
7501
7943
|
roleKey: null,
|
|
@@ -7518,7 +7960,7 @@ function listFilterConditions(workspaceId, query) {
|
|
|
7518
7960
|
}
|
|
7519
7961
|
if (query.roleKey !== void 0) {
|
|
7520
7962
|
conditions.push(
|
|
7521
|
-
query.roleKey === null ?
|
|
7963
|
+
query.roleKey === null ? isNull4(workspaceInstructionPolicyRevisions.roleKey) : eq12(workspaceInstructionPolicyRevisions.roleKey, query.roleKey)
|
|
7522
7964
|
);
|
|
7523
7965
|
}
|
|
7524
7966
|
if (query.afterRevision !== void 0) {
|
|
@@ -7619,11 +8061,27 @@ async function diffWorkspaceInstructionPolicyRevisions(db, workspaceId, input) {
|
|
|
7619
8061
|
});
|
|
7620
8062
|
}
|
|
7621
8063
|
async function changeActiveRevision(db, input) {
|
|
8064
|
+
const requestFingerprint = activeRevisionRequestFingerprint(input);
|
|
7622
8065
|
return await withRlsContext(
|
|
7623
8066
|
db,
|
|
7624
8067
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
7625
8068
|
async (scopedDb) => {
|
|
7626
8069
|
await lockWorkspace(scopedDb, input);
|
|
8070
|
+
if (await getRevisionByOperationInTransaction(scopedDb, input.workspaceId, input.operationId)) {
|
|
8071
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8072
|
+
}
|
|
8073
|
+
const existingEvent = await getEventByOperationInTransaction(
|
|
8074
|
+
scopedDb,
|
|
8075
|
+
input.workspaceId,
|
|
8076
|
+
input.operationId
|
|
8077
|
+
);
|
|
8078
|
+
if (existingEvent) {
|
|
8079
|
+
const legacyRequestMatches = hasOwnField(input, "expectedCurrentRevisionId") && !hasOwnField(input, "expectedActivationVersion") && existingEvent.type === input.type && existingEvent.newRevisionId === input.targetRevisionId && existingEvent.oldRevisionId === input.expectedCurrentRevisionId && existingEvent.actorSubjectId === input.actorSubjectId && existingEvent.reason === input.reason;
|
|
8080
|
+
if (existingEvent.requestFingerprint === null ? !legacyRequestMatches : existingEvent.requestFingerprint !== requestFingerprint) {
|
|
8081
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8082
|
+
}
|
|
8083
|
+
return { head: headFromEventRow(existingEvent), event: eventFromRow(existingEvent) };
|
|
8084
|
+
}
|
|
7627
8085
|
const targetRow = await getRevisionInTransaction(
|
|
7628
8086
|
scopedDb,
|
|
7629
8087
|
input.workspaceId,
|
|
@@ -7638,7 +8096,7 @@ async function changeActiveRevision(db, input) {
|
|
|
7638
8096
|
};
|
|
7639
8097
|
const currentRow = await getHeadInTransaction(scopedDb, input.workspaceId, targetIdentity);
|
|
7640
8098
|
const currentHead = currentRow ? headFromRow(currentRow) : null;
|
|
7641
|
-
if ((currentHead?.revisionId ?? null) !== input.expectedCurrentRevisionId) {
|
|
8099
|
+
if ((currentHead?.revisionId ?? null) !== input.expectedCurrentRevisionId || input.expectedActivationVersion !== void 0 && (currentHead?.activationVersion ?? 0) !== input.expectedActivationVersion) {
|
|
7642
8100
|
throw new WorkspaceInstructionPolicyConflictError(currentHead);
|
|
7643
8101
|
}
|
|
7644
8102
|
if (currentHead?.revisionId === target.id) {
|
|
@@ -7667,6 +8125,8 @@ async function changeActiveRevision(db, input) {
|
|
|
7667
8125
|
const activationVersion = (currentHead?.activationVersion ?? 0) + 1;
|
|
7668
8126
|
const createdAt = /* @__PURE__ */ new Date();
|
|
7669
8127
|
const [eventRow] = await scopedDb.insert(workspaceInstructionPolicyActivationEvents).values({
|
|
8128
|
+
operationId: input.operationId,
|
|
8129
|
+
requestFingerprint,
|
|
7670
8130
|
accountId: input.accountId,
|
|
7671
8131
|
workspaceId: input.workspaceId,
|
|
7672
8132
|
...targetIdentity,
|
|
@@ -7712,12 +8172,17 @@ async function changeActiveRevision(db, input) {
|
|
|
7712
8172
|
async function activateWorkspaceInstructionPolicyRevision(db, input) {
|
|
7713
8173
|
return await changeActiveRevision(db, {
|
|
7714
8174
|
...input,
|
|
8175
|
+
operationId: input.operationId ?? randomUUID(),
|
|
7715
8176
|
targetRevisionId: input.revisionId,
|
|
7716
8177
|
type: "activate"
|
|
7717
8178
|
});
|
|
7718
8179
|
}
|
|
7719
8180
|
async function rollbackWorkspaceInstructionPolicyRevision(db, input) {
|
|
7720
|
-
return await changeActiveRevision(db, {
|
|
8181
|
+
return await changeActiveRevision(db, {
|
|
8182
|
+
...input,
|
|
8183
|
+
operationId: input.operationId ?? randomUUID(),
|
|
8184
|
+
type: "rollback"
|
|
8185
|
+
});
|
|
7721
8186
|
}
|
|
7722
8187
|
async function getOrCreateWorkspaceInstructionPolicySnapshot(db, claims) {
|
|
7723
8188
|
try {
|
|
@@ -7800,16 +8265,17 @@ import {
|
|
|
7800
8265
|
PREFERENCE_REGISTRY_DESCRIPTOR_MAX_COUNT,
|
|
7801
8266
|
PREFERENCE_REGISTRY_DESCRIPTOR_MAX_UTF8_BYTES,
|
|
7802
8267
|
PREFERENCE_REGISTRY_TITLE_MAX_CHARS,
|
|
8268
|
+
PreferenceRegistryDescriptor,
|
|
7803
8269
|
PreferenceRegistryDetailResponse,
|
|
7804
8270
|
PreferenceRegistryEvent,
|
|
7805
8271
|
PreferenceRegistryFullContent,
|
|
7806
8272
|
PreferenceRegistryListResponse,
|
|
7807
8273
|
PreferenceRegistryRecord,
|
|
7808
8274
|
PreferenceRegistryRevisionSummary,
|
|
7809
|
-
PreferenceRegistrySnapshot,
|
|
8275
|
+
PreferenceRegistrySnapshot as PreferenceRegistrySnapshot2,
|
|
7810
8276
|
PreferenceRegistryScopeTarget
|
|
7811
8277
|
} from "@opengeni/contracts";
|
|
7812
|
-
import { and as and13, asc as asc7, desc as desc5, eq as eq13, gt as
|
|
8278
|
+
import { and as and13, asc as asc7, desc as desc5, eq as eq13, gt as gt4, inArray as inArray8, isNull as isNull5, lte, or as or4, sql as sql9 } from "drizzle-orm";
|
|
7813
8279
|
var importedSources = /* @__PURE__ */ new Set([
|
|
7814
8280
|
"knowledge_proposal",
|
|
7815
8281
|
"imported_document",
|
|
@@ -8120,9 +8586,9 @@ async function listPreferenceRegistryScoped(db, input) {
|
|
|
8120
8586
|
if (input.status === "active") {
|
|
8121
8587
|
conditions.push(
|
|
8122
8588
|
eq13(preferenceRegistryPreferences.status, "active"),
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8589
|
+
or4(
|
|
8590
|
+
isNull5(preferenceRegistryRevisions.expiresAt),
|
|
8591
|
+
gt4(preferenceRegistryRevisions.expiresAt, now)
|
|
8126
8592
|
)
|
|
8127
8593
|
);
|
|
8128
8594
|
} else if (input.status === "expired") {
|
|
@@ -8162,6 +8628,81 @@ async function listPreferenceRegistry(db, input) {
|
|
|
8162
8628
|
return await listPreferenceRegistryScoped(scopedDb, input);
|
|
8163
8629
|
});
|
|
8164
8630
|
}
|
|
8631
|
+
async function getCurrentPreferenceRegistryGovernanceMetadata(db, input) {
|
|
8632
|
+
return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
|
|
8633
|
+
const rows = await scopedDb.select({
|
|
8634
|
+
preference: preferenceRegistryPreferences,
|
|
8635
|
+
revision: preferenceRegistryRevisions
|
|
8636
|
+
}).from(preferenceRegistryPreferences).innerJoin(
|
|
8637
|
+
preferenceRegistryRevisions,
|
|
8638
|
+
and13(
|
|
8639
|
+
eq13(
|
|
8640
|
+
preferenceRegistryRevisions.id,
|
|
8641
|
+
preferenceRegistryPreferences.activeRevisionId
|
|
8642
|
+
),
|
|
8643
|
+
eq13(
|
|
8644
|
+
preferenceRegistryRevisions.accountId,
|
|
8645
|
+
preferenceRegistryPreferences.accountId
|
|
8646
|
+
)
|
|
8647
|
+
)
|
|
8648
|
+
).where(
|
|
8649
|
+
and13(
|
|
8650
|
+
eq13(preferenceRegistryPreferences.status, "active"),
|
|
8651
|
+
or4(
|
|
8652
|
+
isNull5(preferenceRegistryRevisions.expiresAt),
|
|
8653
|
+
gt4(preferenceRegistryRevisions.expiresAt, sql9`transaction_timestamp()`)
|
|
8654
|
+
)
|
|
8655
|
+
)
|
|
8656
|
+
).orderBy(
|
|
8657
|
+
sql9`case ${preferenceRegistryPreferences.scope}
|
|
8658
|
+
when 'organization' then 0
|
|
8659
|
+
when 'workspace' then 1
|
|
8660
|
+
when 'user' then 2
|
|
8661
|
+
else 3
|
|
8662
|
+
end`,
|
|
8663
|
+
desc5(preferenceRegistryRevisions.precedenceRank),
|
|
8664
|
+
asc7(preferenceRegistryPreferences.stableKey),
|
|
8665
|
+
asc7(preferenceRegistryPreferences.id)
|
|
8666
|
+
).limit(PREFERENCE_REGISTRY_DESCRIPTOR_MAX_COUNT + 1);
|
|
8667
|
+
const bounded = boundPreferenceRegistryDescriptors(
|
|
8668
|
+
rows.map(
|
|
8669
|
+
({ preference, revision }) => PreferenceRegistryDescriptor.parse({
|
|
8670
|
+
id: preference.id,
|
|
8671
|
+
stableKey: preference.stableKey,
|
|
8672
|
+
title: revision.title,
|
|
8673
|
+
description: revision.description,
|
|
8674
|
+
scope: preference.scope,
|
|
8675
|
+
activeVersion: preference.activationVersion,
|
|
8676
|
+
revisionId: revision.id,
|
|
8677
|
+
contentHash: revision.contentHash,
|
|
8678
|
+
precedence: {
|
|
8679
|
+
tier: preference.scope,
|
|
8680
|
+
rank: revision.precedenceRank,
|
|
8681
|
+
conflictStrategy: revision.conflictStrategy,
|
|
8682
|
+
conflictsWith: revision.conflictsWith
|
|
8683
|
+
},
|
|
8684
|
+
provenance: {
|
|
8685
|
+
source: revision.provenanceSource,
|
|
8686
|
+
sourceIdHash: revision.provenanceSourceId ? contentHash2(revision.provenanceSourceId) : null,
|
|
8687
|
+
trust: revision.trust
|
|
8688
|
+
},
|
|
8689
|
+
expiresAt: revision.expiresAt ? iso2(revision.expiresAt) : null,
|
|
8690
|
+
retrievalHandle: `preference://${preference.id}/revisions/${revision.id}?sha256=${revision.contentHash}`
|
|
8691
|
+
})
|
|
8692
|
+
)
|
|
8693
|
+
);
|
|
8694
|
+
return {
|
|
8695
|
+
descriptors: bounded.descriptors.map((descriptor) => ({
|
|
8696
|
+
id: descriptor.id,
|
|
8697
|
+
revisionId: descriptor.revisionId,
|
|
8698
|
+
contentHash: descriptor.contentHash,
|
|
8699
|
+
activeVersion: descriptor.activeVersion,
|
|
8700
|
+
scope: descriptor.scope
|
|
8701
|
+
})),
|
|
8702
|
+
truncated: bounded.truncated
|
|
8703
|
+
};
|
|
8704
|
+
});
|
|
8705
|
+
}
|
|
8165
8706
|
async function listPreferenceRegistryForAttempt(db, input) {
|
|
8166
8707
|
return await withPreferenceRegistryAttemptAuthority(db, input, async (scopedDb) => {
|
|
8167
8708
|
return await listPreferenceRegistryScoped(scopedDb, input);
|
|
@@ -8382,9 +8923,9 @@ async function supersedePreferenceRegistry(db, input) {
|
|
|
8382
8923
|
and13(
|
|
8383
8924
|
eq13(preferenceRegistryRevisions.id, replacement.activeRevisionId),
|
|
8384
8925
|
eq13(preferenceRegistryRevisions.preferenceId, replacement.id),
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8926
|
+
or4(
|
|
8927
|
+
isNull5(preferenceRegistryRevisions.expiresAt),
|
|
8928
|
+
gt4(preferenceRegistryRevisions.expiresAt, sql9`transaction_timestamp()`)
|
|
8388
8929
|
)
|
|
8389
8930
|
)
|
|
8390
8931
|
).limit(1);
|
|
@@ -8586,7 +9127,7 @@ async function getOrCreatePreferenceRegistrySnapshot(db, claims) {
|
|
|
8586
9127
|
"Preference snapshot authority conflicts with the accepted human attempt"
|
|
8587
9128
|
);
|
|
8588
9129
|
}
|
|
8589
|
-
return
|
|
9130
|
+
return PreferenceRegistrySnapshot2.parse({
|
|
8590
9131
|
...snapshot,
|
|
8591
9132
|
createdAt: iso2(snapshot.createdAt)
|
|
8592
9133
|
});
|
|
@@ -11970,7 +12511,7 @@ var defaultDeps2 = {
|
|
|
11970
12511
|
var inflight2 = /* @__PURE__ */ new Map();
|
|
11971
12512
|
var REFRESH_WINDOW_MS = 6e4;
|
|
11972
12513
|
var CONNECTION_REFRESH_TIMEOUT_MS = 1e4;
|
|
11973
|
-
function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
12514
|
+
function buildConnectionTokenResolver(db, settings, deps = defaultDeps2, options = {}) {
|
|
11974
12515
|
const load = async (input) => {
|
|
11975
12516
|
const subjectOwned = input.connectionRef.subjectScope === "subject";
|
|
11976
12517
|
if (subjectOwned && !input.subjectId) {
|
|
@@ -12033,6 +12574,7 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
12033
12574
|
status: "ok",
|
|
12034
12575
|
headers,
|
|
12035
12576
|
connectionId: cred.id,
|
|
12577
|
+
connectionVersion: cred.version,
|
|
12036
12578
|
expiresAt: cred.expiresAt
|
|
12037
12579
|
};
|
|
12038
12580
|
};
|
|
@@ -12041,7 +12583,7 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
12041
12583
|
if (!key) {
|
|
12042
12584
|
throw new Error("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is not configured");
|
|
12043
12585
|
}
|
|
12044
|
-
const refreshed = await deps.refresh(cred, ref, settings);
|
|
12586
|
+
const refreshed = await deps.refresh(cred, ref, settings, options.refreshTransport);
|
|
12045
12587
|
const refreshRecord = {
|
|
12046
12588
|
id: cred.id,
|
|
12047
12589
|
version: cred.version,
|
|
@@ -12117,17 +12659,29 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
12117
12659
|
cred = await refreshSingleFlight(cred, ref);
|
|
12118
12660
|
} catch (error) {
|
|
12119
12661
|
if (isPermanentRefreshError(error)) {
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12662
|
+
let handled = false;
|
|
12663
|
+
if (options.transitionPermanentRefreshFailure) {
|
|
12664
|
+
try {
|
|
12665
|
+
handled = await options.transitionPermanentRefreshFailure({
|
|
12666
|
+
workspaceId: cred.workspaceId,
|
|
12667
|
+
connectionId: cred.id,
|
|
12668
|
+
connectionVersion: cred.version,
|
|
12669
|
+
subjectId: cred.subjectId,
|
|
12670
|
+
providerDomain: cred.providerDomain,
|
|
12671
|
+
httpStatus: error.httpStatus,
|
|
12672
|
+
oauthErrorCode: error.oauthErrorCode
|
|
12673
|
+
});
|
|
12674
|
+
} catch {
|
|
12675
|
+
handled = false;
|
|
12676
|
+
}
|
|
12677
|
+
}
|
|
12678
|
+
if (!handled) {
|
|
12679
|
+
await deps.setStatus(db, input.workspaceId, "needs_reauth", error.message, {
|
|
12126
12680
|
id: cred.id,
|
|
12127
12681
|
version: cred.version,
|
|
12128
12682
|
subjectId: cred.subjectId
|
|
12129
|
-
}
|
|
12130
|
-
|
|
12683
|
+
}).catch(() => void 0);
|
|
12684
|
+
}
|
|
12131
12685
|
}
|
|
12132
12686
|
return authNeeded(ref, "refresh_failed", cred.id);
|
|
12133
12687
|
}
|
|
@@ -12182,10 +12736,12 @@ function canonicalResource(value) {
|
|
|
12182
12736
|
}
|
|
12183
12737
|
var ConnectionRefreshHttpError = class extends Error {
|
|
12184
12738
|
httpStatus;
|
|
12185
|
-
|
|
12739
|
+
oauthErrorCode;
|
|
12740
|
+
constructor(httpStatus, oauthErrorCode = null) {
|
|
12186
12741
|
super(`connection refresh failed with HTTP ${httpStatus}`);
|
|
12187
12742
|
this.name = "ConnectionRefreshHttpError";
|
|
12188
12743
|
this.httpStatus = httpStatus;
|
|
12744
|
+
this.oauthErrorCode = oauthErrorCode;
|
|
12189
12745
|
}
|
|
12190
12746
|
};
|
|
12191
12747
|
function isPermanentRefreshError(error) {
|
|
@@ -12317,8 +12873,10 @@ async function refreshOAuthConnectionCredential(cred, ref, settings, transportOp
|
|
|
12317
12873
|
throw new ConnectionRefreshHttpError(response.status);
|
|
12318
12874
|
}
|
|
12319
12875
|
if (!response.ok) {
|
|
12320
|
-
|
|
12321
|
-
|
|
12876
|
+
throw new ConnectionRefreshHttpError(
|
|
12877
|
+
response.status,
|
|
12878
|
+
await readOAuthRefreshErrorCode(response)
|
|
12879
|
+
);
|
|
12322
12880
|
}
|
|
12323
12881
|
const payload = await readResponseJsonBounded(
|
|
12324
12882
|
response,
|
|
@@ -12347,6 +12905,20 @@ async function refreshOAuthConnectionCredential(cred, ref, settings, transportOp
|
|
|
12347
12905
|
...scopeText ? { grantedScopes: scopeText.split(/\s+/).filter(Boolean) } : {}
|
|
12348
12906
|
};
|
|
12349
12907
|
}
|
|
12908
|
+
async function readOAuthRefreshErrorCode(response) {
|
|
12909
|
+
try {
|
|
12910
|
+
const payload = await readResponseJsonBounded(
|
|
12911
|
+
response,
|
|
12912
|
+
OAUTH_MAX_RESPONSE_BYTES,
|
|
12913
|
+
"OAuth refresh error response"
|
|
12914
|
+
);
|
|
12915
|
+
const code = stringValue(payload.error);
|
|
12916
|
+
return code && /^[a-z0-9_.-]{1,64}$/i.test(code) ? code : null;
|
|
12917
|
+
} catch {
|
|
12918
|
+
await cancelResponseBody(response);
|
|
12919
|
+
return null;
|
|
12920
|
+
}
|
|
12921
|
+
}
|
|
12350
12922
|
function expiresAtFromTokenResponse(payload, fallback) {
|
|
12351
12923
|
const expiresAt = stringValue(payload.expires_at);
|
|
12352
12924
|
if (expiresAt) {
|
|
@@ -12380,7 +12952,7 @@ async function cancelResponseBody(response) {
|
|
|
12380
12952
|
}
|
|
12381
12953
|
|
|
12382
12954
|
// src/workspace-artifacts.ts
|
|
12383
|
-
import { and as and16, desc as desc7, eq as eq16, lt as lt3, or as
|
|
12955
|
+
import { and as and16, desc as desc7, eq as eq16, lt as lt3, or as or5, sql as sql13 } from "drizzle-orm";
|
|
12384
12956
|
var WorkspaceArtifactNotFoundError = class extends Error {
|
|
12385
12957
|
name = "WorkspaceArtifactNotFoundError";
|
|
12386
12958
|
};
|
|
@@ -12475,7 +13047,7 @@ async function listWorkspaceArtifacts(db, workspaceId, options = {}) {
|
|
|
12475
13047
|
const cursor = options.cursor ? decodeListCursor(options.cursor) : null;
|
|
12476
13048
|
const visibility = cursor ? and16(
|
|
12477
13049
|
eq16(workspaceArtifacts.workspaceId, workspaceId),
|
|
12478
|
-
|
|
13050
|
+
or5(
|
|
12479
13051
|
lt3(workspaceArtifacts.updatedAt, cursor.updatedAt),
|
|
12480
13052
|
and16(
|
|
12481
13053
|
eq16(workspaceArtifacts.updatedAt, cursor.updatedAt),
|
|
@@ -13345,6 +13917,29 @@ async function withWorkspaceUsageLock(db, workspaceId, fn) {
|
|
|
13345
13917
|
return await fn(scopedDb);
|
|
13346
13918
|
});
|
|
13347
13919
|
}
|
|
13920
|
+
async function resolveDocumentIndexAuthority(db, input) {
|
|
13921
|
+
const rows = await rawRows(
|
|
13922
|
+
db,
|
|
13923
|
+
sql14`
|
|
13924
|
+
select authority_kind, authority_workspace_id, authority_subject_id
|
|
13925
|
+
from opengeni_private.resolve_document_index_authority(
|
|
13926
|
+
${input.accountId},
|
|
13927
|
+
${input.workspaceId},
|
|
13928
|
+
${input.documentId}
|
|
13929
|
+
)
|
|
13930
|
+
`
|
|
13931
|
+
);
|
|
13932
|
+
const row = rows[0];
|
|
13933
|
+
if (!row) return null;
|
|
13934
|
+
if (row.authority_kind !== "organization" && row.authority_kind !== "workspace" && row.authority_kind !== "personal") {
|
|
13935
|
+
throw new Error(`Stored document authority kind is invalid: ${row.authority_kind}`);
|
|
13936
|
+
}
|
|
13937
|
+
return {
|
|
13938
|
+
authorityKind: row.authority_kind,
|
|
13939
|
+
authorityWorkspaceId: row.authority_workspace_id,
|
|
13940
|
+
authoritySubjectId: row.authority_subject_id
|
|
13941
|
+
};
|
|
13942
|
+
}
|
|
13348
13943
|
async function withAccountRls(db, accountId, fn) {
|
|
13349
13944
|
return await withRlsContext(db, { accountId, workspaceId: null }, fn);
|
|
13350
13945
|
}
|
|
@@ -13967,7 +14562,7 @@ async function upsertGitHubInstallation(db, input) {
|
|
|
13967
14562
|
},
|
|
13968
14563
|
// This legacy metadata helper cannot mutate an owner-authorized row.
|
|
13969
14564
|
// New bindings use bindAuthorizedGitHubInstallationRepositories.
|
|
13970
|
-
setWhere:
|
|
14565
|
+
setWhere: isNull6(githubInstallations.authorityNonce)
|
|
13971
14566
|
}).returning();
|
|
13972
14567
|
if (!row) {
|
|
13973
14568
|
throw new Error("Failed to upsert GitHub installation");
|
|
@@ -14010,7 +14605,7 @@ async function bindGitHubInstallationRepositories(db, input) {
|
|
|
14010
14605
|
updatedAt: /* @__PURE__ */ new Date()
|
|
14011
14606
|
},
|
|
14012
14607
|
// Preserve the immutable authority + allowlist audit boundary.
|
|
14013
|
-
setWhere:
|
|
14608
|
+
setWhere: isNull6(githubInstallations.authorityNonce)
|
|
14014
14609
|
}).returning();
|
|
14015
14610
|
if (!row) {
|
|
14016
14611
|
throw new Error("Failed to bind GitHub installation");
|
|
@@ -14463,7 +15058,7 @@ async function sumUsageQuantity(db, input) {
|
|
|
14463
15058
|
eq17(usageEvents.eventType, input.eventType),
|
|
14464
15059
|
...input.accountId ? [eq17(usageEvents.accountId, input.accountId)] : [],
|
|
14465
15060
|
...input.workspaceId ? [eq17(usageEvents.workspaceId, input.workspaceId)] : [],
|
|
14466
|
-
...input.since ? [
|
|
15061
|
+
...input.since ? [gt5(usageEvents.occurredAt, input.since)] : []
|
|
14467
15062
|
];
|
|
14468
15063
|
const [{ total } = { total: 0 }] = await scopedDb.select({
|
|
14469
15064
|
total: sql14`coalesce(sum(${usageEvents.quantity}), 0)`
|
|
@@ -14619,6 +15214,20 @@ async function countScheduledTasksForWorkspace(db, workspaceId) {
|
|
|
14619
15214
|
return Number(count);
|
|
14620
15215
|
});
|
|
14621
15216
|
}
|
|
15217
|
+
var ConnectionDisconnectIdempotencyError = class extends Error {
|
|
15218
|
+
code = "IDEMPOTENCY_KEY_REUSED";
|
|
15219
|
+
constructor() {
|
|
15220
|
+
super("The disconnect idempotency key was already used with different input");
|
|
15221
|
+
this.name = "ConnectionDisconnectIdempotencyError";
|
|
15222
|
+
}
|
|
15223
|
+
};
|
|
15224
|
+
var ConnectionDisconnectGenerationError = class extends Error {
|
|
15225
|
+
code = "CONNECTION_GENERATION_CHANGED";
|
|
15226
|
+
constructor() {
|
|
15227
|
+
super("The disconnect operation belongs to an older connection generation");
|
|
15228
|
+
this.name = "ConnectionDisconnectGenerationError";
|
|
15229
|
+
}
|
|
15230
|
+
};
|
|
14622
15231
|
async function createFileUpload(db, input) {
|
|
14623
15232
|
return await withRlsContext(
|
|
14624
15233
|
db,
|
|
@@ -14674,6 +15283,25 @@ async function requireFile(db, workspaceId, fileId) {
|
|
|
14674
15283
|
}
|
|
14675
15284
|
return file;
|
|
14676
15285
|
}
|
|
15286
|
+
async function getFiles(db, workspaceId, fileIds) {
|
|
15287
|
+
const ids = [...new Set(fileIds)];
|
|
15288
|
+
if (ids.length === 0) return [];
|
|
15289
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
15290
|
+
const rows = await scopedDb.select().from(files).where(and17(eq17(files.workspaceId, workspaceId), inArray11(files.id, ids)));
|
|
15291
|
+
return rows.map(mapFile);
|
|
15292
|
+
});
|
|
15293
|
+
}
|
|
15294
|
+
function durableUserHistoryItem(prompt, resources) {
|
|
15295
|
+
const attachmentRefs = resources.filter(
|
|
15296
|
+
(resource) => resource.kind === "file"
|
|
15297
|
+
);
|
|
15298
|
+
return sanitizeModelPayload({
|
|
15299
|
+
type: "message",
|
|
15300
|
+
role: "user",
|
|
15301
|
+
content: prompt,
|
|
15302
|
+
...attachmentRefs.length > 0 ? { [MODEL_ATTACHMENT_REFS_FIELD]: attachmentRefs } : {}
|
|
15303
|
+
});
|
|
15304
|
+
}
|
|
14677
15305
|
async function getRetainedFileArtifact(db, workspaceId, fileId) {
|
|
14678
15306
|
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
14679
15307
|
const [row] = await scopedDb.select({
|
|
@@ -15251,11 +15879,11 @@ async function upsertCapabilityCatalogItem(db, input) {
|
|
|
15251
15879
|
async function listCapabilityCatalogItems(db, workspaceId) {
|
|
15252
15880
|
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
15253
15881
|
const rows = await scopedDb.select().from(capabilityCatalogItems).where(
|
|
15254
|
-
|
|
15882
|
+
or6(
|
|
15255
15883
|
eq17(capabilityCatalogItems.workspaceId, workspaceId),
|
|
15256
15884
|
and17(
|
|
15257
|
-
|
|
15258
|
-
|
|
15885
|
+
isNull6(capabilityCatalogItems.workspaceId),
|
|
15886
|
+
or6(
|
|
15259
15887
|
ne(capabilityCatalogItems.source, registryCapabilitySource),
|
|
15260
15888
|
eq17(capabilityCatalogItems.stale, false)
|
|
15261
15889
|
)
|
|
@@ -15277,9 +15905,9 @@ async function getCapabilityCatalogItem(db, workspaceId, capabilityId) {
|
|
|
15277
15905
|
const [row] = await scopedDb.select().from(capabilityCatalogItems).where(
|
|
15278
15906
|
and17(
|
|
15279
15907
|
eq17(capabilityCatalogItems.id, capabilityId),
|
|
15280
|
-
|
|
15908
|
+
or6(
|
|
15281
15909
|
eq17(capabilityCatalogItems.workspaceId, workspaceId),
|
|
15282
|
-
|
|
15910
|
+
isNull6(capabilityCatalogItems.workspaceId)
|
|
15283
15911
|
)
|
|
15284
15912
|
)
|
|
15285
15913
|
).orderBy(asc8(sql14`(${capabilityCatalogItems.workspaceId} is null)`)).limit(1);
|
|
@@ -15387,12 +16015,12 @@ async function listEnabledMcpCapabilityServers(db, workspaceId) {
|
|
|
15387
16015
|
}).from(capabilityInstallations).innerJoin(
|
|
15388
16016
|
capabilityCatalogItems,
|
|
15389
16017
|
and17(
|
|
15390
|
-
|
|
16018
|
+
or6(
|
|
15391
16019
|
eq17(
|
|
15392
16020
|
capabilityInstallations.workspaceId,
|
|
15393
16021
|
capabilityCatalogItems.workspaceId
|
|
15394
16022
|
),
|
|
15395
|
-
|
|
16023
|
+
isNull6(capabilityCatalogItems.workspaceId)
|
|
15396
16024
|
),
|
|
15397
16025
|
eq17(capabilityInstallations.capabilityId, capabilityCatalogItems.id)
|
|
15398
16026
|
)
|
|
@@ -15501,10 +16129,10 @@ var connectionMetadataColumns = {
|
|
|
15501
16129
|
updatedAt: connections.updatedAt
|
|
15502
16130
|
};
|
|
15503
16131
|
function connectionSubjectVisibility(subjectId) {
|
|
15504
|
-
return subjectId ?
|
|
16132
|
+
return subjectId ? or6(isNull6(connections.subjectId), eq17(connections.subjectId, subjectId)) : isNull6(connections.subjectId);
|
|
15505
16133
|
}
|
|
15506
16134
|
function connectionExactSubject(subjectId) {
|
|
15507
|
-
return subjectId ? eq17(connections.subjectId, subjectId) :
|
|
16135
|
+
return subjectId ? eq17(connections.subjectId, subjectId) : isNull6(connections.subjectId);
|
|
15508
16136
|
}
|
|
15509
16137
|
function personalSlackCanonicalConnectionOrder() {
|
|
15510
16138
|
return [
|
|
@@ -15617,6 +16245,124 @@ async function updateConnection(db, input) {
|
|
|
15617
16245
|
async (scopedDb) => await updateConnectionInScope(scopedDb, input)
|
|
15618
16246
|
);
|
|
15619
16247
|
}
|
|
16248
|
+
async function transitionConnectionStateInScope(db, input) {
|
|
16249
|
+
const [row] = await db.update(connections).set({
|
|
16250
|
+
...input.status !== void 0 ? { status: input.status } : {},
|
|
16251
|
+
metadata: input.metadata,
|
|
16252
|
+
...input.lastError !== void 0 ? { lastError: input.lastError } : {},
|
|
16253
|
+
version: sql14`${connections.version} + 1`,
|
|
16254
|
+
...input.updatedBySubjectId !== void 0 ? { updatedBySubjectId: input.updatedBySubjectId } : {},
|
|
16255
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
16256
|
+
}).where(
|
|
16257
|
+
and17(
|
|
16258
|
+
eq17(connections.workspaceId, input.workspaceId),
|
|
16259
|
+
eq17(connections.id, input.connectionId),
|
|
16260
|
+
connectionSubjectVisibility(input.visibleToSubjectId),
|
|
16261
|
+
eq17(connections.version, input.expectedVersion)
|
|
16262
|
+
)
|
|
16263
|
+
).returning(connectionMetadataColumns);
|
|
16264
|
+
return row ? mapConnectionMetadata(row) : null;
|
|
16265
|
+
}
|
|
16266
|
+
async function transitionConnectionState(db, input) {
|
|
16267
|
+
return await withConnectionSubjectRls(
|
|
16268
|
+
db,
|
|
16269
|
+
input.workspaceId,
|
|
16270
|
+
input.visibleToSubjectId,
|
|
16271
|
+
async (scopedDb) => await transitionConnectionStateInScope(scopedDb, input)
|
|
16272
|
+
);
|
|
16273
|
+
}
|
|
16274
|
+
var connectionDisconnectOperationColumns = {
|
|
16275
|
+
accountId: connectionDisconnectOperations.accountId,
|
|
16276
|
+
workspaceId: connectionDisconnectOperations.workspaceId,
|
|
16277
|
+
connectionId: connectionDisconnectOperations.connectionId,
|
|
16278
|
+
subjectId: connectionDisconnectOperations.subjectId,
|
|
16279
|
+
idempotencyKey: connectionDisconnectOperations.idempotencyKey,
|
|
16280
|
+
expectedVersion: connectionDisconnectOperations.expectedVersion,
|
|
16281
|
+
resultVersion: connectionDisconnectOperations.resultVersion
|
|
16282
|
+
};
|
|
16283
|
+
async function disconnectConnectionIdempotently(db, input) {
|
|
16284
|
+
return await withConnectionSubjectRls(
|
|
16285
|
+
db,
|
|
16286
|
+
input.workspaceId,
|
|
16287
|
+
input.subjectId,
|
|
16288
|
+
async (scopedDb) => {
|
|
16289
|
+
const [connectionRow] = await scopedDb.select(connectionMetadataColumns).from(connections).where(
|
|
16290
|
+
and17(
|
|
16291
|
+
eq17(connections.accountId, input.accountId),
|
|
16292
|
+
eq17(connections.workspaceId, input.workspaceId),
|
|
16293
|
+
eq17(connections.id, input.connectionId),
|
|
16294
|
+
eq17(connections.subjectId, input.subjectId)
|
|
16295
|
+
)
|
|
16296
|
+
).for("update");
|
|
16297
|
+
if (!connectionRow) return null;
|
|
16298
|
+
const connection = mapConnectionMetadata(connectionRow);
|
|
16299
|
+
const [replayed] = await scopedDb.select(connectionDisconnectOperationColumns).from(connectionDisconnectOperations).where(
|
|
16300
|
+
and17(
|
|
16301
|
+
eq17(connectionDisconnectOperations.workspaceId, input.workspaceId),
|
|
16302
|
+
eq17(connectionDisconnectOperations.subjectId, input.subjectId),
|
|
16303
|
+
eq17(connectionDisconnectOperations.idempotencyKey, input.idempotencyKey)
|
|
16304
|
+
)
|
|
16305
|
+
).limit(1);
|
|
16306
|
+
if (replayed) {
|
|
16307
|
+
if (replayed.accountId !== input.accountId || replayed.connectionId !== input.connectionId || replayed.expectedVersion !== input.expectedVersion) {
|
|
16308
|
+
throw new ConnectionDisconnectIdempotencyError();
|
|
16309
|
+
}
|
|
16310
|
+
if (connection.version !== replayed.resultVersion) {
|
|
16311
|
+
throw new ConnectionDisconnectGenerationError();
|
|
16312
|
+
}
|
|
16313
|
+
return connection;
|
|
16314
|
+
}
|
|
16315
|
+
if (connection.version !== input.expectedVersion) {
|
|
16316
|
+
throw new ConnectionDisconnectGenerationError();
|
|
16317
|
+
}
|
|
16318
|
+
const [inserted] = await scopedDb.insert(connectionDisconnectOperations).values({
|
|
16319
|
+
accountId: input.accountId,
|
|
16320
|
+
workspaceId: input.workspaceId,
|
|
16321
|
+
connectionId: input.connectionId,
|
|
16322
|
+
subjectId: input.subjectId,
|
|
16323
|
+
idempotencyKey: input.idempotencyKey,
|
|
16324
|
+
expectedVersion: input.expectedVersion,
|
|
16325
|
+
resultVersion: input.expectedVersion + 1
|
|
16326
|
+
}).onConflictDoNothing().returning(connectionDisconnectOperationColumns);
|
|
16327
|
+
if (!inserted) {
|
|
16328
|
+
const [conflictingKey] = await scopedDb.select(connectionDisconnectOperationColumns).from(connectionDisconnectOperations).where(
|
|
16329
|
+
and17(
|
|
16330
|
+
eq17(connectionDisconnectOperations.workspaceId, input.workspaceId),
|
|
16331
|
+
eq17(connectionDisconnectOperations.subjectId, input.subjectId),
|
|
16332
|
+
eq17(connectionDisconnectOperations.idempotencyKey, input.idempotencyKey)
|
|
16333
|
+
)
|
|
16334
|
+
).limit(1);
|
|
16335
|
+
if (!conflictingKey || conflictingKey.accountId !== input.accountId || conflictingKey.connectionId !== input.connectionId || conflictingKey.expectedVersion !== input.expectedVersion) {
|
|
16336
|
+
throw new ConnectionDisconnectIdempotencyError();
|
|
16337
|
+
}
|
|
16338
|
+
if (connection.version !== conflictingKey.resultVersion) {
|
|
16339
|
+
throw new ConnectionDisconnectGenerationError();
|
|
16340
|
+
}
|
|
16341
|
+
return connection;
|
|
16342
|
+
}
|
|
16343
|
+
const [updated] = await scopedDb.update(connections).set({
|
|
16344
|
+
status: "revoked",
|
|
16345
|
+
metadata: input.metadata,
|
|
16346
|
+
...input.lastError !== void 0 ? { lastError: input.lastError } : {},
|
|
16347
|
+
version: sql14`${connections.version} + 1`,
|
|
16348
|
+
...input.updatedBySubjectId !== void 0 ? { updatedBySubjectId: input.updatedBySubjectId } : {},
|
|
16349
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
16350
|
+
}).where(
|
|
16351
|
+
and17(
|
|
16352
|
+
eq17(connections.accountId, input.accountId),
|
|
16353
|
+
eq17(connections.workspaceId, input.workspaceId),
|
|
16354
|
+
eq17(connections.id, input.connectionId),
|
|
16355
|
+
eq17(connections.subjectId, input.subjectId),
|
|
16356
|
+
eq17(connections.version, input.expectedVersion)
|
|
16357
|
+
)
|
|
16358
|
+
).returning(connectionMetadataColumns);
|
|
16359
|
+
if (!updated) {
|
|
16360
|
+
throw new ConnectionDisconnectGenerationError();
|
|
16361
|
+
}
|
|
16362
|
+
return mapConnectionMetadata(updated);
|
|
16363
|
+
}
|
|
16364
|
+
);
|
|
16365
|
+
}
|
|
15620
16366
|
async function revokeConnectionInScope(db, workspaceId, connectionId, updatedBySubjectId, expectedVersion) {
|
|
15621
16367
|
const [row] = await db.update(connections).set({
|
|
15622
16368
|
status: "revoked",
|
|
@@ -15645,12 +16391,18 @@ async function revokeConnectionInScope(db, workspaceId, connectionId, updatedByS
|
|
|
15645
16391
|
).returning(connectionMetadataColumns);
|
|
15646
16392
|
return row ? mapConnectionMetadata(row) : null;
|
|
15647
16393
|
}
|
|
15648
|
-
async function revokeConnection(db, workspaceId, connectionId, updatedBySubjectId) {
|
|
16394
|
+
async function revokeConnection(db, workspaceId, connectionId, updatedBySubjectId, expectedVersion) {
|
|
15649
16395
|
return await withConnectionSubjectRls(
|
|
15650
16396
|
db,
|
|
15651
16397
|
workspaceId,
|
|
15652
16398
|
updatedBySubjectId,
|
|
15653
|
-
async (scopedDb) => await revokeConnectionInScope(
|
|
16399
|
+
async (scopedDb) => await revokeConnectionInScope(
|
|
16400
|
+
scopedDb,
|
|
16401
|
+
workspaceId,
|
|
16402
|
+
connectionId,
|
|
16403
|
+
updatedBySubjectId,
|
|
16404
|
+
expectedVersion
|
|
16405
|
+
)
|
|
15654
16406
|
);
|
|
15655
16407
|
}
|
|
15656
16408
|
async function resolveSlackInstallationRoute(db, slackTeamId) {
|
|
@@ -15715,7 +16467,7 @@ async function enqueueSlackInteractionInbox(db, input) {
|
|
|
15715
16467
|
const [existing] = await scopedDb.select().from(slackInteractionInbox).where(
|
|
15716
16468
|
and17(
|
|
15717
16469
|
eq17(slackInteractionInbox.connectionId, input.connectionId),
|
|
15718
|
-
|
|
16470
|
+
or6(
|
|
15719
16471
|
eq17(slackInteractionInbox.providerEventId, input.providerEventId),
|
|
15720
16472
|
eq17(slackInteractionInbox.providerMessageId, input.providerMessageId)
|
|
15721
16473
|
)
|
|
@@ -15929,7 +16681,7 @@ async function bindSlackInteractionSession(db, input) {
|
|
|
15929
16681
|
eq17(slackInteractions.id, input.id),
|
|
15930
16682
|
eq17(slackInteractions.owningSubjectId, input.owningSubjectId),
|
|
15931
16683
|
eq17(slackInteractions.sessionReservationId, input.sessionId),
|
|
15932
|
-
|
|
16684
|
+
isNull6(slackInteractions.sessionId)
|
|
15933
16685
|
)
|
|
15934
16686
|
).returning();
|
|
15935
16687
|
if (row) return mapSlackInteraction(row);
|
|
@@ -16431,8 +17183,8 @@ async function claimSlackBotPostOperation(db, input) {
|
|
|
16431
17183
|
}).where(
|
|
16432
17184
|
and17(
|
|
16433
17185
|
eq17(slackBotPostOperations.id, existing.id),
|
|
16434
|
-
|
|
16435
|
-
|
|
17186
|
+
or6(
|
|
17187
|
+
isNull6(slackBotPostOperations.claimHolderId),
|
|
16436
17188
|
lte2(slackBotPostOperations.claimExpiresAt, sql14`now()`)
|
|
16437
17189
|
)
|
|
16438
17190
|
)
|
|
@@ -16628,7 +17380,7 @@ async function markSlackBotDeleteOperationProviderStarted(db, input) {
|
|
|
16628
17380
|
eq17(slackBotDeleteOperations.connectionId, input.connectionId),
|
|
16629
17381
|
eq17(slackBotDeleteOperations.operationId, input.operationId),
|
|
16630
17382
|
eq17(slackBotDeleteOperations.claimHolderId, input.claimHolderId),
|
|
16631
|
-
|
|
17383
|
+
or6(
|
|
16632
17384
|
eq17(slackBotDeleteOperations.status, "pending"),
|
|
16633
17385
|
eq17(slackBotDeleteOperations.status, "outcome_unknown")
|
|
16634
17386
|
)
|
|
@@ -16736,7 +17488,7 @@ async function loadConnectionCredentialForBroker(db, settings, input) {
|
|
|
16736
17488
|
"connection credential present but OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is not configured"
|
|
16737
17489
|
);
|
|
16738
17490
|
}
|
|
16739
|
-
const subjectPredicate = input.allowSubjectOwned ? connectionExactSubject(input.subjectId) :
|
|
17491
|
+
const subjectPredicate = input.allowSubjectOwned ? connectionExactSubject(input.subjectId) : isNull6(connections.subjectId);
|
|
16740
17492
|
const conditions = [
|
|
16741
17493
|
eq17(connections.workspaceId, input.workspaceId),
|
|
16742
17494
|
subjectPredicate
|
|
@@ -20219,7 +20971,7 @@ async function withCodexCapacityMutation(db, input, mutate) {
|
|
|
20219
20971
|
eq17(codexCapacityWaiters.workspaceId, input.workspaceId),
|
|
20220
20972
|
eq17(codexCapacityWaiters.status, "waiting"),
|
|
20221
20973
|
...input.policyHash !== void 0 ? [
|
|
20222
|
-
input.policyHash === null ?
|
|
20974
|
+
input.policyHash === null ? isNull6(codexCapacityWaiters.policyHash) : eq17(codexCapacityWaiters.policyHash, input.policyHash)
|
|
20223
20975
|
] : []
|
|
20224
20976
|
)
|
|
20225
20977
|
).returning();
|
|
@@ -20312,7 +21064,7 @@ async function supersedeCodexCapacityWaitInTransaction(tx, input) {
|
|
|
20312
21064
|
eq17(sessionTurns.workspaceId, input.session.workspaceId),
|
|
20313
21065
|
eq17(sessionTurns.id, input.blockedTurn.id),
|
|
20314
21066
|
eq17(sessionTurns.status, "waiting_capacity"),
|
|
20315
|
-
|
|
21067
|
+
isNull6(sessionTurns.activeAttemptId),
|
|
20316
21068
|
eq17(sessionTurns.executionGeneration, input.waiter.blockedTurnGeneration)
|
|
20317
21069
|
)
|
|
20318
21070
|
).returning({ id: sessionTurns.id });
|
|
@@ -20590,7 +21342,7 @@ async function reconcileCodexCapacityWait(db, input, decide, policy) {
|
|
|
20590
21342
|
eq17(sessionTurns.workspaceId, input.workspaceId),
|
|
20591
21343
|
eq17(sessionTurns.id, blockedTurn.id),
|
|
20592
21344
|
eq17(sessionTurns.status, "waiting_capacity"),
|
|
20593
|
-
|
|
21345
|
+
isNull6(sessionTurns.activeAttemptId),
|
|
20594
21346
|
eq17(sessionTurns.executionGeneration, waiter.blockedTurnGeneration)
|
|
20595
21347
|
)
|
|
20596
21348
|
).returning({ id: sessionTurns.id });
|
|
@@ -21411,7 +22163,7 @@ async function setInitialActiveCodexCredential(db, workspaceId, credentialId) {
|
|
|
21411
22163
|
const rows = await scopedDb.update(codexRotationSettings).set({ activeCredentialId: credentialId, updatedAt: /* @__PURE__ */ new Date() }).where(
|
|
21412
22164
|
and17(
|
|
21413
22165
|
eq17(codexRotationSettings.workspaceId, workspaceId),
|
|
21414
|
-
|
|
22166
|
+
isNull6(codexRotationSettings.activeCredentialId)
|
|
21415
22167
|
)
|
|
21416
22168
|
).returning({ id: codexRotationSettings.id });
|
|
21417
22169
|
return rows.length > 0;
|
|
@@ -21560,8 +22312,8 @@ async function setSessionCodexPin(db, workspaceId, sessionId, pinnedCredentialId
|
|
|
21560
22312
|
];
|
|
21561
22313
|
if (options.expected) {
|
|
21562
22314
|
conditions.push(
|
|
21563
|
-
options.expected.pinnedCredentialId === null ?
|
|
21564
|
-
options.expected.pinSource === null ?
|
|
22315
|
+
options.expected.pinnedCredentialId === null ? isNull6(sessions.codexPinnedCredentialId) : eq17(sessions.codexPinnedCredentialId, options.expected.pinnedCredentialId),
|
|
22316
|
+
options.expected.pinSource === null ? isNull6(sessions.codexPinSource) : eq17(sessions.codexPinSource, options.expected.pinSource)
|
|
21565
22317
|
);
|
|
21566
22318
|
}
|
|
21567
22319
|
const updated = await scopedDb.update(sessions).set({
|
|
@@ -22911,7 +23663,7 @@ async function listSessionSpawnDenials(db, workspaceId, options = {}) {
|
|
|
22911
23663
|
const rows = await scopedDb.select().from(sessionSpawnDenials).where(
|
|
22912
23664
|
and17(
|
|
22913
23665
|
eq17(sessionSpawnDenials.workspaceId, workspaceId),
|
|
22914
|
-
options.parentSessionId === void 0 ? void 0 : options.parentSessionId === null ?
|
|
23666
|
+
options.parentSessionId === void 0 ? void 0 : options.parentSessionId === null ? isNull6(sessionSpawnDenials.parentSessionId) : eq17(sessionSpawnDenials.parentSessionId, options.parentSessionId)
|
|
22915
23667
|
)
|
|
22916
23668
|
).orderBy(desc8(sessionSpawnDenials.createdAt), desc8(sessionSpawnDenials.id)).limit(Math.max(1, Math.min(options.limit ?? 100, 1e3)));
|
|
22917
23669
|
return rows.map(mapSessionSpawnDenial);
|
|
@@ -23229,7 +23981,7 @@ function sessionFilters(options) {
|
|
|
23229
23981
|
if (Object.prototype.hasOwnProperty.call(options, "parentSessionId")) {
|
|
23230
23982
|
const parentSessionId = options.parentSessionId;
|
|
23231
23983
|
if (parentSessionId === null) {
|
|
23232
|
-
filters.push(
|
|
23984
|
+
filters.push(isNull6(sessions.parentSessionId));
|
|
23233
23985
|
} else if (parentSessionId !== void 0) {
|
|
23234
23986
|
filters.push(eq17(sessions.parentSessionId, parentSessionId));
|
|
23235
23987
|
}
|
|
@@ -23238,7 +23990,7 @@ function sessionFilters(options) {
|
|
|
23238
23990
|
if (search) {
|
|
23239
23991
|
const pattern = `%${search.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_")}%`;
|
|
23240
23992
|
filters.push(
|
|
23241
|
-
|
|
23993
|
+
or6(ilike(sessions.title, pattern), ilike(sessions.initialMessage, pattern))
|
|
23242
23994
|
);
|
|
23243
23995
|
}
|
|
23244
23996
|
return filters;
|
|
@@ -23271,7 +24023,7 @@ function sessionAuthorizationScopeFilter(scope) {
|
|
|
23271
24023
|
)
|
|
23272
24024
|
select id from authorized_sessions
|
|
23273
24025
|
)` : sql14`false`;
|
|
23274
|
-
return
|
|
24026
|
+
return or6(exact, descendants);
|
|
23275
24027
|
}
|
|
23276
24028
|
async function sessionIdsCoveredByAuthorizationRoots(db, workspaceId, sessionIds, scope) {
|
|
23277
24029
|
if (sessionIds.length === 0) return /* @__PURE__ */ new Set();
|
|
@@ -23453,7 +24205,7 @@ async function listSessionsForSubject(db, workspaceId, options) {
|
|
|
23453
24205
|
).where(
|
|
23454
24206
|
and17(
|
|
23455
24207
|
...filters,
|
|
23456
|
-
|
|
24208
|
+
or6(isNull6(sessionPins.id), eq17(sessionPins.pinned, false))
|
|
23457
24209
|
)
|
|
23458
24210
|
).orderBy(desc8(sessions.updatedAt), desc8(sessions.id)).limit(limit);
|
|
23459
24211
|
pageIds = ordinaryIdRows.map((row) => row.id);
|
|
@@ -23466,14 +24218,14 @@ async function listSessionsForSubject(db, workspaceId, options) {
|
|
|
23466
24218
|
lte2(sessionListSnapshots.expiresAt, now)
|
|
23467
24219
|
)
|
|
23468
24220
|
);
|
|
23469
|
-
const snapshotSearchFilter = searchFilter ? eq17(sessionListSnapshots.search, searchFilter) :
|
|
24221
|
+
const snapshotSearchFilter = searchFilter ? eq17(sessionListSnapshots.search, searchFilter) : isNull6(sessionListSnapshots.search);
|
|
23470
24222
|
const [reusableSnapshot] = await tx.select().from(sessionListSnapshots).where(
|
|
23471
24223
|
and17(
|
|
23472
24224
|
eq17(sessionListSnapshots.workspaceId, workspaceId),
|
|
23473
24225
|
eq17(sessionListSnapshots.subjectId, options.subjectId),
|
|
23474
24226
|
eq17(sessionListSnapshots.parentSessionFilter, parentFilter),
|
|
23475
24227
|
snapshotSearchFilter,
|
|
23476
|
-
|
|
24228
|
+
gt5(
|
|
23477
24229
|
sessionListSnapshots.createdAt,
|
|
23478
24230
|
new Date(now.getTime() - SESSION_LIST_SNAPSHOT_REUSE_MS)
|
|
23479
24231
|
)
|
|
@@ -23494,7 +24246,7 @@ async function listSessionsForSubject(db, workspaceId, options) {
|
|
|
23494
24246
|
).where(
|
|
23495
24247
|
and17(
|
|
23496
24248
|
...filters,
|
|
23497
|
-
|
|
24249
|
+
or6(isNull6(sessionPins.id), eq17(sessionPins.pinned, false))
|
|
23498
24250
|
)
|
|
23499
24251
|
).orderBy(desc8(sessions.updatedAt), desc8(sessions.id)).limit(SESSION_LIST_SNAPSHOT_MAX_IDS + 1);
|
|
23500
24252
|
if (ordinaryIdRows.length > SESSION_LIST_SNAPSHOT_MAX_IDS) {
|
|
@@ -23568,7 +24320,7 @@ async function listSessionsForSubject(db, workspaceId, options) {
|
|
|
23568
24320
|
eq17(sessions.workspaceId, workspaceId),
|
|
23569
24321
|
inArray11(sessions.id, pageIds),
|
|
23570
24322
|
...options.authorizationScope ? [sessionAuthorizationScopeFilter(options.authorizationScope)] : [],
|
|
23571
|
-
|
|
24323
|
+
or6(isNull6(sessionPins.id), eq17(sessionPins.pinned, false))
|
|
23572
24324
|
)
|
|
23573
24325
|
);
|
|
23574
24326
|
const ordinaryById = new Map(ordinaryRows.map((row) => [row.session.id, row]));
|
|
@@ -23758,7 +24510,7 @@ async function listSessions(db, workspaceId, limitOrOptions = 50) {
|
|
|
23758
24510
|
if (Object.prototype.hasOwnProperty.call(options, "parentSessionId")) {
|
|
23759
24511
|
const parentSessionId = options.parentSessionId;
|
|
23760
24512
|
if (parentSessionId === null) {
|
|
23761
|
-
filters.push(
|
|
24513
|
+
filters.push(isNull6(sessions.parentSessionId));
|
|
23762
24514
|
} else if (parentSessionId !== void 0) {
|
|
23763
24515
|
filters.push(eq17(sessions.parentSessionId, parentSessionId));
|
|
23764
24516
|
}
|
|
@@ -23899,11 +24651,11 @@ async function listSessionDiscoverySummaries(db, workspaceId, options) {
|
|
|
23899
24651
|
if (orderBy === "createdAt" && options.cursor && (cursorSortRevision !== "0" || options.cursor.snapshotRevision !== "0")) {
|
|
23900
24652
|
throw new Error("sessions_list created-order cursor cannot carry activity revisions");
|
|
23901
24653
|
}
|
|
23902
|
-
const cursorPredicate = options.cursor ? orderBy === "updatedAt" ?
|
|
24654
|
+
const cursorPredicate = options.cursor ? orderBy === "updatedAt" ? or6(
|
|
23903
24655
|
sql14`${sessions.activityRevision} < ${cursorSortRevision}::text::bigint`,
|
|
23904
24656
|
and17(
|
|
23905
24657
|
sql14`${sessions.activityRevision} = ${cursorSortRevision}::text::bigint`,
|
|
23906
|
-
|
|
24658
|
+
or6(
|
|
23907
24659
|
sql14`${sessions.updatedAt} < ${options.cursor.sortAt}::text::timestamptz`,
|
|
23908
24660
|
and17(
|
|
23909
24661
|
sql14`${sessions.updatedAt} = ${options.cursor.sortAt}::text::timestamptz`,
|
|
@@ -23911,7 +24663,7 @@ async function listSessionDiscoverySummaries(db, workspaceId, options) {
|
|
|
23911
24663
|
)
|
|
23912
24664
|
)
|
|
23913
24665
|
)
|
|
23914
|
-
) :
|
|
24666
|
+
) : or6(
|
|
23915
24667
|
// Cast through text deliberately. postgres.js otherwise infers a
|
|
23916
24668
|
// timestamptz parameter and serializes this exact cursor string via
|
|
23917
24669
|
// JS Date, which discards PostgreSQL's sub-millisecond precision.
|
|
@@ -24282,16 +25034,16 @@ async function listSessionEventPage(db, workspaceId, sessionId, options = {}) {
|
|
|
24282
25034
|
const filters = [
|
|
24283
25035
|
eq17(sessionEvents.workspaceId, workspaceId),
|
|
24284
25036
|
eq17(sessionEvents.sessionId, sessionId),
|
|
24285
|
-
|
|
25037
|
+
gt5(sessionEvents.sequence, after)
|
|
24286
25038
|
];
|
|
24287
25039
|
if (options.authoritativeLatest) {
|
|
24288
25040
|
filters.push(
|
|
24289
25041
|
and17(
|
|
24290
|
-
|
|
24291
|
-
|
|
25042
|
+
or6(
|
|
25043
|
+
isNull6(sessionEvents.turnAssociation),
|
|
24292
25044
|
eq17(sessionEvents.turnAssociation, "current")
|
|
24293
25045
|
),
|
|
24294
|
-
|
|
25046
|
+
isNull6(sessionEvents.duplicateOfEventId)
|
|
24295
25047
|
)
|
|
24296
25048
|
);
|
|
24297
25049
|
}
|
|
@@ -24306,7 +25058,7 @@ async function listSessionEventPage(db, workspaceId, sessionId, options = {}) {
|
|
|
24306
25058
|
filters.push(lt4(sessionEvents.sequence, cursor));
|
|
24307
25059
|
}
|
|
24308
25060
|
} else if (cursor !== void 0) {
|
|
24309
|
-
filters.push(
|
|
25061
|
+
filters.push(gt5(sessionEvents.sequence, cursor));
|
|
24310
25062
|
}
|
|
24311
25063
|
if (direction === "after" && before !== void 0 && before <= POSTGRES_INT_MAX) {
|
|
24312
25064
|
filters.push(lt4(sessionEvents.sequence, before));
|
|
@@ -24489,7 +25241,7 @@ async function listSessionEvents(db, workspaceId, sessionId, afterOrOptions = 0,
|
|
|
24489
25241
|
const filters = [
|
|
24490
25242
|
eq17(sessionEvents.workspaceId, workspaceId),
|
|
24491
25243
|
eq17(sessionEvents.sessionId, sessionId),
|
|
24492
|
-
|
|
25244
|
+
gt5(sessionEvents.sequence, after)
|
|
24493
25245
|
];
|
|
24494
25246
|
if (typeFilters.includeTypes.length > 0) {
|
|
24495
25247
|
filters.push(inArray11(sessionEvents.type, typeFilters.includeTypes));
|
|
@@ -24623,7 +25375,7 @@ async function listWorkspaceControlEvents(db, workspaceId, after = 0, limit = 50
|
|
|
24623
25375
|
const rows = await scopedDb.select().from(workspaceControlEvents).where(
|
|
24624
25376
|
and17(
|
|
24625
25377
|
eq17(workspaceControlEvents.workspaceId, workspaceId),
|
|
24626
|
-
|
|
25378
|
+
gt5(workspaceControlEvents.revision, cursor)
|
|
24627
25379
|
)
|
|
24628
25380
|
).orderBy(asc8(workspaceControlEvents.revision)).limit(boundedLimit);
|
|
24629
25381
|
return rows.map(mapWorkspaceControlEvent);
|
|
@@ -24769,21 +25521,6 @@ function validateAnsweredHumanInput(questions, answers) {
|
|
|
24769
25521
|
`Text question ${question.id} accepts one value`
|
|
24770
25522
|
);
|
|
24771
25523
|
}
|
|
24772
|
-
const value = values[0] ?? "";
|
|
24773
|
-
const minLength = question.validation?.minLength;
|
|
24774
|
-
const maxLength = question.validation?.maxLength;
|
|
24775
|
-
if (minLength != null && value.length < minLength) {
|
|
24776
|
-
throw new HumanInputResponseValidationError(
|
|
24777
|
-
"INVALID_RESPONSE",
|
|
24778
|
-
`Question ${question.id} is shorter than its minimum length`
|
|
24779
|
-
);
|
|
24780
|
-
}
|
|
24781
|
-
if (maxLength != null && value.length > maxLength) {
|
|
24782
|
-
throw new HumanInputResponseValidationError(
|
|
24783
|
-
"INVALID_RESPONSE",
|
|
24784
|
-
`Question ${question.id} exceeds its maximum length`
|
|
24785
|
-
);
|
|
24786
|
-
}
|
|
24787
25524
|
continue;
|
|
24788
25525
|
}
|
|
24789
25526
|
if (other && !question.allowOther) {
|
|
@@ -24856,7 +25593,7 @@ async function hasAcceptedRequiresActionAdvance(tx, input) {
|
|
|
24856
25593
|
eq17(sessionEvents.turnId, input.turnId),
|
|
24857
25594
|
eq17(sessionEvents.turnGeneration, input.turnGeneration),
|
|
24858
25595
|
inArray11(sessionEvents.type, ["user.approvalDecision", "user.humanInputResponse"]),
|
|
24859
|
-
|
|
25596
|
+
gt5(sessionEvents.sequence, trigger.sequence)
|
|
24860
25597
|
)
|
|
24861
25598
|
).limit(1);
|
|
24862
25599
|
return accepted !== void 0;
|
|
@@ -24994,6 +25731,26 @@ async function acceptSessionHumanInputResponse(db, input) {
|
|
|
24994
25731
|
occurredAt: now
|
|
24995
25732
|
}).returning();
|
|
24996
25733
|
if (!event) throw new Error("Failed to append human-input response");
|
|
25734
|
+
await mirrorSessionRealtimeContextInTransaction(tx, {
|
|
25735
|
+
accountId: session.accountId,
|
|
25736
|
+
workspaceId: input.workspaceId,
|
|
25737
|
+
sessionId: input.sessionId,
|
|
25738
|
+
sourceKind: "human_input_response",
|
|
25739
|
+
sourceId: event.id,
|
|
25740
|
+
turnId: turn.id,
|
|
25741
|
+
channel: response.outcome === "answered" || response.outcome === "skipped" ? "speakable" : null,
|
|
25742
|
+
text: renderRealtimeHumanInputResponseContext({
|
|
25743
|
+
requestId: request.id,
|
|
25744
|
+
questions: request.questions,
|
|
25745
|
+
response
|
|
25746
|
+
}),
|
|
25747
|
+
payload: {
|
|
25748
|
+
requestId: request.id,
|
|
25749
|
+
outcome: response.outcome,
|
|
25750
|
+
sourceEventId: event.id
|
|
25751
|
+
},
|
|
25752
|
+
now
|
|
25753
|
+
});
|
|
24997
25754
|
await tx.update(sessions).set({ lastSequence: session.lastSequence + 1, updatedAt: now }).where(eq17(sessions.id, session.id));
|
|
24998
25755
|
const workflowWakeRevision = await enqueueSessionWorkflowWakeInTransaction(
|
|
24999
25756
|
tx,
|
|
@@ -26054,7 +26811,7 @@ function exactPtyIdentityPredicates(identity) {
|
|
|
26054
26811
|
eq17(sandboxPtySessions.providerBackend, identity.providerBackend),
|
|
26055
26812
|
eq17(sandboxPtySessions.providerInstanceId, identity.providerInstanceId),
|
|
26056
26813
|
eq17(sandboxPtySessions.routeKind, identity.routeKind),
|
|
26057
|
-
identity.routeTargetId === null ?
|
|
26814
|
+
identity.routeTargetId === null ? isNull6(sandboxPtySessions.routeTargetId) : eq17(sandboxPtySessions.routeTargetId, identity.routeTargetId),
|
|
26058
26815
|
eq17(sandboxPtySessions.routeEpoch, identity.routeEpoch)
|
|
26059
26816
|
];
|
|
26060
26817
|
}
|
|
@@ -28079,7 +28836,7 @@ async function settleExactLostProviderWorkspaceBlockersTx(tx, input) {
|
|
|
28079
28836
|
eq17(sandboxWorkspaceMutationAdmissions.sandboxGroupId, input.sandboxGroupId),
|
|
28080
28837
|
eq17(sandboxWorkspaceMutationAdmissions.leaseEpoch, input.lostEpoch),
|
|
28081
28838
|
eq17(sandboxWorkspaceMutationAdmissions.providerInstanceId, input.lostInstanceId),
|
|
28082
|
-
|
|
28839
|
+
isNull6(sandboxWorkspaceMutationAdmissions.settledAt)
|
|
28083
28840
|
)
|
|
28084
28841
|
).returning({ id: sandboxWorkspaceMutationAdmissions.id });
|
|
28085
28842
|
const closedPtys = await tx.update(sandboxPtySessions).set({ status: "closed", closedAt: /* @__PURE__ */ new Date() }).where(
|
|
@@ -29711,8 +30468,8 @@ async function retainWorkspaceMutationProcess(db, input) {
|
|
|
29711
30468
|
and17(
|
|
29712
30469
|
eq17(sandboxRetainedProcesses.id, process.id),
|
|
29713
30470
|
eq17(sandboxRetainedProcesses.state, "active"),
|
|
29714
|
-
|
|
29715
|
-
|
|
30471
|
+
isNull6(sandboxRetainedProcesses.providerBindingKey),
|
|
30472
|
+
isNull6(sandboxRetainedProcesses.providerBinding)
|
|
29716
30473
|
)
|
|
29717
30474
|
).returning();
|
|
29718
30475
|
if (!bound) {
|
|
@@ -29917,8 +30674,8 @@ async function bindRetainedProcessProviderIdentity(db, input) {
|
|
|
29917
30674
|
eq17(sandboxRetainedProcesses.id, process.id),
|
|
29918
30675
|
eq17(sandboxRetainedProcesses.state, "active"),
|
|
29919
30676
|
eq17(sandboxRetainedProcesses.reconcileClaimId, input.claimId),
|
|
29920
|
-
|
|
29921
|
-
|
|
30677
|
+
isNull6(sandboxRetainedProcesses.providerBindingKey),
|
|
30678
|
+
isNull6(sandboxRetainedProcesses.providerBinding)
|
|
29922
30679
|
)
|
|
29923
30680
|
).returning();
|
|
29924
30681
|
if (!updated) {
|
|
@@ -31893,7 +32650,7 @@ async function setEnrollmentDisplayState(db, input) {
|
|
|
31893
32650
|
// Only write on a CHANGE to EITHER field — an unchanged display state must
|
|
31894
32651
|
// not churn a write on every reconnect Hello. `IS DISTINCT FROM` is the
|
|
31895
32652
|
// null-safe inequality (a plain `ne` skips NULL rows).
|
|
31896
|
-
|
|
32653
|
+
or6(
|
|
31897
32654
|
ne(enrollments.hasDisplay, input.hasDisplay),
|
|
31898
32655
|
sql14`${enrollments.desktopUnavailableReason} IS DISTINCT FROM ${input.desktopUnavailableReason}`
|
|
31899
32656
|
)
|
|
@@ -32588,7 +33345,7 @@ async function forceDrainOverLimitViewerOnlyBoxes(db, input) {
|
|
|
32588
33345
|
and17(
|
|
32589
33346
|
eq17(usageEvents.workspaceId, input.workspaceId),
|
|
32590
33347
|
eq17(usageEvents.eventType, "sandbox.warm_seconds"),
|
|
32591
|
-
|
|
33348
|
+
gt5(usageEvents.occurredAt, since)
|
|
32592
33349
|
)
|
|
32593
33350
|
);
|
|
32594
33351
|
if (Number(total) >= input.maxWarmSecondsPerWorkspace) {
|
|
@@ -34575,7 +35332,7 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
34575
35332
|
"acknowledged",
|
|
34576
35333
|
"settled"
|
|
34577
35334
|
]),
|
|
34578
|
-
|
|
35335
|
+
isNull6(sessionTurnAttempts.quiescedAt)
|
|
34579
35336
|
)
|
|
34580
35337
|
).orderBy(desc8(sessionAttemptInterruptions.requestedAt)).limit(1);
|
|
34581
35338
|
if (unquiescedInterruption) {
|
|
@@ -34623,7 +35380,7 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
34623
35380
|
eq17(sessions.accountId, session.accountId),
|
|
34624
35381
|
eq17(sessions.workspaceId, workspaceId),
|
|
34625
35382
|
eq17(sessions.id, sessionId),
|
|
34626
|
-
|
|
35383
|
+
isNull6(sessions.activeTurnId)
|
|
34627
35384
|
)
|
|
34628
35385
|
).returning({ id: sessions.id });
|
|
34629
35386
|
if (!claimedSession) {
|
|
@@ -35328,11 +36085,10 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
35328
36085
|
sessionId,
|
|
35329
36086
|
turnId: row.id,
|
|
35330
36087
|
position: Number(historyPosition),
|
|
35331
|
-
item:
|
|
35332
|
-
|
|
35333
|
-
|
|
35334
|
-
|
|
35335
|
-
}),
|
|
36088
|
+
item: durableUserHistoryItem(
|
|
36089
|
+
row.prompt,
|
|
36090
|
+
Array.isArray(row.resources) ? row.resources : []
|
|
36091
|
+
),
|
|
35336
36092
|
producerCodexCredentialId: null
|
|
35337
36093
|
});
|
|
35338
36094
|
const providerDelegatedTurn = isSessionRealtimeDelegationTurnMetadata(row.metadata);
|
|
@@ -35449,7 +36205,7 @@ async function markSessionAttemptQuiesced(db, input) {
|
|
|
35449
36205
|
and17(
|
|
35450
36206
|
eq17(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
35451
36207
|
eq17(sessionTurnAttempts.id, input.attemptId),
|
|
35452
|
-
|
|
36208
|
+
isNull6(sessionTurnAttempts.quiescedAt)
|
|
35453
36209
|
)
|
|
35454
36210
|
).returning({ id: sessionTurnAttempts.id });
|
|
35455
36211
|
if (!marked) {
|
|
@@ -35987,7 +36743,7 @@ async function peekSessionWork(db, workspaceId, sessionId) {
|
|
|
35987
36743
|
"user.approvalDecision",
|
|
35988
36744
|
"user.humanInputResponse"
|
|
35989
36745
|
]),
|
|
35990
|
-
|
|
36746
|
+
gt5(sessionEvents.sequence, currentTrigger.sequence)
|
|
35991
36747
|
)
|
|
35992
36748
|
).orderBy(desc8(sessionEvents.sequence), desc8(sessionEvents.id)).limit(1);
|
|
35993
36749
|
if (actionResponse) {
|
|
@@ -36452,11 +37208,11 @@ async function applySessionTurnSettlement(db, workspaceId, input, hooks = {}) {
|
|
|
36452
37208
|
activeTurnId: session.activeTurnId
|
|
36453
37209
|
};
|
|
36454
37210
|
}
|
|
37211
|
+
const humanInputRequests = input.runState?.humanInputRequests ?? [];
|
|
36455
37212
|
if (input.runState) {
|
|
36456
37213
|
if (input.turnStatus !== "requires_action" || input.sessionStatus !== "requires_action") {
|
|
36457
37214
|
throw new Error("A frozen run state requires a requires_action settlement");
|
|
36458
37215
|
}
|
|
36459
|
-
const humanInputRequests = input.runState.humanInputRequests ?? [];
|
|
36460
37216
|
for (const request of humanInputRequests) {
|
|
36461
37217
|
const parsedQuestions = request.questions.map(
|
|
36462
37218
|
(question) => HumanInputQuestionContract.parse(question)
|
|
@@ -36656,7 +37412,10 @@ async function applySessionTurnSettlement(db, workspaceId, input, hooks = {}) {
|
|
|
36656
37412
|
eq17(sessionHumanInputRequests.turnId, input.turnId),
|
|
36657
37413
|
eq17(sessionHumanInputRequests.status, "pending")
|
|
36658
37414
|
)
|
|
36659
|
-
).returning({
|
|
37415
|
+
).returning({
|
|
37416
|
+
id: sessionHumanInputRequests.id,
|
|
37417
|
+
questions: sessionHumanInputRequests.questions
|
|
37418
|
+
}) : [];
|
|
36660
37419
|
const terminalHumanInputEvents = terminalHumanInputRows.map(
|
|
36661
37420
|
(request) => ({
|
|
36662
37421
|
type: "user.humanInputResponse",
|
|
@@ -36721,6 +37480,69 @@ async function applySessionTurnSettlement(db, workspaceId, input, hooks = {}) {
|
|
|
36721
37480
|
};
|
|
36722
37481
|
});
|
|
36723
37482
|
const inserted = values.length > 0 ? await tx.insert(sessionEvents).values(values).returning() : [];
|
|
37483
|
+
const requestedEvents = inserted.filter(
|
|
37484
|
+
(event) => event.type === "session.humanInput.requested"
|
|
37485
|
+
);
|
|
37486
|
+
if (requestedEvents.length > 0) {
|
|
37487
|
+
const requestedIds = new Set(
|
|
37488
|
+
requestedEvents.flatMap((event) => {
|
|
37489
|
+
const request = sessionEventPayloadRecord(event.payload).request;
|
|
37490
|
+
if (!request || typeof request !== "object" || Array.isArray(request)) return [];
|
|
37491
|
+
const id = request.id;
|
|
37492
|
+
return typeof id === "string" ? [id] : [];
|
|
37493
|
+
})
|
|
37494
|
+
);
|
|
37495
|
+
const requests = humanInputRequests.filter((request) => requestedIds.has(request.id));
|
|
37496
|
+
if (requests.length !== requestedIds.size) {
|
|
37497
|
+
throw new Error("Human-input realtime projection lost its durable request contract");
|
|
37498
|
+
}
|
|
37499
|
+
await mirrorSessionRealtimeContextInTransaction(tx, {
|
|
37500
|
+
accountId: session.accountId,
|
|
37501
|
+
workspaceId,
|
|
37502
|
+
sessionId: input.sessionId,
|
|
37503
|
+
sourceKind: "human_input_request",
|
|
37504
|
+
sourceId: requestedEvents.map((event) => event.id).join(":"),
|
|
37505
|
+
turnId: input.turnId,
|
|
37506
|
+
channel: "speakable",
|
|
37507
|
+
text: renderRealtimeHumanInputRequestContext({ requests }),
|
|
37508
|
+
payload: {
|
|
37509
|
+
status: "waiting_for_user",
|
|
37510
|
+
requestIds: requests.map((request) => request.id),
|
|
37511
|
+
sourceEventIds: requestedEvents.map((event) => event.id)
|
|
37512
|
+
},
|
|
37513
|
+
now
|
|
37514
|
+
});
|
|
37515
|
+
}
|
|
37516
|
+
const terminalHumanInputById = new Map(
|
|
37517
|
+
terminalHumanInputRows.map((request) => [request.id, request])
|
|
37518
|
+
);
|
|
37519
|
+
for (const event of inserted) {
|
|
37520
|
+
if (event.type !== "user.humanInputResponse") continue;
|
|
37521
|
+
const payload = sessionEventPayloadRecord(event.payload);
|
|
37522
|
+
const requestId = typeof payload.requestId === "string" ? payload.requestId : null;
|
|
37523
|
+
const request = requestId ? terminalHumanInputById.get(requestId) : null;
|
|
37524
|
+
if (!request) continue;
|
|
37525
|
+
await mirrorSessionRealtimeContextInTransaction(tx, {
|
|
37526
|
+
accountId: session.accountId,
|
|
37527
|
+
workspaceId,
|
|
37528
|
+
sessionId: input.sessionId,
|
|
37529
|
+
sourceKind: "human_input_response",
|
|
37530
|
+
sourceId: event.id,
|
|
37531
|
+
turnId: input.turnId,
|
|
37532
|
+
channel: null,
|
|
37533
|
+
text: renderRealtimeHumanInputResponseContext({
|
|
37534
|
+
requestId: request.id,
|
|
37535
|
+
questions: request.questions,
|
|
37536
|
+
response: { outcome: "cancelled" }
|
|
37537
|
+
}),
|
|
37538
|
+
payload: {
|
|
37539
|
+
requestId: request.id,
|
|
37540
|
+
outcome: "cancelled",
|
|
37541
|
+
sourceEventId: event.id
|
|
37542
|
+
},
|
|
37543
|
+
now
|
|
37544
|
+
});
|
|
37545
|
+
}
|
|
36724
37546
|
const terminal = isTerminalSessionTurnStatus(input.turnStatus);
|
|
36725
37547
|
if (isTerminalSessionTurnStatus(input.turnStatus)) {
|
|
36726
37548
|
const terminalType = terminalSessionTurnEventType(input.turnStatus);
|
|
@@ -37217,7 +38039,7 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
|
|
|
37217
38039
|
sessionHistoryItems.producerCodexCredentialId,
|
|
37218
38040
|
input.providerArtifactInvalidation.codexCredentialId
|
|
37219
38041
|
),
|
|
37220
|
-
|
|
38042
|
+
isNull6(sessionHistoryItems.providerArtifactInvalidatedAt),
|
|
37221
38043
|
sql14`${sessionHistoryItems.item} ->> 'type' in ('reasoning', 'compaction')`
|
|
37222
38044
|
)
|
|
37223
38045
|
).returning({ id: sessionHistoryItems.id });
|
|
@@ -37231,7 +38053,7 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
|
|
|
37231
38053
|
agentRunStates.frozenCodexCredentialId,
|
|
37232
38054
|
input.providerArtifactInvalidation.codexCredentialId
|
|
37233
38055
|
),
|
|
37234
|
-
|
|
38056
|
+
isNull6(agentRunStates.providerArtifactInvalidatedAt)
|
|
37235
38057
|
)
|
|
37236
38058
|
).orderBy(desc8(agentRunStates.stateVersion)).limit(1);
|
|
37237
38059
|
const invalidatedRunState = latestRunState ? await tx.update(agentRunStates).set({
|
|
@@ -37241,7 +38063,7 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
|
|
|
37241
38063
|
}).where(
|
|
37242
38064
|
and17(
|
|
37243
38065
|
eq17(agentRunStates.id, latestRunState.id),
|
|
37244
|
-
|
|
38066
|
+
isNull6(agentRunStates.providerArtifactInvalidatedAt)
|
|
37245
38067
|
)
|
|
37246
38068
|
).returning({ id: agentRunStates.id }) : [];
|
|
37247
38069
|
providerArtifactsInvalidated = invalidatedHistory.length + invalidatedRunState.length;
|
|
@@ -38029,7 +38851,7 @@ async function markSessionWorkflowWakeDelivered(db, input) {
|
|
|
38029
38851
|
and17(
|
|
38030
38852
|
eq17(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
38031
38853
|
eq17(sessionTurnAttempts.sessionId, input.sessionId),
|
|
38032
|
-
|
|
38854
|
+
isNull6(sessionTurnAttempts.quiescedAt),
|
|
38033
38855
|
inArray11(sessionAttemptInterruptions.state, ["settled", "rejected_stale"])
|
|
38034
38856
|
)
|
|
38035
38857
|
).limit(1);
|
|
@@ -39744,6 +40566,8 @@ export {
|
|
|
39744
40566
|
CODEX_CREDENTIAL_LEASE_TTL_MS,
|
|
39745
40567
|
CODEX_RESET_REDEMPTION_OUTCOMES,
|
|
39746
40568
|
CODEX_ROTATION_STRATEGIES,
|
|
40569
|
+
ConnectionDisconnectGenerationError,
|
|
40570
|
+
ConnectionDisconnectIdempotencyError,
|
|
39747
40571
|
ConnectionRefreshHttpError,
|
|
39748
40572
|
FORCE_RLS_TABLES,
|
|
39749
40573
|
GitHubInstallationAuthorityCommitError,
|
|
@@ -39852,6 +40676,7 @@ export {
|
|
|
39852
40676
|
WorkspaceInstructionPolicyInvalidOperationError,
|
|
39853
40677
|
WorkspaceInstructionPolicyLegacyUnavailableError,
|
|
39854
40678
|
WorkspaceInstructionPolicyNotFoundError,
|
|
40679
|
+
WorkspaceInstructionPolicyOperationReuseError,
|
|
39855
40680
|
WorkspaceInstructionPolicySnapshotAuthorityError,
|
|
39856
40681
|
abandonCodexResetRedemptionBeforeProvider,
|
|
39857
40682
|
abandonRecordingForTurnAttempt,
|
|
@@ -40053,6 +40878,8 @@ export {
|
|
|
40053
40878
|
disableHostExportConsumer,
|
|
40054
40879
|
disconnectAllCodexAccounts,
|
|
40055
40880
|
disconnectCodexAccount,
|
|
40881
|
+
disconnectConnectionIdempotently,
|
|
40882
|
+
durableUserHistoryItem,
|
|
40056
40883
|
editQueuedTurnInTransaction,
|
|
40057
40884
|
enableCapabilityInstallation,
|
|
40058
40885
|
enablePackInstallation,
|
|
@@ -40101,11 +40928,13 @@ export {
|
|
|
40101
40928
|
getCodexRotationSettings,
|
|
40102
40929
|
getComposerDraftInTransaction,
|
|
40103
40930
|
getConnectionMetadata,
|
|
40931
|
+
getCurrentPreferenceRegistryGovernanceMetadata,
|
|
40104
40932
|
getDeviceEnrollmentRequestByDeviceCode,
|
|
40105
40933
|
getEligibleKnowledgeClaim,
|
|
40106
40934
|
getEnrollment,
|
|
40107
40935
|
getFile,
|
|
40108
40936
|
getFileUpload,
|
|
40937
|
+
getFiles,
|
|
40109
40938
|
getHostExportConsumerStatus,
|
|
40110
40939
|
getHumanInputResumeForEvent,
|
|
40111
40940
|
getKnowledgeMemory,
|
|
@@ -40188,6 +41017,7 @@ export {
|
|
|
40188
41017
|
getWorkspaceInstructionPolicyRevision,
|
|
40189
41018
|
getWorkspaceModelPolicy,
|
|
40190
41019
|
getWorkspacePack,
|
|
41020
|
+
getWorkspaceStateAcceptedAttemptGovernance,
|
|
40191
41021
|
grantWorkspaceAccess,
|
|
40192
41022
|
hasAuditableGitHubInstallationAuthority,
|
|
40193
41023
|
hasCreditLedgerEntry,
|
|
@@ -40422,6 +41252,7 @@ export {
|
|
|
40422
41252
|
reserveSessionCommandReceipt,
|
|
40423
41253
|
reserveToolspaceCallForAttempt,
|
|
40424
41254
|
resolveConnectorActionPolicy,
|
|
41255
|
+
resolveDocumentIndexAuthority,
|
|
40425
41256
|
resolveSlackInstallationRoute,
|
|
40426
41257
|
resolveWorkspaceMemoryBlock,
|
|
40427
41258
|
restoreKnowledgeSourceObject,
|
|
@@ -40514,6 +41345,7 @@ export {
|
|
|
40514
41345
|
syncSessionRealtimeLedgerInTransaction,
|
|
40515
41346
|
touchEnrollmentLastSeen,
|
|
40516
41347
|
touchLeaseHolder,
|
|
41348
|
+
transitionConnectionState,
|
|
40517
41349
|
updateCodexAllocatorEligibility,
|
|
40518
41350
|
updateCodexRotationSettings,
|
|
40519
41351
|
updateConnection,
|