@opengeni/db 0.23.0 → 0.27.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-EX7CDSGH.js} +196 -2
- package/dist/chunk-EX7CDSGH.js.map +1 -0
- package/dist/{chunk-L6ADMZHE.js → chunk-IHPCI4GV.js} +5 -1
- package/dist/chunk-IHPCI4GV.js.map +1 -0
- package/dist/connection-token-resolver.d.ts +24 -2
- package/dist/index.d.ts +68 -2
- package/dist/index.js +1457 -338
- 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 +3 -3
- package/dist/schema.d.ts +262 -4
- package/dist/schema.js +5 -1
- package/dist/session-realtime-mirror.d.ts +22 -1
- package/dist/workspace-instruction-policies-schema.d.ts +449 -0
- package/dist/workspace-instruction-policies.d.ts +88 -8
- 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/drizzle/0169_workspace_instruction_policy_onboarding_proposals.sql +202 -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 +4 -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 +116 -0
- package/src/workspace-instruction-policies.ts +819 -25
- 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,
|
|
@@ -103,7 +104,9 @@ import {
|
|
|
103
104
|
workspaceInferenceControls,
|
|
104
105
|
workspaceInstructionPolicyActivationEvents,
|
|
105
106
|
workspaceInstructionPolicyHeads,
|
|
107
|
+
workspaceInstructionPolicyOnboardingProposals,
|
|
106
108
|
workspaceInstructionPolicyRevisions,
|
|
109
|
+
workspaceInstructionPolicySnapshots,
|
|
107
110
|
workspaceMemberships,
|
|
108
111
|
workspaceModelPolicies,
|
|
109
112
|
workspacePacks,
|
|
@@ -111,7 +114,7 @@ import {
|
|
|
111
114
|
workspaceVariableSetVariables,
|
|
112
115
|
workspaceVariableSets,
|
|
113
116
|
workspaces
|
|
114
|
-
} from "./chunk-
|
|
117
|
+
} from "./chunk-EX7CDSGH.js";
|
|
115
118
|
import {
|
|
116
119
|
migrate,
|
|
117
120
|
runMigrations
|
|
@@ -131,7 +134,7 @@ import {
|
|
|
131
134
|
inspectRuntimeDatabasePosture,
|
|
132
135
|
provisionRoles,
|
|
133
136
|
runtimeDatabaseReadyCheck
|
|
134
|
-
} from "./chunk-
|
|
137
|
+
} from "./chunk-IHPCI4GV.js";
|
|
135
138
|
import "./chunk-PZ5AY32C.js";
|
|
136
139
|
|
|
137
140
|
// src/index.ts
|
|
@@ -144,7 +147,8 @@ import {
|
|
|
144
147
|
canonicalModalCheckpointProviderBinding,
|
|
145
148
|
decodeNativeSnapshotRef,
|
|
146
149
|
parseWorkspaceArchiveDescriptor,
|
|
147
|
-
stableJson as stableJson3
|
|
150
|
+
stableJson as stableJson3,
|
|
151
|
+
MODEL_ATTACHMENT_REFS_FIELD
|
|
148
152
|
} from "@opengeni/contracts";
|
|
149
153
|
import {
|
|
150
154
|
approvalIdentifier,
|
|
@@ -191,17 +195,17 @@ import {
|
|
|
191
195
|
desc as desc8,
|
|
192
196
|
eq as eq17,
|
|
193
197
|
getTableColumns,
|
|
194
|
-
gt as
|
|
198
|
+
gt as gt5,
|
|
195
199
|
gte as gte2,
|
|
196
200
|
inArray as inArray11,
|
|
197
201
|
isNotNull,
|
|
198
202
|
ilike,
|
|
199
|
-
isNull as
|
|
203
|
+
isNull as isNull6,
|
|
200
204
|
lt as lt4,
|
|
201
205
|
lte as lte2,
|
|
202
206
|
ne,
|
|
203
207
|
notInArray,
|
|
204
|
-
or as
|
|
208
|
+
or as or6,
|
|
205
209
|
sql as sql14
|
|
206
210
|
} from "drizzle-orm";
|
|
207
211
|
|
|
@@ -1162,13 +1166,199 @@ async function closePendingSessionToolCallsInTransaction(tx, input) {
|
|
|
1162
1166
|
}
|
|
1163
1167
|
|
|
1164
1168
|
// src/session-control.ts
|
|
1165
|
-
import { createHash } from "crypto";
|
|
1169
|
+
import { createHash as createHash2 } from "crypto";
|
|
1166
1170
|
import {
|
|
1167
1171
|
boundWorkspaceControlEvent,
|
|
1168
1172
|
McpPersonalConnectionDelegations,
|
|
1169
1173
|
workspaceControlUtf8Bytes
|
|
1170
1174
|
} from "@opengeni/contracts";
|
|
1171
|
-
import { and as
|
|
1175
|
+
import { and as and5, eq as eq5, inArray, sql as sql3 } from "drizzle-orm";
|
|
1176
|
+
|
|
1177
|
+
// src/session-realtime-mirror.ts
|
|
1178
|
+
import { createHash } from "crypto";
|
|
1179
|
+
import { and as and4, desc, eq as eq4, gt, sql as sql2 } from "drizzle-orm";
|
|
1180
|
+
var SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES = 131072;
|
|
1181
|
+
var SESSION_REALTIME_MIRROR_MAX_PAYLOAD_BYTES = 131072;
|
|
1182
|
+
var TRUNCATION_MARKER = "\n\u2026realtime context truncated\u2026";
|
|
1183
|
+
function deterministicUuid(seed) {
|
|
1184
|
+
const bytes = createHash("sha256").update(seed, "utf8").digest().subarray(0, 16);
|
|
1185
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
1186
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
1187
|
+
const hex = bytes.toString("hex");
|
|
1188
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
1189
|
+
}
|
|
1190
|
+
function takeUtf8Head(value, maximumBytes) {
|
|
1191
|
+
const bytes = Buffer.from(value, "utf8");
|
|
1192
|
+
if (bytes.length <= maximumBytes) return value;
|
|
1193
|
+
let end = maximumBytes;
|
|
1194
|
+
while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
|
|
1195
|
+
return bytes.subarray(0, end).toString("utf8");
|
|
1196
|
+
}
|
|
1197
|
+
function boundedText(value) {
|
|
1198
|
+
if (Buffer.byteLength(value, "utf8") <= SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES) return value;
|
|
1199
|
+
const markerBytes = Buffer.byteLength(TRUNCATION_MARKER, "utf8");
|
|
1200
|
+
return `${takeUtf8Head(value, SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES - markerBytes)}${TRUNCATION_MARKER}`;
|
|
1201
|
+
}
|
|
1202
|
+
function boundedPayload(value) {
|
|
1203
|
+
const payload = sanitizeEventPayload(value);
|
|
1204
|
+
if (Buffer.byteLength(JSON.stringify(payload), "utf8") > SESSION_REALTIME_MIRROR_MAX_PAYLOAD_BYTES) {
|
|
1205
|
+
throw new Error("Realtime mirror payload exceeds the durable ledger limit");
|
|
1206
|
+
}
|
|
1207
|
+
return payload;
|
|
1208
|
+
}
|
|
1209
|
+
async function mirrorSessionRealtimeContextInTransaction(db, input) {
|
|
1210
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
1211
|
+
const modes = await db.select().from(sessionRealtimeModes).where(
|
|
1212
|
+
and4(
|
|
1213
|
+
eq4(sessionRealtimeModes.accountId, input.accountId),
|
|
1214
|
+
eq4(sessionRealtimeModes.workspaceId, input.workspaceId),
|
|
1215
|
+
eq4(sessionRealtimeModes.sessionId, input.sessionId),
|
|
1216
|
+
eq4(sessionRealtimeModes.state, "active"),
|
|
1217
|
+
gt(sessionRealtimeModes.leaseExpiresAt, now)
|
|
1218
|
+
)
|
|
1219
|
+
).orderBy(desc(sessionRealtimeModes.startedAt)).for("update").limit(2);
|
|
1220
|
+
if (modes.length === 0) return null;
|
|
1221
|
+
if (modes.length !== 1) {
|
|
1222
|
+
throw new Error(`Session ${input.sessionId} has multiple active realtime modes`);
|
|
1223
|
+
}
|
|
1224
|
+
const mode = modes[0];
|
|
1225
|
+
const operationId = deterministicUuid(
|
|
1226
|
+
`opengeni:session-realtime-mirror:${mode.id}:${input.sourceKind}:${input.sourceId}`
|
|
1227
|
+
);
|
|
1228
|
+
const [existing] = await db.select().from(sessionRealtimeEntries).where(
|
|
1229
|
+
and4(
|
|
1230
|
+
eq4(sessionRealtimeEntries.realtimeId, mode.id),
|
|
1231
|
+
eq4(sessionRealtimeEntries.operationId, operationId)
|
|
1232
|
+
)
|
|
1233
|
+
).limit(1);
|
|
1234
|
+
if (existing) return { entry: existing, replay: true };
|
|
1235
|
+
const [sequenceRow] = await db.select({ next: sql2`coalesce(max(${sessionRealtimeEntries.sequence}), 0) + 1` }).from(sessionRealtimeEntries).where(eq4(sessionRealtimeEntries.realtimeId, mode.id));
|
|
1236
|
+
const payload = boundedPayload({
|
|
1237
|
+
...input.payload ?? {},
|
|
1238
|
+
source: input.sourceKind,
|
|
1239
|
+
sourceId: input.sourceId,
|
|
1240
|
+
channel: input.channel,
|
|
1241
|
+
...input.turnId ? { sourceTurnId: input.turnId } : {}
|
|
1242
|
+
});
|
|
1243
|
+
const [entry] = await db.insert(sessionRealtimeEntries).values({
|
|
1244
|
+
accountId: input.accountId,
|
|
1245
|
+
workspaceId: input.workspaceId,
|
|
1246
|
+
sessionId: input.sessionId,
|
|
1247
|
+
realtimeId: mode.id,
|
|
1248
|
+
operationId,
|
|
1249
|
+
connectionEpoch: mode.connectionEpoch,
|
|
1250
|
+
sequence: Number(sequenceRow?.next ?? 1),
|
|
1251
|
+
direction: "provider_out",
|
|
1252
|
+
kind: "session_update",
|
|
1253
|
+
text: boundedText(input.text),
|
|
1254
|
+
payload,
|
|
1255
|
+
createdAt: now,
|
|
1256
|
+
updatedAt: now
|
|
1257
|
+
}).returning();
|
|
1258
|
+
if (!entry) throw new Error("Failed to append realtime session context");
|
|
1259
|
+
return { entry, replay: false };
|
|
1260
|
+
}
|
|
1261
|
+
function renderRealtimeHumanInputContext(input) {
|
|
1262
|
+
return [
|
|
1263
|
+
"<session_user_message>",
|
|
1264
|
+
` <status>${input.routing}</status>`,
|
|
1265
|
+
` <delivery>${input.delivery}</delivery>`,
|
|
1266
|
+
` <text>${escapeXmlText(input.text)}</text>`,
|
|
1267
|
+
" <instruction>Already routed by OpenGeni; do not delegate this message again.</instruction>",
|
|
1268
|
+
"</session_user_message>"
|
|
1269
|
+
].join("\n");
|
|
1270
|
+
}
|
|
1271
|
+
function renderRealtimeHumanInputRequestContext(input) {
|
|
1272
|
+
const lines = ["<session_human_input_request>", " <status>waiting_for_user</status>"];
|
|
1273
|
+
for (const request of input.requests) {
|
|
1274
|
+
lines.push(
|
|
1275
|
+
" <request>",
|
|
1276
|
+
` <id>${escapeXmlText(request.id)}</id>`,
|
|
1277
|
+
` <allow_skip>${String(request.allowSkip)}</allow_skip>`,
|
|
1278
|
+
` <expires_at>${escapeXmlText(renderExpiry(request.expiresAt))}</expires_at>`
|
|
1279
|
+
);
|
|
1280
|
+
for (const question of request.questions) {
|
|
1281
|
+
lines.push(
|
|
1282
|
+
" <question>",
|
|
1283
|
+
` <id>${escapeXmlText(question.id)}</id>`,
|
|
1284
|
+
` <kind>${escapeXmlText(question.kind)}</kind>`,
|
|
1285
|
+
` <required>${String(question.required)}</required>`
|
|
1286
|
+
);
|
|
1287
|
+
if (question.label) lines.push(` <label>${escapeXmlText(question.label)}</label>`);
|
|
1288
|
+
lines.push(` <prompt>${escapeXmlText(question.prompt)}</prompt>`);
|
|
1289
|
+
if (question.helpText) {
|
|
1290
|
+
lines.push(` <help_text>${escapeXmlText(question.helpText)}</help_text>`);
|
|
1291
|
+
}
|
|
1292
|
+
if (question.options.length > 0) {
|
|
1293
|
+
lines.push(" <options>");
|
|
1294
|
+
for (const option of question.options) {
|
|
1295
|
+
lines.push(
|
|
1296
|
+
" <option>",
|
|
1297
|
+
` <id>${escapeXmlText(option.id)}</id>`,
|
|
1298
|
+
` <label>${escapeXmlText(option.label)}</label>`
|
|
1299
|
+
);
|
|
1300
|
+
if (option.description) {
|
|
1301
|
+
lines.push(` <description>${escapeXmlText(option.description)}</description>`);
|
|
1302
|
+
}
|
|
1303
|
+
lines.push(" </option>");
|
|
1304
|
+
}
|
|
1305
|
+
lines.push(" </options>");
|
|
1306
|
+
}
|
|
1307
|
+
lines.push(
|
|
1308
|
+
` <allow_other>${String(question.allowOther)}</allow_other>`,
|
|
1309
|
+
" </question>"
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
lines.push(" </request>");
|
|
1313
|
+
}
|
|
1314
|
+
lines.push(
|
|
1315
|
+
" <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>",
|
|
1316
|
+
"</session_human_input_request>"
|
|
1317
|
+
);
|
|
1318
|
+
return lines.join("\n");
|
|
1319
|
+
}
|
|
1320
|
+
function renderRealtimeHumanInputResponseContext(input) {
|
|
1321
|
+
const lines = [
|
|
1322
|
+
"<session_human_input_response>",
|
|
1323
|
+
` <request_id>${escapeXmlText(input.requestId)}</request_id>`,
|
|
1324
|
+
` <outcome>${escapeXmlText(input.response.outcome)}</outcome>`
|
|
1325
|
+
];
|
|
1326
|
+
if (input.response.outcome === "answered") {
|
|
1327
|
+
const questions = new Map(input.questions.map((question) => [question.id, question]));
|
|
1328
|
+
lines.push(" <answers>");
|
|
1329
|
+
for (const answer of input.response.answers) {
|
|
1330
|
+
const question = questions.get(answer.questionId);
|
|
1331
|
+
const optionLabels = new Map(
|
|
1332
|
+
question?.options.map((option) => [option.id, option.label]) ?? []
|
|
1333
|
+
);
|
|
1334
|
+
lines.push(
|
|
1335
|
+
" <answer>",
|
|
1336
|
+
` <question_id>${escapeXmlText(answer.questionId)}</question_id>`
|
|
1337
|
+
);
|
|
1338
|
+
if (question) lines.push(` <question>${escapeXmlText(question.prompt)}</question>`);
|
|
1339
|
+
for (const value of answer.values) {
|
|
1340
|
+
lines.push(` <value>${escapeXmlText(optionLabels.get(value) ?? value)}</value>`);
|
|
1341
|
+
}
|
|
1342
|
+
if (answer.other) lines.push(` <other>${escapeXmlText(answer.other)}</other>`);
|
|
1343
|
+
lines.push(" </answer>");
|
|
1344
|
+
}
|
|
1345
|
+
lines.push(" </answers>");
|
|
1346
|
+
}
|
|
1347
|
+
lines.push(
|
|
1348
|
+
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>",
|
|
1349
|
+
"</session_human_input_response>"
|
|
1350
|
+
);
|
|
1351
|
+
return lines.join("\n");
|
|
1352
|
+
}
|
|
1353
|
+
function renderExpiry(value) {
|
|
1354
|
+
if (value === null || value === void 0) return "none";
|
|
1355
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
1356
|
+
}
|
|
1357
|
+
function escapeXmlText(value) {
|
|
1358
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/session-control.ts
|
|
1172
1362
|
var SESSION_ANCESTRY_LIMIT = 1e4;
|
|
1173
1363
|
var NO_SETTLEMENT_ATTEMPTS = {
|
|
1174
1364
|
attemptCount: 0,
|
|
@@ -1239,7 +1429,7 @@ async function assertAgentCommandAuthorityInTransaction(db, input) {
|
|
|
1239
1429
|
if (!lockedSessions.some((row) => row.id === input.targetSessionId)) {
|
|
1240
1430
|
throw new SessionControlInvariantError(`Target session not found: ${input.targetSessionId}`);
|
|
1241
1431
|
}
|
|
1242
|
-
const rows = await db.execute(
|
|
1432
|
+
const rows = await db.execute(sql3`
|
|
1243
1433
|
select
|
|
1244
1434
|
attempt.id as "attemptId",
|
|
1245
1435
|
attempt.state as "attemptState",
|
|
@@ -1281,7 +1471,7 @@ async function assertAgentCommandAuthorityInTransaction(db, input) {
|
|
|
1281
1471
|
throw new AgentCommandAuthorityError("SELF_STEER", "An agent cannot steer its own session");
|
|
1282
1472
|
}
|
|
1283
1473
|
if (input.action !== "pause") return;
|
|
1284
|
-
const ancestry = await db.execute(
|
|
1474
|
+
const ancestry = await db.execute(sql3`
|
|
1285
1475
|
with recursive caller_ancestry(id, parent_id, depth, path, cycle) as (
|
|
1286
1476
|
select session.id, session.parent_session_id, 0, array[session.id], false
|
|
1287
1477
|
from ${sessions} session
|
|
@@ -1333,17 +1523,17 @@ function canonicalize(value) {
|
|
|
1333
1523
|
return value;
|
|
1334
1524
|
}
|
|
1335
1525
|
function canonicalSessionCommandHash(value) {
|
|
1336
|
-
return
|
|
1526
|
+
return createHash2("sha256").update(JSON.stringify(canonicalize(value))).digest("hex");
|
|
1337
1527
|
}
|
|
1338
1528
|
function controlEtag(value) {
|
|
1339
1529
|
return `sc1:${canonicalSessionCommandHash(value)}`;
|
|
1340
1530
|
}
|
|
1341
1531
|
function lockClause(mode) {
|
|
1342
|
-
if (mode === "none") return
|
|
1343
|
-
return mode === "update" ?
|
|
1532
|
+
if (mode === "none") return sql3.empty();
|
|
1533
|
+
return mode === "update" ? sql3.raw("for update") : sql3.raw("for share");
|
|
1344
1534
|
}
|
|
1345
1535
|
async function lockWorkspaceInferenceControl(db, workspaceId, mode) {
|
|
1346
|
-
const rows = await db.execute(
|
|
1536
|
+
const rows = await db.execute(sql3`
|
|
1347
1537
|
select
|
|
1348
1538
|
workspace_id as "workspaceId",
|
|
1349
1539
|
account_id as "accountId",
|
|
@@ -1370,27 +1560,27 @@ async function lockSessionEventWriteRows(db, input) {
|
|
|
1370
1560
|
const control = controlLock === "share" || controlLock === "update" ? await lockWorkspaceInferenceControl(db, input.workspaceId, controlLock) : null;
|
|
1371
1561
|
let workspace = null;
|
|
1372
1562
|
if ((input.workspaceLock ?? "key_share") === "key_share") {
|
|
1373
|
-
const [lockedWorkspace] = await db.select().from(workspaces).where(
|
|
1563
|
+
const [lockedWorkspace] = await db.select().from(workspaces).where(eq5(workspaces.id, input.workspaceId)).for("key share").limit(1);
|
|
1374
1564
|
workspace = lockedWorkspace ?? null;
|
|
1375
1565
|
}
|
|
1376
1566
|
const sessionIds = [...new Set(input.sessionIds ?? [])].sort();
|
|
1377
1567
|
const sessions2 = sessionIds.length > 0 ? await db.select().from(sessions).where(
|
|
1378
|
-
|
|
1379
|
-
|
|
1568
|
+
and5(
|
|
1569
|
+
eq5(sessions.workspaceId, input.workspaceId),
|
|
1380
1570
|
inArray(sessions.id, sessionIds)
|
|
1381
1571
|
)
|
|
1382
1572
|
).orderBy(sessions.id).for("update") : [];
|
|
1383
1573
|
const turnIds = [...new Set(input.turnIds ?? [])].sort();
|
|
1384
1574
|
const turns = turnIds.length > 0 ? await db.select().from(sessionTurns).where(
|
|
1385
|
-
|
|
1386
|
-
|
|
1575
|
+
and5(
|
|
1576
|
+
eq5(sessionTurns.workspaceId, input.workspaceId),
|
|
1387
1577
|
inArray(sessionTurns.id, turnIds)
|
|
1388
1578
|
)
|
|
1389
1579
|
).orderBy(sessionTurns.id).for("update") : [];
|
|
1390
1580
|
const attemptIds = [...new Set(input.attemptIds ?? [])].sort();
|
|
1391
1581
|
const attempts = attemptIds.length > 0 ? await db.select().from(sessionTurnAttempts).where(
|
|
1392
|
-
|
|
1393
|
-
|
|
1582
|
+
and5(
|
|
1583
|
+
eq5(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
1394
1584
|
inArray(sessionTurnAttempts.id, attemptIds)
|
|
1395
1585
|
)
|
|
1396
1586
|
).orderBy(sessionTurnAttempts.id).for("update") : [];
|
|
@@ -1403,9 +1593,9 @@ async function registerSessionTurnAttemptClaim(db, input) {
|
|
|
1403
1593
|
}).onConflictDoNothing({ target: sessionTurnAttempts.id }).returning();
|
|
1404
1594
|
if (inserted) return inserted;
|
|
1405
1595
|
const [existing] = await db.select().from(sessionTurnAttempts).where(
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1596
|
+
and5(
|
|
1597
|
+
eq5(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
1598
|
+
eq5(sessionTurnAttempts.id, input.id)
|
|
1409
1599
|
)
|
|
1410
1600
|
).for("update").limit(1);
|
|
1411
1601
|
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 +1607,9 @@ async function registerSessionTurnAttemptClaim(db, input) {
|
|
|
1417
1607
|
}
|
|
1418
1608
|
async function closeSessionTurnAttemptInTransaction(db, input) {
|
|
1419
1609
|
const [attempt] = await db.select().from(sessionTurnAttempts).where(
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1610
|
+
and5(
|
|
1611
|
+
eq5(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
1612
|
+
eq5(sessionTurnAttempts.id, input.id)
|
|
1423
1613
|
)
|
|
1424
1614
|
).for("update").limit(1);
|
|
1425
1615
|
if (!attempt || attempt.accountId !== input.accountId || attempt.sessionId !== input.sessionId || attempt.turnId !== input.turnId || attempt.executionGeneration !== input.executionGeneration) {
|
|
@@ -1450,10 +1640,10 @@ async function closeSessionTurnAttemptInTransaction(db, input) {
|
|
|
1450
1640
|
closedAt: now,
|
|
1451
1641
|
updatedAt: now
|
|
1452
1642
|
}).where(
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1643
|
+
and5(
|
|
1644
|
+
eq5(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
1645
|
+
eq5(sessionTurnAttempts.id, input.id),
|
|
1646
|
+
sql3`${sessionTurnAttempts.state} in ('claimed', 'running')`
|
|
1457
1647
|
)
|
|
1458
1648
|
).returning({ id: sessionTurnAttempts.id });
|
|
1459
1649
|
if (!closed) {
|
|
@@ -1465,14 +1655,14 @@ function targetValues(sessionIds) {
|
|
|
1465
1655
|
if (sessionIds.length === 0) {
|
|
1466
1656
|
throw new SessionControlInvariantError("At least one target session is required");
|
|
1467
1657
|
}
|
|
1468
|
-
return
|
|
1469
|
-
sessionIds.map((id) =>
|
|
1470
|
-
|
|
1658
|
+
return sql3.join(
|
|
1659
|
+
sessionIds.map((id) => sql3`(${id}::uuid)`),
|
|
1660
|
+
sql3`, `
|
|
1471
1661
|
);
|
|
1472
1662
|
}
|
|
1473
1663
|
var TARGET_PATH_PROJECTION_LIMIT = 128;
|
|
1474
1664
|
async function loadTargetAncestryRows(db, workspaceId, sessionIds) {
|
|
1475
|
-
return await db.execute(
|
|
1665
|
+
return await db.execute(sql3`
|
|
1476
1666
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1477
1667
|
ancestry as (
|
|
1478
1668
|
select
|
|
@@ -1535,7 +1725,7 @@ async function loadTargetAncestryRows(db, workspaceId, sessionIds) {
|
|
|
1535
1725
|
`);
|
|
1536
1726
|
}
|
|
1537
1727
|
async function loadAncestryNodes(db, workspaceId, sessionIds) {
|
|
1538
|
-
const rows = await db.execute(
|
|
1728
|
+
const rows = await db.execute(sql3`
|
|
1539
1729
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1540
1730
|
ancestry_ids(id) as (
|
|
1541
1731
|
select session.id
|
|
@@ -1728,7 +1918,7 @@ function projectEffectiveControl(workspace, targetId, rows, settlementAttempts)
|
|
|
1728
1918
|
};
|
|
1729
1919
|
}
|
|
1730
1920
|
async function settlementAttemptCounts(db, workspaceId, sessionIds) {
|
|
1731
|
-
const rows = await db.execute(
|
|
1921
|
+
const rows = await db.execute(sql3`
|
|
1732
1922
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1733
1923
|
interruptions as (
|
|
1734
1924
|
select
|
|
@@ -1875,7 +2065,7 @@ async function evaluateSessionDiscoveryControls(db, workspaceId, sessionIds) {
|
|
|
1875
2065
|
if (workspace.workspaceState === "paused" && workspacePauseRevision === null) {
|
|
1876
2066
|
throw new SessionControlInvariantError("Paused workspace is missing its pause revision");
|
|
1877
2067
|
}
|
|
1878
|
-
const rows = await db.execute(
|
|
2068
|
+
const rows = await db.execute(sql3`
|
|
1879
2069
|
with recursive targets(id) as (values ${targetValues(uniqueIds)}),
|
|
1880
2070
|
ancestry as (
|
|
1881
2071
|
select
|
|
@@ -2042,22 +2232,22 @@ async function evaluateSessionControl(db, workspaceId, sessionId, options = {})
|
|
|
2042
2232
|
async function findCommandReceipt(db, input) {
|
|
2043
2233
|
const actorSubjectId = input.actor.type === "agent_attempt" ? null : input.actor.subjectId;
|
|
2044
2234
|
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
|
-
|
|
2235
|
+
const identity = input.identityScope === "goal_operation" ? and5(
|
|
2236
|
+
eq5(sessionCommandReceipts.workspaceId, input.workspaceId),
|
|
2237
|
+
eq5(sessionCommandReceipts.actorType, "agent_attempt"),
|
|
2238
|
+
eq5(sessionCommandReceipts.action, input.action),
|
|
2239
|
+
eq5(sessionCommandReceipts.targetSessionId, input.targetSessionId),
|
|
2240
|
+
sql3`${sessionCommandReceipts.targetTurnId} is null`,
|
|
2241
|
+
eq5(sessionCommandReceipts.operationKey, input.operationKey)
|
|
2242
|
+
) : and5(
|
|
2243
|
+
eq5(sessionCommandReceipts.workspaceId, input.workspaceId),
|
|
2244
|
+
eq5(sessionCommandReceipts.actorType, input.actor.type),
|
|
2245
|
+
sql3`${sessionCommandReceipts.actorSubjectId} is not distinct from ${actorSubjectId}`,
|
|
2246
|
+
sql3`${sessionCommandReceipts.actorAttemptId} is not distinct from ${actorAttemptId}::uuid`,
|
|
2247
|
+
eq5(sessionCommandReceipts.action, input.action),
|
|
2248
|
+
sql3`${sessionCommandReceipts.targetSessionId} is not distinct from ${input.targetSessionId}::uuid`,
|
|
2249
|
+
sql3`${sessionCommandReceipts.targetTurnId} is not distinct from ${input.targetTurnId}::uuid`,
|
|
2250
|
+
eq5(sessionCommandReceipts.operationKey, input.operationKey)
|
|
2061
2251
|
);
|
|
2062
2252
|
const rows = await db.select().from(sessionCommandReceipts).where(identity).for("update");
|
|
2063
2253
|
return rows[0] ?? null;
|
|
@@ -2108,9 +2298,9 @@ function nextRevision(workspace) {
|
|
|
2108
2298
|
}
|
|
2109
2299
|
async function advanceWorkspaceRevision(db, workspaceId, revision) {
|
|
2110
2300
|
const rows = await db.update(workspaceInferenceControls).set({ revision, updatedAt: /* @__PURE__ */ new Date() }).where(
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2301
|
+
and5(
|
|
2302
|
+
eq5(workspaceInferenceControls.workspaceId, workspaceId),
|
|
2303
|
+
eq5(workspaceInferenceControls.revision, revision - 1)
|
|
2114
2304
|
)
|
|
2115
2305
|
).returning({ workspaceId: workspaceInferenceControls.workspaceId });
|
|
2116
2306
|
if (rows.length !== 1) {
|
|
@@ -2120,7 +2310,7 @@ async function advanceWorkspaceRevision(db, workspaceId, revision) {
|
|
|
2120
2310
|
}
|
|
2121
2311
|
}
|
|
2122
2312
|
async function registerContinuableWakes(db, input) {
|
|
2123
|
-
const rows = await db.execute(
|
|
2313
|
+
const rows = await db.execute(sql3`
|
|
2124
2314
|
with eligible as (
|
|
2125
2315
|
select *
|
|
2126
2316
|
from opengeni_private.list_continuable_sessions(
|
|
@@ -2162,7 +2352,7 @@ async function registerWorkspaceWakes(db, input) {
|
|
|
2162
2352
|
});
|
|
2163
2353
|
}
|
|
2164
2354
|
async function registerInterruptionWakes(db, input) {
|
|
2165
|
-
const rows = await db.execute(
|
|
2355
|
+
const rows = await db.execute(sql3`
|
|
2166
2356
|
with eligible as (
|
|
2167
2357
|
select distinct
|
|
2168
2358
|
session.id as session_id,
|
|
@@ -2198,11 +2388,11 @@ async function registerInterruptionWakes(db, input) {
|
|
|
2198
2388
|
}
|
|
2199
2389
|
async function registerCancellationWakes(db, input) {
|
|
2200
2390
|
if (input.sessionIds.length === 0) return 0;
|
|
2201
|
-
const sessionIds =
|
|
2202
|
-
input.sessionIds.map((id) =>
|
|
2203
|
-
|
|
2391
|
+
const sessionIds = sql3.join(
|
|
2392
|
+
input.sessionIds.map((id) => sql3`${id}::uuid`),
|
|
2393
|
+
sql3`, `
|
|
2204
2394
|
);
|
|
2205
|
-
const rows = await db.execute(
|
|
2395
|
+
const rows = await db.execute(sql3`
|
|
2206
2396
|
with eligible as (
|
|
2207
2397
|
select
|
|
2208
2398
|
session.id as session_id,
|
|
@@ -2244,7 +2434,7 @@ async function registerSessionWorkflowWakeInTransaction(db, input) {
|
|
|
2244
2434
|
target: sessionWorkflowWakeOutbox.sessionId,
|
|
2245
2435
|
set: {
|
|
2246
2436
|
temporalWorkflowId: input.temporalWorkflowId,
|
|
2247
|
-
wakeRevision:
|
|
2437
|
+
wakeRevision: sql3`${sessionWorkflowWakeOutbox.wakeRevision} + 1`,
|
|
2248
2438
|
reason: input.reason,
|
|
2249
2439
|
attempts: 0,
|
|
2250
2440
|
nextAttemptAt: /* @__PURE__ */ new Date(),
|
|
@@ -2259,9 +2449,9 @@ async function registerSessionWorkflowWakeInTransaction(db, input) {
|
|
|
2259
2449
|
}
|
|
2260
2450
|
async function registerInternalUpdateWakeInTransaction(db, input) {
|
|
2261
2451
|
const [existing] = await db.select().from(sessionWorkflowWakeOutbox).where(
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2452
|
+
and5(
|
|
2453
|
+
eq5(sessionWorkflowWakeOutbox.workspaceId, input.workspaceId),
|
|
2454
|
+
eq5(sessionWorkflowWakeOutbox.sessionId, input.sessionId)
|
|
2265
2455
|
)
|
|
2266
2456
|
).for("update").limit(1);
|
|
2267
2457
|
if (existing && existing.wakeRevision > existing.deliveredRevision) {
|
|
@@ -2271,7 +2461,7 @@ async function registerInternalUpdateWakeInTransaction(db, input) {
|
|
|
2271
2461
|
nextAttemptAt: /* @__PURE__ */ new Date(),
|
|
2272
2462
|
lastError: null,
|
|
2273
2463
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2274
|
-
}).where(
|
|
2464
|
+
}).where(eq5(sessionWorkflowWakeOutbox.sessionId, input.sessionId));
|
|
2275
2465
|
return { wakeRevision: existing.wakeRevision, shouldSignal: false };
|
|
2276
2466
|
}
|
|
2277
2467
|
return {
|
|
@@ -2283,7 +2473,7 @@ async function registerInternalUpdateWakeInTransaction(db, input) {
|
|
|
2283
2473
|
};
|
|
2284
2474
|
}
|
|
2285
2475
|
async function interruptDescendantAttempts(db, input) {
|
|
2286
|
-
const rows = await db.execute(
|
|
2476
|
+
const rows = await db.execute(sql3`
|
|
2287
2477
|
with recursive live_attempts as (
|
|
2288
2478
|
select attempt.id, attempt.account_id, attempt.workspace_id, attempt.session_id
|
|
2289
2479
|
from ${sessionTurnAttempts} attempt
|
|
@@ -2331,7 +2521,7 @@ async function interruptDescendantAttempts(db, input) {
|
|
|
2331
2521
|
return Number(rows[0]?.count ?? 0);
|
|
2332
2522
|
}
|
|
2333
2523
|
async function interruptWorkspaceAttempts(db, input) {
|
|
2334
|
-
const rows = await db.execute(
|
|
2524
|
+
const rows = await db.execute(sql3`
|
|
2335
2525
|
with inserted as (
|
|
2336
2526
|
insert into ${sessionAttemptInterruptions} (
|
|
2337
2527
|
account_id, workspace_id, session_id, operation_id, attempt_id,
|
|
@@ -2358,12 +2548,12 @@ async function updateSessionCommandReceiptResult(db, receiptId, input) {
|
|
|
2358
2548
|
...input.draftRevision !== void 0 ? { appliedDraftRevision: input.draftRevision } : {},
|
|
2359
2549
|
result: input.result,
|
|
2360
2550
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2361
|
-
}).where(
|
|
2551
|
+
}).where(eq5(sessionCommandReceipts.id, receiptId)).returning();
|
|
2362
2552
|
if (!receipt) throw new SessionControlInvariantError("Command receipt disappeared");
|
|
2363
2553
|
return receipt;
|
|
2364
2554
|
}
|
|
2365
2555
|
async function loadSessionSubtreeIds(db, workspaceId, rootSessionId) {
|
|
2366
|
-
const rows = await db.execute(
|
|
2556
|
+
const rows = await db.execute(sql3`
|
|
2367
2557
|
with recursive subtree(id, root_parent_session_id, depth, path, cycle) as (
|
|
2368
2558
|
select session.id, session.parent_session_id, 0::integer, array[session.id]::uuid[], false
|
|
2369
2559
|
from ${sessions} session
|
|
@@ -2400,8 +2590,8 @@ var TERMINAL_CANCELLATION_LIVE_TURN_STATUSES = [
|
|
|
2400
2590
|
];
|
|
2401
2591
|
async function loadCancellationTurnIds(db, workspaceId, sessionIds) {
|
|
2402
2592
|
const rows = await db.select({ id: sessionTurns.id }).from(sessionTurns).where(
|
|
2403
|
-
|
|
2404
|
-
|
|
2593
|
+
and5(
|
|
2594
|
+
eq5(sessionTurns.workspaceId, workspaceId),
|
|
2405
2595
|
inArray(sessionTurns.sessionId, sessionIds),
|
|
2406
2596
|
inArray(sessionTurns.status, TERMINAL_CANCELLATION_LIVE_TURN_STATUSES)
|
|
2407
2597
|
)
|
|
@@ -2420,10 +2610,10 @@ async function enqueueCancelledChildOutboxInTransaction(db, input) {
|
|
|
2420
2610
|
let personalConnectionDelegations = [];
|
|
2421
2611
|
if (input.rootSession.parentTurnId) {
|
|
2422
2612
|
const [parentTurn] = await db.select({ delegations: sessionTurns.personalConnectionDelegations }).from(sessionTurns).where(
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2613
|
+
and5(
|
|
2614
|
+
eq5(sessionTurns.workspaceId, input.workspaceId),
|
|
2615
|
+
eq5(sessionTurns.sessionId, parentSessionId),
|
|
2616
|
+
eq5(sessionTurns.id, input.rootSession.parentTurnId)
|
|
2427
2617
|
)
|
|
2428
2618
|
).limit(1);
|
|
2429
2619
|
if (parentTurn) {
|
|
@@ -2465,7 +2655,7 @@ async function enqueueCancelledChildOutboxInTransaction(db, input) {
|
|
|
2465
2655
|
});
|
|
2466
2656
|
}
|
|
2467
2657
|
async function assertSessionBranchIsNotCancelled(db, workspaceId, sessionId) {
|
|
2468
|
-
const rows = await db.execute(
|
|
2658
|
+
const rows = await db.execute(sql3`
|
|
2469
2659
|
with recursive ancestry(id, parent_id, status, depth, path, cycle) as (
|
|
2470
2660
|
select session.id, session.parent_session_id, session.status, 0::integer,
|
|
2471
2661
|
array[session.id]::uuid[], false
|
|
@@ -2540,7 +2730,7 @@ async function cancelSessionSubtreeInTransaction(db, input) {
|
|
|
2540
2730
|
activeAttemptId: null,
|
|
2541
2731
|
cancelledBy: input.actor,
|
|
2542
2732
|
cancelReason: input.reason ?? "session_cancelled",
|
|
2543
|
-
version:
|
|
2733
|
+
version: sql3`${sessionTurns.version} + 1`,
|
|
2544
2734
|
finishedAt: now,
|
|
2545
2735
|
updatedAt: now
|
|
2546
2736
|
}).where(inArray(sessionTurns.id, immediatelyCancelledTurnIds));
|
|
@@ -2551,29 +2741,30 @@ async function cancelSessionSubtreeInTransaction(db, input) {
|
|
|
2551
2741
|
respondedAt: now,
|
|
2552
2742
|
updatedAt: now
|
|
2553
2743
|
}).where(
|
|
2554
|
-
|
|
2555
|
-
|
|
2744
|
+
and5(
|
|
2745
|
+
eq5(sessionHumanInputRequests.workspaceId, input.workspaceId),
|
|
2556
2746
|
inArray(sessionHumanInputRequests.turnId, immediatelyCancelledTurnIds),
|
|
2557
|
-
|
|
2747
|
+
eq5(sessionHumanInputRequests.status, "pending")
|
|
2558
2748
|
)
|
|
2559
2749
|
).returning({
|
|
2560
2750
|
id: sessionHumanInputRequests.id,
|
|
2561
2751
|
sessionId: sessionHumanInputRequests.sessionId,
|
|
2562
|
-
turnId: sessionHumanInputRequests.turnId
|
|
2752
|
+
turnId: sessionHumanInputRequests.turnId,
|
|
2753
|
+
questions: sessionHumanInputRequests.questions
|
|
2563
2754
|
});
|
|
2564
2755
|
await db.update(codexCapacityWaiters).set({ status: "superseded", lastWakeReason: "session_cancelled", updatedAt: now }).where(
|
|
2565
|
-
|
|
2566
|
-
|
|
2756
|
+
and5(
|
|
2757
|
+
eq5(codexCapacityWaiters.workspaceId, input.workspaceId),
|
|
2567
2758
|
inArray(codexCapacityWaiters.blockedTurnId, immediatelyCancelledTurnIds),
|
|
2568
|
-
|
|
2759
|
+
eq5(codexCapacityWaiters.status, "waiting")
|
|
2569
2760
|
)
|
|
2570
2761
|
);
|
|
2571
2762
|
}
|
|
2572
2763
|
const cancelledSystemUpdates = await db.update(sessionSystemUpdates).set({ state: "cancelled" }).where(
|
|
2573
|
-
|
|
2574
|
-
|
|
2764
|
+
and5(
|
|
2765
|
+
eq5(sessionSystemUpdates.workspaceId, input.workspaceId),
|
|
2575
2766
|
inArray(sessionSystemUpdates.sessionId, input.sessionIds),
|
|
2576
|
-
|
|
2767
|
+
eq5(sessionSystemUpdates.state, "pending")
|
|
2577
2768
|
)
|
|
2578
2769
|
).returning({
|
|
2579
2770
|
id: sessionSystemUpdates.id,
|
|
@@ -2674,19 +2865,51 @@ async function cancelSessionSubtreeInTransaction(db, input) {
|
|
|
2674
2865
|
}
|
|
2675
2866
|
const inserted = eventValues.length > 0 ? await db.insert(sessionEvents).values(eventValues).returning({
|
|
2676
2867
|
id: sessionEvents.id,
|
|
2677
|
-
sequence: sessionEvents.sequence
|
|
2868
|
+
sequence: sessionEvents.sequence,
|
|
2869
|
+
type: sessionEvents.type,
|
|
2870
|
+
turnId: sessionEvents.turnId,
|
|
2871
|
+
payload: sessionEvents.payload
|
|
2678
2872
|
}) : [];
|
|
2873
|
+
const cancelledHumanInputsById = new Map(
|
|
2874
|
+
cancelledHumanInputs.filter((request) => request.sessionId === session.id).map((request) => [request.id, request])
|
|
2875
|
+
);
|
|
2876
|
+
for (const event of inserted) {
|
|
2877
|
+
if (event.type !== "user.humanInputResponse") continue;
|
|
2878
|
+
const payload = event.payload;
|
|
2879
|
+
const request = typeof payload.requestId === "string" ? cancelledHumanInputsById.get(payload.requestId) : null;
|
|
2880
|
+
if (!request) continue;
|
|
2881
|
+
await mirrorSessionRealtimeContextInTransaction(db, {
|
|
2882
|
+
accountId: input.accountId,
|
|
2883
|
+
workspaceId: input.workspaceId,
|
|
2884
|
+
sessionId: session.id,
|
|
2885
|
+
sourceKind: "human_input_response",
|
|
2886
|
+
sourceId: event.id,
|
|
2887
|
+
turnId: event.turnId,
|
|
2888
|
+
channel: null,
|
|
2889
|
+
text: renderRealtimeHumanInputResponseContext({
|
|
2890
|
+
requestId: request.id,
|
|
2891
|
+
questions: request.questions,
|
|
2892
|
+
response: { outcome: "cancelled" }
|
|
2893
|
+
}),
|
|
2894
|
+
payload: {
|
|
2895
|
+
requestId: request.id,
|
|
2896
|
+
outcome: "cancelled",
|
|
2897
|
+
sourceEventId: event.id
|
|
2898
|
+
},
|
|
2899
|
+
now
|
|
2900
|
+
});
|
|
2901
|
+
}
|
|
2679
2902
|
const liveTurnId = session.activeTurnId && liveTurnIds.has(session.activeTurnId) ? session.activeTurnId : null;
|
|
2680
2903
|
await db.update(sessions).set({
|
|
2681
2904
|
status: "cancelled",
|
|
2682
2905
|
activeTurnId: liveTurnId,
|
|
2683
|
-
queueVersion:
|
|
2906
|
+
queueVersion: sql3`${sessions.queueVersion} + 1`,
|
|
2684
2907
|
queueHeadPosition: 0,
|
|
2685
2908
|
queueTailPosition: 0,
|
|
2686
2909
|
lastSequence: sequence,
|
|
2687
2910
|
updatedAt: now
|
|
2688
2911
|
}).where(
|
|
2689
|
-
|
|
2912
|
+
and5(eq5(sessions.workspaceId, input.workspaceId), eq5(sessions.id, session.id))
|
|
2690
2913
|
);
|
|
2691
2914
|
const eventIds = [...preinsertedEvents, ...inserted].sort((left, right) => left.sequence - right.sequence).map((event) => event.id);
|
|
2692
2915
|
if (session.id === input.rootSessionId) eventIds.unshift(input.rootControlEventId);
|
|
@@ -2810,9 +3033,9 @@ async function mutateSessionControlInTransaction(db, input) {
|
|
|
2810
3033
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2811
3034
|
}
|
|
2812
3035
|
).where(
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
input.action === "cancel" ? inArray(sessions.id, cancellationSessionIds) :
|
|
3036
|
+
and5(
|
|
3037
|
+
eq5(sessions.workspaceId, input.workspaceId),
|
|
3038
|
+
input.action === "cancel" ? inArray(sessions.id, cancellationSessionIds) : eq5(sessions.id, input.sessionId)
|
|
2816
3039
|
)
|
|
2817
3040
|
).returning({
|
|
2818
3041
|
id: sessions.id,
|
|
@@ -2869,7 +3092,7 @@ async function mutateSessionControlInTransaction(db, input) {
|
|
|
2869
3092
|
}).returning({ id: sessionEvents.id });
|
|
2870
3093
|
if (!controlEvent)
|
|
2871
3094
|
throw new SessionControlInvariantError("Session control event was not inserted");
|
|
2872
|
-
await db.update(sessions).set({ lastSequence: updated.lastSequence + 1, updatedAt: /* @__PURE__ */ new Date() }).where(
|
|
3095
|
+
await db.update(sessions).set({ lastSequence: updated.lastSequence + 1, updatedAt: /* @__PURE__ */ new Date() }).where(eq5(sessions.id, input.sessionId));
|
|
2873
3096
|
await db.insert(auditEvents).values({
|
|
2874
3097
|
accountId: input.accountId,
|
|
2875
3098
|
workspaceId: input.workspaceId,
|
|
@@ -2963,9 +3186,9 @@ async function autoResumeSessionBranchInTransaction(db, input) {
|
|
|
2963
3186
|
directControlChangedAt: /* @__PURE__ */ new Date(),
|
|
2964
3187
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2965
3188
|
}).where(
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
3189
|
+
and5(
|
|
3190
|
+
eq5(sessions.workspaceId, input.workspaceId),
|
|
3191
|
+
eq5(sessions.id, input.sessionId)
|
|
2969
3192
|
)
|
|
2970
3193
|
);
|
|
2971
3194
|
const workspaceControlEventId = await insertWorkspaceControlEventInTransaction(db, {
|
|
@@ -3035,9 +3258,9 @@ async function mutateWorkspaceControlInTransaction(db, input) {
|
|
|
3035
3258
|
changedAt: /* @__PURE__ */ new Date(),
|
|
3036
3259
|
updatedAt: /* @__PURE__ */ new Date()
|
|
3037
3260
|
}).where(
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3261
|
+
and5(
|
|
3262
|
+
eq5(workspaceInferenceControls.workspaceId, input.workspaceId),
|
|
3263
|
+
eq5(workspaceInferenceControls.revision, currentRevision)
|
|
3041
3264
|
)
|
|
3042
3265
|
).returning({ workspaceId: workspaceInferenceControls.workspaceId });
|
|
3043
3266
|
if (!updated) {
|
|
@@ -3114,7 +3337,7 @@ async function insertWorkspaceControlEventInTransaction(db, input) {
|
|
|
3114
3337
|
|
|
3115
3338
|
// src/session-realtime.ts
|
|
3116
3339
|
import { createHash as createHash4, timingSafeEqual } from "crypto";
|
|
3117
|
-
import { and as and9, asc as asc4, eq as eq9, inArray as inArray4 } from "drizzle-orm";
|
|
3340
|
+
import { and as and9, asc as asc4, eq as eq9, gt as gt3, inArray as inArray4, isNull as isNull2, or } from "drizzle-orm";
|
|
3118
3341
|
|
|
3119
3342
|
// src/session-realtime-context.ts
|
|
3120
3343
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -3134,115 +3357,17 @@ import {
|
|
|
3134
3357
|
import { and as and7, asc as asc2, eq as eq7, inArray as inArray2, sql as sql4 } from "drizzle-orm";
|
|
3135
3358
|
|
|
3136
3359
|
// src/session-realtime-state.ts
|
|
3137
|
-
import { and as
|
|
3360
|
+
import { and as and6, eq as eq6, gt as gt2 } from "drizzle-orm";
|
|
3138
3361
|
async function sessionRealtimeIsActiveInTransaction(db, workspaceId, sessionId, now = /* @__PURE__ */ new Date()) {
|
|
3139
3362
|
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
3363
|
and6(
|
|
3186
|
-
eq6(sessionRealtimeModes.
|
|
3187
|
-
eq6(sessionRealtimeModes.
|
|
3188
|
-
eq6(sessionRealtimeModes.sessionId, input.sessionId),
|
|
3364
|
+
eq6(sessionRealtimeModes.workspaceId, workspaceId),
|
|
3365
|
+
eq6(sessionRealtimeModes.sessionId, sessionId),
|
|
3189
3366
|
eq6(sessionRealtimeModes.state, "active"),
|
|
3190
3367
|
gt2(sessionRealtimeModes.leaseExpiresAt, now)
|
|
3191
3368
|
)
|
|
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
3369
|
).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(">", ">");
|
|
3370
|
+
return Boolean(row);
|
|
3246
3371
|
}
|
|
3247
3372
|
|
|
3248
3373
|
// src/session-queue-commands.ts
|
|
@@ -3370,10 +3495,13 @@ async function supersedeSessionCurrentDirectionInTransaction(db, input) {
|
|
|
3370
3495
|
eq7(sessionHumanInputRequests.turnId, current.id),
|
|
3371
3496
|
eq7(sessionHumanInputRequests.status, "pending")
|
|
3372
3497
|
)
|
|
3373
|
-
).returning({
|
|
3498
|
+
).returning({
|
|
3499
|
+
id: sessionHumanInputRequests.id,
|
|
3500
|
+
questions: sessionHumanInputRequests.questions
|
|
3501
|
+
});
|
|
3374
3502
|
let lastSequence = closedTools.sequence;
|
|
3375
3503
|
if (cancelledHumanInputs.length > 0) {
|
|
3376
|
-
await db.insert(sessionEvents).values(
|
|
3504
|
+
const cancelledHumanInputEvents = await db.insert(sessionEvents).values(
|
|
3377
3505
|
cancelledHumanInputs.map((request) => ({
|
|
3378
3506
|
accountId: input.accountId,
|
|
3379
3507
|
workspaceId: input.workspaceId,
|
|
@@ -3386,7 +3514,33 @@ async function supersedeSessionCurrentDirectionInTransaction(db, input) {
|
|
|
3386
3514
|
payload: { requestId: request.id, response: { outcome: "cancelled" } },
|
|
3387
3515
|
occurredAt: now
|
|
3388
3516
|
}))
|
|
3389
|
-
);
|
|
3517
|
+
).returning();
|
|
3518
|
+
const requestsById = new Map(cancelledHumanInputs.map((request) => [request.id, request]));
|
|
3519
|
+
for (const event of cancelledHumanInputEvents) {
|
|
3520
|
+
const payload = event.payload;
|
|
3521
|
+
const request = typeof payload.requestId === "string" ? requestsById.get(payload.requestId) : null;
|
|
3522
|
+
if (!request) continue;
|
|
3523
|
+
await mirrorSessionRealtimeContextInTransaction(db, {
|
|
3524
|
+
accountId: input.accountId,
|
|
3525
|
+
workspaceId: input.workspaceId,
|
|
3526
|
+
sessionId: input.sessionId,
|
|
3527
|
+
sourceKind: "human_input_response",
|
|
3528
|
+
sourceId: event.id,
|
|
3529
|
+
turnId: current.id,
|
|
3530
|
+
channel: null,
|
|
3531
|
+
text: renderRealtimeHumanInputResponseContext({
|
|
3532
|
+
requestId: request.id,
|
|
3533
|
+
questions: request.questions,
|
|
3534
|
+
response: { outcome: "cancelled" }
|
|
3535
|
+
}),
|
|
3536
|
+
payload: {
|
|
3537
|
+
requestId: request.id,
|
|
3538
|
+
outcome: "cancelled",
|
|
3539
|
+
sourceEventId: event.id
|
|
3540
|
+
},
|
|
3541
|
+
now
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3390
3544
|
}
|
|
3391
3545
|
await db.update(sessionTurns).set({
|
|
3392
3546
|
status: "superseded",
|
|
@@ -5435,6 +5589,46 @@ async function beginSessionRealtimeInTransaction(db, input) {
|
|
|
5435
5589
|
now
|
|
5436
5590
|
);
|
|
5437
5591
|
eventIds.push(event.id);
|
|
5592
|
+
const pendingHumanInputs = await db.select({
|
|
5593
|
+
id: sessionHumanInputRequests.id,
|
|
5594
|
+
turnId: sessionHumanInputRequests.turnId,
|
|
5595
|
+
questions: sessionHumanInputRequests.questions,
|
|
5596
|
+
allowSkip: sessionHumanInputRequests.allowSkip,
|
|
5597
|
+
expiresAt: sessionHumanInputRequests.expiresAt
|
|
5598
|
+
}).from(sessionHumanInputRequests).where(
|
|
5599
|
+
and9(
|
|
5600
|
+
eq9(sessionHumanInputRequests.accountId, input.accountId),
|
|
5601
|
+
eq9(sessionHumanInputRequests.workspaceId, input.workspaceId),
|
|
5602
|
+
eq9(sessionHumanInputRequests.sessionId, input.sessionId),
|
|
5603
|
+
eq9(sessionHumanInputRequests.status, "pending"),
|
|
5604
|
+
or(
|
|
5605
|
+
isNull2(sessionHumanInputRequests.expiresAt),
|
|
5606
|
+
gt3(sessionHumanInputRequests.expiresAt, now)
|
|
5607
|
+
)
|
|
5608
|
+
)
|
|
5609
|
+
).orderBy(
|
|
5610
|
+
asc4(sessionHumanInputRequests.createdAt),
|
|
5611
|
+
asc4(sessionHumanInputRequests.id)
|
|
5612
|
+
);
|
|
5613
|
+
if (pendingHumanInputs.length > 0) {
|
|
5614
|
+
const sourceTurnIds = new Set(pendingHumanInputs.map((request) => request.turnId));
|
|
5615
|
+
await mirrorSessionRealtimeContextInTransaction(db, {
|
|
5616
|
+
accountId: input.accountId,
|
|
5617
|
+
workspaceId: input.workspaceId,
|
|
5618
|
+
sessionId: input.sessionId,
|
|
5619
|
+
sourceKind: "human_input_request",
|
|
5620
|
+
sourceId: `startup:${pendingHumanInputs.map((request) => request.id).join(":")}`,
|
|
5621
|
+
turnId: sourceTurnIds.size === 1 ? pendingHumanInputs[0].turnId : null,
|
|
5622
|
+
channel: "speakable",
|
|
5623
|
+
text: renderRealtimeHumanInputRequestContext({ requests: pendingHumanInputs }),
|
|
5624
|
+
payload: {
|
|
5625
|
+
status: "waiting_for_user",
|
|
5626
|
+
requestIds: pendingHumanInputs.map((request) => request.id),
|
|
5627
|
+
trigger: "realtime_start"
|
|
5628
|
+
},
|
|
5629
|
+
now
|
|
5630
|
+
});
|
|
5631
|
+
}
|
|
5438
5632
|
return {
|
|
5439
5633
|
mode: mapRealtimeMode(row),
|
|
5440
5634
|
replay: false,
|
|
@@ -5566,7 +5760,7 @@ async function settleExpiredSessionRealtimeInTransaction(db, session, now = /* @
|
|
|
5566
5760
|
// src/session-realtime-ledger.ts
|
|
5567
5761
|
import { createHash as createHash6 } from "crypto";
|
|
5568
5762
|
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
|
|
5763
|
+
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
5764
|
|
|
5571
5765
|
// src/session-realtime-terminal.ts
|
|
5572
5766
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -6016,7 +6210,7 @@ async function claimSessionRealtimeConnectionInTransaction(db, input) {
|
|
|
6016
6210
|
and11(
|
|
6017
6211
|
eq11(sessionRealtimeEntries.realtimeId, input.realtimeId),
|
|
6018
6212
|
eq11(sessionRealtimeEntries.direction, "provider_out"),
|
|
6019
|
-
|
|
6213
|
+
isNull3(sessionRealtimeEntries.providerAckedAt)
|
|
6020
6214
|
)
|
|
6021
6215
|
).orderBy(asc5(sessionRealtimeEntries.sequence)).limit(SESSION_REALTIME_STARTUP_MAX_ENTRIES);
|
|
6022
6216
|
const startupFenceSequence = startupEntries.at(-1)?.sequence ?? 0;
|
|
@@ -6048,7 +6242,7 @@ async function startupEntriesForConnection(db, connection) {
|
|
|
6048
6242
|
and11(
|
|
6049
6243
|
eq11(sessionRealtimeEntries.realtimeId, connection.realtimeId),
|
|
6050
6244
|
eq11(sessionRealtimeEntries.direction, "provider_out"),
|
|
6051
|
-
|
|
6245
|
+
isNull3(sessionRealtimeEntries.providerAckedAt),
|
|
6052
6246
|
sql7`${sessionRealtimeEntries.sequence} <= ${connection.startupFenceSequence}`
|
|
6053
6247
|
)
|
|
6054
6248
|
).orderBy(asc5(sessionRealtimeEntries.sequence)).limit(SESSION_REALTIME_STARTUP_MAX_ENTRIES);
|
|
@@ -6470,7 +6664,7 @@ async function materializeRealtimeUpdates(db, input, accountId, allocateSequence
|
|
|
6470
6664
|
eq11(sessionSystemUpdates.sessionId, input.sessionId),
|
|
6471
6665
|
inArray6(sessionSystemUpdates.state, ["pending", "deferred"]),
|
|
6472
6666
|
inArray6(sessionSystemUpdates.kind, ["agent_message", "child_terminal_result"]),
|
|
6473
|
-
|
|
6667
|
+
isNull3(sessionRealtimeEntries.id)
|
|
6474
6668
|
)
|
|
6475
6669
|
).orderBy(asc5(sessionSystemUpdates.createdAt), asc5(sessionSystemUpdates.id)).limit(SESSION_REALTIME_LEDGER_MAX_OUTBOUND);
|
|
6476
6670
|
const updates = rows.map(({ update }) => update);
|
|
@@ -6579,7 +6773,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6579
6773
|
}).where(
|
|
6580
6774
|
and11(
|
|
6581
6775
|
eq11(sessionRealtimeConnections.id, connection.id),
|
|
6582
|
-
|
|
6776
|
+
isNull3(sessionRealtimeConnections.startupAcknowledgedAt)
|
|
6583
6777
|
)
|
|
6584
6778
|
).returning({ id: sessionRealtimeConnections.id });
|
|
6585
6779
|
if (!acknowledged) {
|
|
@@ -6616,7 +6810,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6616
6810
|
const [existing] = await db.select().from(sessionRealtimeEntries).where(
|
|
6617
6811
|
and11(
|
|
6618
6812
|
eq11(sessionRealtimeEntries.realtimeId, input.realtimeId),
|
|
6619
|
-
incoming.kind === "delegation_call" && incoming.delegationItemId ?
|
|
6813
|
+
incoming.kind === "delegation_call" && incoming.delegationItemId ? or2(
|
|
6620
6814
|
eq11(sessionRealtimeEntries.operationId, incoming.operationId),
|
|
6621
6815
|
and11(
|
|
6622
6816
|
eq11(sessionRealtimeEntries.kind, "delegation_call"),
|
|
@@ -6695,7 +6889,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6695
6889
|
eq11(sessionRealtimeEntries.sessionId, input.sessionId),
|
|
6696
6890
|
eq11(sessionRealtimeEntries.realtimeId, input.realtimeId),
|
|
6697
6891
|
eq11(sessionRealtimeEntries.direction, "provider_out"),
|
|
6698
|
-
|
|
6892
|
+
isNull3(sessionRealtimeEntries.clientAckedAt),
|
|
6699
6893
|
sql7`${sessionRealtimeEntries.sequence} <= ${clientAck}`
|
|
6700
6894
|
)
|
|
6701
6895
|
);
|
|
@@ -6730,7 +6924,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6730
6924
|
sessionRealtimeEntries.id,
|
|
6731
6925
|
ackable.map((entry) => entry.id)
|
|
6732
6926
|
),
|
|
6733
|
-
|
|
6927
|
+
isNull3(sessionRealtimeEntries.providerAckedAt)
|
|
6734
6928
|
)
|
|
6735
6929
|
);
|
|
6736
6930
|
}
|
|
@@ -6740,7 +6934,7 @@ async function syncSessionRealtimeLedgerInTransaction(db, input, hooks = {}) {
|
|
|
6740
6934
|
eq11(sessionRealtimeEntries.sessionId, input.sessionId),
|
|
6741
6935
|
eq11(sessionRealtimeEntries.realtimeId, input.realtimeId),
|
|
6742
6936
|
eq11(sessionRealtimeEntries.direction, "provider_out"),
|
|
6743
|
-
|
|
6937
|
+
isNull3(sessionRealtimeEntries.providerAckedAt)
|
|
6744
6938
|
)
|
|
6745
6939
|
).orderBy(
|
|
6746
6940
|
sql7`case when ${sessionRealtimeEntries.clientAckedAt} is null then 0 else 1 end`,
|
|
@@ -7300,13 +7494,14 @@ function renderMemoryEntry(record) {
|
|
|
7300
7494
|
import { sql as sql15 } from "drizzle-orm";
|
|
7301
7495
|
|
|
7302
7496
|
// src/workspace-instruction-policies.ts
|
|
7303
|
-
import { createHash as createHash8 } from "crypto";
|
|
7497
|
+
import { createHash as createHash8, randomUUID } from "crypto";
|
|
7304
7498
|
import {
|
|
7499
|
+
PreferenceRegistrySnapshot,
|
|
7305
7500
|
ResolvedWorkspaceInstructionPolicySnapshot,
|
|
7306
7501
|
WORKSPACE_INSTRUCTION_POLICY_CONTENT_MAX_CHARS,
|
|
7307
7502
|
WorkspaceInstructionPolicySnapshot
|
|
7308
7503
|
} from "@opengeni/contracts";
|
|
7309
|
-
import { and as and12, asc as asc6, desc as desc4, eq as eq12, inArray as inArray7, isNull as
|
|
7504
|
+
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
7505
|
var WorkspaceInstructionPolicyConflictError = class extends Error {
|
|
7311
7506
|
constructor(currentHead) {
|
|
7312
7507
|
super("The active workspace instruction policy changed in another request");
|
|
@@ -7315,6 +7510,13 @@ var WorkspaceInstructionPolicyConflictError = class extends Error {
|
|
|
7315
7510
|
name = "WorkspaceInstructionPolicyConflictError";
|
|
7316
7511
|
code = "WORKSPACE_INSTRUCTION_POLICY_CONFLICT";
|
|
7317
7512
|
};
|
|
7513
|
+
var WorkspaceInstructionPolicyOperationReuseError = class extends Error {
|
|
7514
|
+
name = "WorkspaceInstructionPolicyOperationReuseError";
|
|
7515
|
+
code = "WORKSPACE_INSTRUCTION_POLICY_OPERATION_REUSED";
|
|
7516
|
+
constructor() {
|
|
7517
|
+
super("The workspace instruction-policy operation id was already used for another request");
|
|
7518
|
+
}
|
|
7519
|
+
};
|
|
7318
7520
|
var WorkspaceInstructionPolicyNotFoundError = class extends Error {
|
|
7319
7521
|
name = "WorkspaceInstructionPolicyNotFoundError";
|
|
7320
7522
|
constructor(message = "Workspace instruction-policy revision was not found") {
|
|
@@ -7330,18 +7532,217 @@ var WorkspaceInstructionPolicyLegacyUnavailableError = class extends Error {
|
|
|
7330
7532
|
super("This workspace has no legacy agent instructions to import");
|
|
7331
7533
|
}
|
|
7332
7534
|
};
|
|
7535
|
+
var WorkspaceInstructionPolicyOnboardingProposalContentError = class extends Error {
|
|
7536
|
+
constructor(code) {
|
|
7537
|
+
super(
|
|
7538
|
+
code === "WORKSPACE_INSTRUCTION_POLICY_ONBOARDING_PROPOSAL_EMPTY" ? "An onboarding proposal must contain non-blank instruction-policy content" : `An onboarding proposal must not exceed ${WORKSPACE_INSTRUCTION_POLICY_CONTENT_MAX_CHARS} characters`
|
|
7539
|
+
);
|
|
7540
|
+
this.code = code;
|
|
7541
|
+
}
|
|
7542
|
+
name = "WorkspaceInstructionPolicyOnboardingProposalContentError";
|
|
7543
|
+
};
|
|
7544
|
+
var WorkspaceInstructionPolicyOnboardingProposalStaleError = class extends Error {
|
|
7545
|
+
constructor(currentHead) {
|
|
7546
|
+
super("The onboarding proposal was prepared against a stale active policy baseline");
|
|
7547
|
+
this.currentHead = currentHead;
|
|
7548
|
+
}
|
|
7549
|
+
name = "WorkspaceInstructionPolicyOnboardingProposalStaleError";
|
|
7550
|
+
code = "WORKSPACE_INSTRUCTION_POLICY_ONBOARDING_PROPOSAL_STALE";
|
|
7551
|
+
};
|
|
7552
|
+
var WorkspaceInstructionPolicyOnboardingProposalConflictError = class extends Error {
|
|
7553
|
+
constructor(existingProposalId, existingDraftRevisionId) {
|
|
7554
|
+
super("This onboarding source version already proposed a draft for the policy target");
|
|
7555
|
+
this.existingProposalId = existingProposalId;
|
|
7556
|
+
this.existingDraftRevisionId = existingDraftRevisionId;
|
|
7557
|
+
}
|
|
7558
|
+
name = "WorkspaceInstructionPolicyOnboardingProposalConflictError";
|
|
7559
|
+
code = "WORKSPACE_INSTRUCTION_POLICY_ONBOARDING_PROPOSAL_CONFLICT";
|
|
7560
|
+
};
|
|
7333
7561
|
var WorkspaceInstructionPolicySnapshotAuthorityError = class extends Error {
|
|
7334
7562
|
name = "WorkspaceInstructionPolicySnapshotAuthorityError";
|
|
7335
7563
|
};
|
|
7336
7564
|
function contentHash(content) {
|
|
7337
7565
|
return createHash8("sha256").update(content, "utf8").digest("hex");
|
|
7338
7566
|
}
|
|
7567
|
+
function hasOwnField(value, field) {
|
|
7568
|
+
return Object.prototype.hasOwnProperty.call(value, field);
|
|
7569
|
+
}
|
|
7570
|
+
function operationRequestFingerprint(operation, fields) {
|
|
7571
|
+
const canonicalRequest = JSON.stringify([
|
|
7572
|
+
"workspace_instruction_policy_operation",
|
|
7573
|
+
1,
|
|
7574
|
+
operation,
|
|
7575
|
+
fields
|
|
7576
|
+
]);
|
|
7577
|
+
return createHash8("sha256").update(canonicalRequest, "utf8").digest("hex");
|
|
7578
|
+
}
|
|
7579
|
+
function draftRequestFingerprint(input) {
|
|
7580
|
+
return operationRequestFingerprint("create_draft", [
|
|
7581
|
+
["accountId", true, input.accountId],
|
|
7582
|
+
["workspaceId", true, input.workspaceId],
|
|
7583
|
+
["kind", true, input.kind],
|
|
7584
|
+
["scope", true, input.scope],
|
|
7585
|
+
["roleKey", true, input.roleKey],
|
|
7586
|
+
["content", true, input.content],
|
|
7587
|
+
["provenanceSource", true, input.provenanceSource],
|
|
7588
|
+
["provenanceSourceId", true, input.provenanceSourceId],
|
|
7589
|
+
["supersedesRevisionId", true, input.supersedesRevisionId],
|
|
7590
|
+
["createdBySubjectId", true, input.createdBySubjectId]
|
|
7591
|
+
]);
|
|
7592
|
+
}
|
|
7593
|
+
function importLegacyRequestFingerprint(input) {
|
|
7594
|
+
return operationRequestFingerprint("import_legacy", [
|
|
7595
|
+
["accountId", true, input.accountId],
|
|
7596
|
+
["workspaceId", true, input.workspaceId],
|
|
7597
|
+
["createdBySubjectId", true, input.createdBySubjectId],
|
|
7598
|
+
["supersedesRevisionId", true, input.supersedesRevisionId]
|
|
7599
|
+
]);
|
|
7600
|
+
}
|
|
7601
|
+
function activeRevisionRequestFingerprint(input) {
|
|
7602
|
+
const expectedCurrentRevisionIdPresent = hasOwnField(input, "expectedCurrentRevisionId");
|
|
7603
|
+
const expectedActivationVersionPresent = hasOwnField(input, "expectedActivationVersion");
|
|
7604
|
+
return operationRequestFingerprint(`change_active_revision:${input.type}`, [
|
|
7605
|
+
["accountId", true, input.accountId],
|
|
7606
|
+
["workspaceId", true, input.workspaceId],
|
|
7607
|
+
["targetRevisionId", true, input.targetRevisionId],
|
|
7608
|
+
[
|
|
7609
|
+
"expectedCurrentRevisionId",
|
|
7610
|
+
expectedCurrentRevisionIdPresent,
|
|
7611
|
+
input.expectedCurrentRevisionId ?? null
|
|
7612
|
+
],
|
|
7613
|
+
[
|
|
7614
|
+
"expectedActivationVersion",
|
|
7615
|
+
expectedActivationVersionPresent,
|
|
7616
|
+
input.expectedActivationVersion ?? null
|
|
7617
|
+
],
|
|
7618
|
+
["actorSubjectId", true, input.actorSubjectId],
|
|
7619
|
+
["reason", true, input.reason]
|
|
7620
|
+
]);
|
|
7621
|
+
}
|
|
7622
|
+
function onboardingProposalRequestFingerprint(input) {
|
|
7623
|
+
return operationRequestFingerprint("create_onboarding_proposal", [
|
|
7624
|
+
["accountId", true, input.accountId],
|
|
7625
|
+
["workspaceId", true, input.workspaceId],
|
|
7626
|
+
["kind", true, input.kind],
|
|
7627
|
+
["scope", true, input.scope],
|
|
7628
|
+
["roleKey", true, input.roleKey],
|
|
7629
|
+
["content", true, input.content],
|
|
7630
|
+
["sourceId", true, input.sourceId],
|
|
7631
|
+
["sourceVersion", true, input.sourceVersion],
|
|
7632
|
+
["confidenceBps", true, input.confidenceBps],
|
|
7633
|
+
["expectedCurrentRevisionId", true, input.expectedCurrentRevisionId],
|
|
7634
|
+
["expectedActivationVersion", true, input.expectedActivationVersion],
|
|
7635
|
+
["createdBySubjectId", true, input.createdBySubjectId]
|
|
7636
|
+
]);
|
|
7637
|
+
}
|
|
7339
7638
|
function iso(value) {
|
|
7340
7639
|
return (value instanceof Date ? value : new Date(value)).toISOString();
|
|
7341
7640
|
}
|
|
7641
|
+
async function getWorkspaceStateAcceptedAttemptGovernance(db, input) {
|
|
7642
|
+
return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
|
|
7643
|
+
const initiatingSubject = sql8`coalesce(
|
|
7644
|
+
${sessionTurns.initiatingHumanSubjectId},
|
|
7645
|
+
case when ${sessionTurns.initiatorKind} = 'subject'
|
|
7646
|
+
then ${sessionTurns.initiatorSubjectId}
|
|
7647
|
+
end
|
|
7648
|
+
)`;
|
|
7649
|
+
const [attempt] = await scopedDb.select({
|
|
7650
|
+
attemptId: sessionTurnAttempts.id,
|
|
7651
|
+
executionGeneration: sessionTurnAttempts.executionGeneration,
|
|
7652
|
+
acceptedAt: sessionTurns.createdAt
|
|
7653
|
+
}).from(sessionTurnAttempts).innerJoin(
|
|
7654
|
+
sessionTurns,
|
|
7655
|
+
and12(
|
|
7656
|
+
eq12(sessionTurns.accountId, sessionTurnAttempts.accountId),
|
|
7657
|
+
eq12(sessionTurns.workspaceId, sessionTurnAttempts.workspaceId),
|
|
7658
|
+
eq12(sessionTurns.sessionId, sessionTurnAttempts.sessionId),
|
|
7659
|
+
eq12(sessionTurns.id, sessionTurnAttempts.turnId)
|
|
7660
|
+
)
|
|
7661
|
+
).where(
|
|
7662
|
+
and12(
|
|
7663
|
+
eq12(sessionTurnAttempts.accountId, input.accountId),
|
|
7664
|
+
eq12(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
7665
|
+
eq12(sessionTurnAttempts.id, input.attemptId),
|
|
7666
|
+
eq12(initiatingSubject, input.subjectId)
|
|
7667
|
+
)
|
|
7668
|
+
).limit(1);
|
|
7669
|
+
if (!attempt) return null;
|
|
7670
|
+
const [policyRows, preferenceRows] = await Promise.all([
|
|
7671
|
+
scopedDb.select().from(workspaceInstructionPolicySnapshots).where(
|
|
7672
|
+
and12(
|
|
7673
|
+
eq12(workspaceInstructionPolicySnapshots.accountId, input.accountId),
|
|
7674
|
+
eq12(workspaceInstructionPolicySnapshots.workspaceId, input.workspaceId),
|
|
7675
|
+
eq12(workspaceInstructionPolicySnapshots.attemptId, input.attemptId),
|
|
7676
|
+
eq12(
|
|
7677
|
+
workspaceInstructionPolicySnapshots.executionGeneration,
|
|
7678
|
+
attempt.executionGeneration
|
|
7679
|
+
)
|
|
7680
|
+
)
|
|
7681
|
+
).limit(1),
|
|
7682
|
+
scopedDb.select().from(preferenceRegistrySnapshots).where(
|
|
7683
|
+
and12(
|
|
7684
|
+
eq12(preferenceRegistrySnapshots.accountId, input.accountId),
|
|
7685
|
+
eq12(preferenceRegistrySnapshots.workspaceId, input.workspaceId),
|
|
7686
|
+
eq12(preferenceRegistrySnapshots.attemptId, input.attemptId),
|
|
7687
|
+
eq12(preferenceRegistrySnapshots.executionGeneration, attempt.executionGeneration),
|
|
7688
|
+
eq12(preferenceRegistrySnapshots.initiatingHumanSubjectId, input.subjectId)
|
|
7689
|
+
)
|
|
7690
|
+
).limit(1)
|
|
7691
|
+
]);
|
|
7692
|
+
const policyRow = policyRows[0] ?? null;
|
|
7693
|
+
const preferenceRow = preferenceRows[0] ?? null;
|
|
7694
|
+
return {
|
|
7695
|
+
attemptId: attempt.attemptId,
|
|
7696
|
+
executionGeneration: attempt.executionGeneration,
|
|
7697
|
+
acceptedAt: iso(attempt.acceptedAt),
|
|
7698
|
+
policySnapshot: policyRow ? WorkspaceInstructionPolicySnapshot.parse({
|
|
7699
|
+
id: policyRow.id,
|
|
7700
|
+
workspaceId: policyRow.workspaceId,
|
|
7701
|
+
sessionId: policyRow.sessionId,
|
|
7702
|
+
turnId: policyRow.turnId,
|
|
7703
|
+
attemptId: policyRow.attemptId,
|
|
7704
|
+
executionGeneration: policyRow.executionGeneration,
|
|
7705
|
+
policyRole: policyRow.policyRole,
|
|
7706
|
+
roleSource: policyRow.roleSource,
|
|
7707
|
+
entries: policyRow.entries,
|
|
7708
|
+
entryHash: policyRow.entryHash,
|
|
7709
|
+
createdAt: iso(policyRow.createdAt)
|
|
7710
|
+
}) : null,
|
|
7711
|
+
preferenceSnapshot: preferenceRow ? (() => {
|
|
7712
|
+
const snapshot = PreferenceRegistrySnapshot.parse({
|
|
7713
|
+
id: preferenceRow.id,
|
|
7714
|
+
workspaceId: preferenceRow.workspaceId,
|
|
7715
|
+
sessionId: preferenceRow.sessionId,
|
|
7716
|
+
turnId: preferenceRow.turnId,
|
|
7717
|
+
attemptId: preferenceRow.attemptId,
|
|
7718
|
+
executionGeneration: preferenceRow.executionGeneration,
|
|
7719
|
+
initiatingHumanSubjectId: preferenceRow.initiatingHumanSubjectId,
|
|
7720
|
+
descriptorHash: preferenceRow.descriptorHash,
|
|
7721
|
+
descriptors: preferenceRow.descriptors,
|
|
7722
|
+
truncated: preferenceRow.truncated,
|
|
7723
|
+
createdAt: iso(preferenceRow.createdAt)
|
|
7724
|
+
});
|
|
7725
|
+
return {
|
|
7726
|
+
id: snapshot.id,
|
|
7727
|
+
descriptorHash: snapshot.descriptorHash,
|
|
7728
|
+
descriptors: snapshot.descriptors.map((descriptor) => ({
|
|
7729
|
+
id: descriptor.id,
|
|
7730
|
+
revisionId: descriptor.revisionId,
|
|
7731
|
+
contentHash: descriptor.contentHash,
|
|
7732
|
+
activeVersion: descriptor.activeVersion,
|
|
7733
|
+
scope: descriptor.scope
|
|
7734
|
+
})),
|
|
7735
|
+
truncated: snapshot.truncated,
|
|
7736
|
+
createdAt: snapshot.createdAt
|
|
7737
|
+
};
|
|
7738
|
+
})() : null
|
|
7739
|
+
};
|
|
7740
|
+
});
|
|
7741
|
+
}
|
|
7342
7742
|
function revisionFromRow(row) {
|
|
7343
7743
|
return {
|
|
7344
7744
|
id: row.id,
|
|
7745
|
+
operationId: row.operationId ?? row.id,
|
|
7345
7746
|
accountId: row.accountId,
|
|
7346
7747
|
workspaceId: row.workspaceId,
|
|
7347
7748
|
revision: row.revision,
|
|
@@ -7375,6 +7776,7 @@ function headFromRow(row) {
|
|
|
7375
7776
|
function eventFromRow(row) {
|
|
7376
7777
|
return {
|
|
7377
7778
|
id: row.id,
|
|
7779
|
+
operationId: row.operationId ?? row.id,
|
|
7378
7780
|
accountId: row.accountId,
|
|
7379
7781
|
workspaceId: row.workspaceId,
|
|
7380
7782
|
kind: row.kind,
|
|
@@ -7397,12 +7799,56 @@ function eventFromRow(row) {
|
|
|
7397
7799
|
createdAt: iso(row.createdAt)
|
|
7398
7800
|
};
|
|
7399
7801
|
}
|
|
7802
|
+
function onboardingProposalFromRow(row, draftRow) {
|
|
7803
|
+
return {
|
|
7804
|
+
id: row.id,
|
|
7805
|
+
operationId: row.operationId,
|
|
7806
|
+
accountId: row.accountId,
|
|
7807
|
+
workspaceId: row.workspaceId,
|
|
7808
|
+
kind: row.kind,
|
|
7809
|
+
scope: row.scope,
|
|
7810
|
+
roleKey: row.roleKey,
|
|
7811
|
+
source: {
|
|
7812
|
+
id: row.sourceId,
|
|
7813
|
+
version: row.sourceVersion,
|
|
7814
|
+
confidenceBps: row.confidenceBps
|
|
7815
|
+
},
|
|
7816
|
+
baseline: row.baselineRevisionId === null ? null : {
|
|
7817
|
+
workspaceId: row.workspaceId,
|
|
7818
|
+
kind: row.kind,
|
|
7819
|
+
scope: row.scope,
|
|
7820
|
+
roleKey: row.roleKey,
|
|
7821
|
+
revisionId: row.baselineRevisionId,
|
|
7822
|
+
revision: row.baselineRevision,
|
|
7823
|
+
contentHash: row.baselineContentHash,
|
|
7824
|
+
activationVersion: row.baselineActivationVersion,
|
|
7825
|
+
activatedAt: iso(row.baselineActivatedAt)
|
|
7826
|
+
},
|
|
7827
|
+
draft: revisionFromRow(draftRow),
|
|
7828
|
+
status: "proposed",
|
|
7829
|
+
createdBySubjectId: row.createdBySubjectId,
|
|
7830
|
+
createdAt: iso(row.createdAt)
|
|
7831
|
+
};
|
|
7832
|
+
}
|
|
7833
|
+
function headFromEventRow(row) {
|
|
7834
|
+
return {
|
|
7835
|
+
workspaceId: row.workspaceId,
|
|
7836
|
+
kind: row.kind,
|
|
7837
|
+
scope: row.scope,
|
|
7838
|
+
roleKey: row.roleKey,
|
|
7839
|
+
revisionId: row.newRevisionId,
|
|
7840
|
+
revision: row.newRevision,
|
|
7841
|
+
contentHash: row.newContentHash,
|
|
7842
|
+
activationVersion: row.activationVersion,
|
|
7843
|
+
activatedAt: iso(row.createdAt)
|
|
7844
|
+
};
|
|
7845
|
+
}
|
|
7400
7846
|
function headTargetConditions(workspaceId, target) {
|
|
7401
7847
|
return [
|
|
7402
7848
|
eq12(workspaceInstructionPolicyHeads.workspaceId, workspaceId),
|
|
7403
7849
|
eq12(workspaceInstructionPolicyHeads.kind, target.kind),
|
|
7404
7850
|
eq12(workspaceInstructionPolicyHeads.scope, target.scope),
|
|
7405
|
-
target.roleKey === null ?
|
|
7851
|
+
target.roleKey === null ? isNull4(workspaceInstructionPolicyHeads.roleKey) : eq12(workspaceInstructionPolicyHeads.roleKey, target.roleKey)
|
|
7406
7852
|
];
|
|
7407
7853
|
}
|
|
7408
7854
|
function eventTargetConditions(workspaceId, target) {
|
|
@@ -7410,7 +7856,7 @@ function eventTargetConditions(workspaceId, target) {
|
|
|
7410
7856
|
eq12(workspaceInstructionPolicyActivationEvents.workspaceId, workspaceId),
|
|
7411
7857
|
eq12(workspaceInstructionPolicyActivationEvents.kind, target.kind),
|
|
7412
7858
|
eq12(workspaceInstructionPolicyActivationEvents.scope, target.scope),
|
|
7413
|
-
target.roleKey === null ?
|
|
7859
|
+
target.roleKey === null ? isNull4(workspaceInstructionPolicyActivationEvents.roleKey) : eq12(workspaceInstructionPolicyActivationEvents.roleKey, target.roleKey)
|
|
7414
7860
|
];
|
|
7415
7861
|
}
|
|
7416
7862
|
async function lockWorkspace(db, input) {
|
|
@@ -7434,6 +7880,65 @@ async function getRevisionInTransaction(db, workspaceId, revisionId) {
|
|
|
7434
7880
|
).limit(1);
|
|
7435
7881
|
return row ?? null;
|
|
7436
7882
|
}
|
|
7883
|
+
async function getRevisionByOperationInTransaction(db, workspaceId, operationId) {
|
|
7884
|
+
const [row] = await db.select().from(workspaceInstructionPolicyRevisions).where(
|
|
7885
|
+
and12(
|
|
7886
|
+
eq12(workspaceInstructionPolicyRevisions.workspaceId, workspaceId),
|
|
7887
|
+
or3(
|
|
7888
|
+
eq12(workspaceInstructionPolicyRevisions.operationId, operationId),
|
|
7889
|
+
and12(
|
|
7890
|
+
isNull4(workspaceInstructionPolicyRevisions.operationId),
|
|
7891
|
+
eq12(workspaceInstructionPolicyRevisions.id, operationId)
|
|
7892
|
+
)
|
|
7893
|
+
)
|
|
7894
|
+
)
|
|
7895
|
+
).limit(1);
|
|
7896
|
+
return row ?? null;
|
|
7897
|
+
}
|
|
7898
|
+
async function getEventByOperationInTransaction(db, workspaceId, operationId) {
|
|
7899
|
+
const [row] = await db.select().from(workspaceInstructionPolicyActivationEvents).where(
|
|
7900
|
+
and12(
|
|
7901
|
+
eq12(workspaceInstructionPolicyActivationEvents.workspaceId, workspaceId),
|
|
7902
|
+
or3(
|
|
7903
|
+
eq12(workspaceInstructionPolicyActivationEvents.operationId, operationId),
|
|
7904
|
+
and12(
|
|
7905
|
+
isNull4(workspaceInstructionPolicyActivationEvents.operationId),
|
|
7906
|
+
eq12(workspaceInstructionPolicyActivationEvents.id, operationId)
|
|
7907
|
+
)
|
|
7908
|
+
)
|
|
7909
|
+
)
|
|
7910
|
+
).limit(1);
|
|
7911
|
+
return row ?? null;
|
|
7912
|
+
}
|
|
7913
|
+
async function getOnboardingProposalByOperationInTransaction(db, workspaceId, operationId) {
|
|
7914
|
+
const [row] = await db.select().from(workspaceInstructionPolicyOnboardingProposals).where(
|
|
7915
|
+
and12(
|
|
7916
|
+
eq12(workspaceInstructionPolicyOnboardingProposals.workspaceId, workspaceId),
|
|
7917
|
+
eq12(workspaceInstructionPolicyOnboardingProposals.operationId, operationId)
|
|
7918
|
+
)
|
|
7919
|
+
).limit(1);
|
|
7920
|
+
return row ?? null;
|
|
7921
|
+
}
|
|
7922
|
+
async function getOnboardingProposalBySourceVersionInTransaction(db, input) {
|
|
7923
|
+
const [row] = await db.select().from(workspaceInstructionPolicyOnboardingProposals).where(
|
|
7924
|
+
and12(
|
|
7925
|
+
eq12(workspaceInstructionPolicyOnboardingProposals.workspaceId, input.workspaceId),
|
|
7926
|
+
eq12(workspaceInstructionPolicyOnboardingProposals.kind, input.kind),
|
|
7927
|
+
eq12(workspaceInstructionPolicyOnboardingProposals.scope, input.scope),
|
|
7928
|
+
input.roleKey === null ? isNull4(workspaceInstructionPolicyOnboardingProposals.roleKey) : eq12(workspaceInstructionPolicyOnboardingProposals.roleKey, input.roleKey),
|
|
7929
|
+
eq12(workspaceInstructionPolicyOnboardingProposals.sourceId, input.sourceId),
|
|
7930
|
+
eq12(workspaceInstructionPolicyOnboardingProposals.sourceVersion, input.sourceVersion)
|
|
7931
|
+
)
|
|
7932
|
+
).limit(1);
|
|
7933
|
+
return row ?? null;
|
|
7934
|
+
}
|
|
7935
|
+
async function resolveOnboardingProposalInTransaction(db, row) {
|
|
7936
|
+
const draftRow = await getRevisionInTransaction(db, row.workspaceId, row.draftRevisionId);
|
|
7937
|
+
if (!draftRow) {
|
|
7938
|
+
throw new Error("Workspace instruction-policy onboarding proposal lost its draft revision");
|
|
7939
|
+
}
|
|
7940
|
+
return onboardingProposalFromRow(row, draftRow);
|
|
7941
|
+
}
|
|
7437
7942
|
async function getHeadInTransaction(db, workspaceId, target) {
|
|
7438
7943
|
const [row] = await db.select().from(workspaceInstructionPolicyHeads).where(and12(...headTargetConditions(workspaceId, target))).for("update").limit(1);
|
|
7439
7944
|
return row ?? null;
|
|
@@ -7441,7 +7946,24 @@ async function getHeadInTransaction(db, workspaceId, target) {
|
|
|
7441
7946
|
function sameTarget(left, right) {
|
|
7442
7947
|
return left.kind === right.kind && left.scope === right.scope && left.roleKey === right.roleKey;
|
|
7443
7948
|
}
|
|
7949
|
+
function draftReceiptMatches(row, input) {
|
|
7950
|
+
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;
|
|
7951
|
+
}
|
|
7444
7952
|
async function createDraftInTransaction(db, input) {
|
|
7953
|
+
if (await getEventByOperationInTransaction(db, input.workspaceId, input.operationId)) {
|
|
7954
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
7955
|
+
}
|
|
7956
|
+
const existing = await getRevisionByOperationInTransaction(
|
|
7957
|
+
db,
|
|
7958
|
+
input.workspaceId,
|
|
7959
|
+
input.operationId
|
|
7960
|
+
);
|
|
7961
|
+
if (existing) {
|
|
7962
|
+
if (existing.requestFingerprint === null ? !draftReceiptMatches(existing, input) : existing.requestFingerprint !== input.requestFingerprint) {
|
|
7963
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
7964
|
+
}
|
|
7965
|
+
return revisionFromRow(existing);
|
|
7966
|
+
}
|
|
7445
7967
|
if (input.supersedesRevisionId !== null) {
|
|
7446
7968
|
const superseded = await getRevisionInTransaction(
|
|
7447
7969
|
db,
|
|
@@ -7455,6 +7977,8 @@ async function createDraftInTransaction(db, input) {
|
|
|
7455
7977
|
}
|
|
7456
7978
|
}
|
|
7457
7979
|
const [created] = await db.insert(workspaceInstructionPolicyRevisions).values({
|
|
7980
|
+
operationId: input.operationId,
|
|
7981
|
+
requestFingerprint: input.requestFingerprint,
|
|
7458
7982
|
accountId: input.accountId,
|
|
7459
7983
|
workspaceId: input.workspaceId,
|
|
7460
7984
|
kind: input.kind,
|
|
@@ -7466,26 +7990,57 @@ async function createDraftInTransaction(db, input) {
|
|
|
7466
7990
|
provenanceSourceId: input.provenanceSourceId,
|
|
7467
7991
|
supersedesRevisionId: input.supersedesRevisionId,
|
|
7468
7992
|
createdBySubjectId: input.createdBySubjectId
|
|
7469
|
-
}).returning();
|
|
7470
|
-
if (!created)
|
|
7993
|
+
}).onConflictDoNothing().returning();
|
|
7994
|
+
if (!created) {
|
|
7995
|
+
const concurrent = await getRevisionByOperationInTransaction(
|
|
7996
|
+
db,
|
|
7997
|
+
input.workspaceId,
|
|
7998
|
+
input.operationId
|
|
7999
|
+
);
|
|
8000
|
+
if (!concurrent || concurrent.requestFingerprint !== input.requestFingerprint) {
|
|
8001
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8002
|
+
}
|
|
8003
|
+
return revisionFromRow(concurrent);
|
|
8004
|
+
}
|
|
7471
8005
|
return revisionFromRow(created);
|
|
7472
8006
|
}
|
|
7473
8007
|
async function createWorkspaceInstructionPolicyDraft(db, input) {
|
|
8008
|
+
const request = { ...input, operationId: input.operationId ?? randomUUID() };
|
|
8009
|
+
const normalized = { ...request, requestFingerprint: draftRequestFingerprint(request) };
|
|
7474
8010
|
return await withRlsContext(
|
|
7475
8011
|
db,
|
|
7476
8012
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
7477
8013
|
async (scopedDb) => {
|
|
7478
8014
|
await lockWorkspace(scopedDb, input);
|
|
7479
|
-
return await createDraftInTransaction(scopedDb,
|
|
8015
|
+
return await createDraftInTransaction(scopedDb, normalized);
|
|
7480
8016
|
}
|
|
7481
8017
|
);
|
|
7482
8018
|
}
|
|
7483
8019
|
async function importLegacyWorkspaceInstructionPolicyDraft(db, input) {
|
|
8020
|
+
const request = { ...input, operationId: input.operationId ?? randomUUID() };
|
|
8021
|
+
const normalized = {
|
|
8022
|
+
...request,
|
|
8023
|
+
requestFingerprint: importLegacyRequestFingerprint(request)
|
|
8024
|
+
};
|
|
7484
8025
|
return await withRlsContext(
|
|
7485
8026
|
db,
|
|
7486
8027
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
7487
8028
|
async (scopedDb) => {
|
|
7488
8029
|
const workspace = await lockWorkspace(scopedDb, input);
|
|
8030
|
+
if (await getEventByOperationInTransaction(scopedDb, input.workspaceId, normalized.operationId)) {
|
|
8031
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8032
|
+
}
|
|
8033
|
+
const existing = await getRevisionByOperationInTransaction(
|
|
8034
|
+
scopedDb,
|
|
8035
|
+
input.workspaceId,
|
|
8036
|
+
normalized.operationId
|
|
8037
|
+
);
|
|
8038
|
+
if (existing) {
|
|
8039
|
+
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) {
|
|
8040
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8041
|
+
}
|
|
8042
|
+
return revisionFromRow(existing);
|
|
8043
|
+
}
|
|
7489
8044
|
if (workspace.agentInstructions === null) {
|
|
7490
8045
|
throw new WorkspaceInstructionPolicyLegacyUnavailableError();
|
|
7491
8046
|
}
|
|
@@ -7495,7 +8050,7 @@ async function importLegacyWorkspaceInstructionPolicyDraft(db, input) {
|
|
|
7495
8050
|
);
|
|
7496
8051
|
}
|
|
7497
8052
|
return await createDraftInTransaction(scopedDb, {
|
|
7498
|
-
...
|
|
8053
|
+
...normalized,
|
|
7499
8054
|
kind: "charter",
|
|
7500
8055
|
scope: "global",
|
|
7501
8056
|
roleKey: null,
|
|
@@ -7506,6 +8061,175 @@ async function importLegacyWorkspaceInstructionPolicyDraft(db, input) {
|
|
|
7506
8061
|
}
|
|
7507
8062
|
);
|
|
7508
8063
|
}
|
|
8064
|
+
async function createWorkspaceInstructionPolicyOnboardingProposal(db, input) {
|
|
8065
|
+
if (input.content.trim().length === 0) {
|
|
8066
|
+
throw new WorkspaceInstructionPolicyOnboardingProposalContentError(
|
|
8067
|
+
"WORKSPACE_INSTRUCTION_POLICY_ONBOARDING_PROPOSAL_EMPTY"
|
|
8068
|
+
);
|
|
8069
|
+
}
|
|
8070
|
+
if (input.content.length > WORKSPACE_INSTRUCTION_POLICY_CONTENT_MAX_CHARS) {
|
|
8071
|
+
throw new WorkspaceInstructionPolicyOnboardingProposalContentError(
|
|
8072
|
+
"WORKSPACE_INSTRUCTION_POLICY_ONBOARDING_PROPOSAL_OVERSIZED"
|
|
8073
|
+
);
|
|
8074
|
+
}
|
|
8075
|
+
const request = {
|
|
8076
|
+
...input,
|
|
8077
|
+
operationId: input.operationId ?? randomUUID(),
|
|
8078
|
+
sourceId: input.sourceId.trim(),
|
|
8079
|
+
sourceVersion: input.sourceVersion.trim()
|
|
8080
|
+
};
|
|
8081
|
+
const normalized = {
|
|
8082
|
+
...request,
|
|
8083
|
+
requestFingerprint: onboardingProposalRequestFingerprint(request)
|
|
8084
|
+
};
|
|
8085
|
+
return await withRlsContext(
|
|
8086
|
+
db,
|
|
8087
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
8088
|
+
async (scopedDb) => {
|
|
8089
|
+
await lockWorkspace(scopedDb, input);
|
|
8090
|
+
const existing = await getOnboardingProposalByOperationInTransaction(
|
|
8091
|
+
scopedDb,
|
|
8092
|
+
input.workspaceId,
|
|
8093
|
+
normalized.operationId
|
|
8094
|
+
);
|
|
8095
|
+
if (existing) {
|
|
8096
|
+
if (existing.requestFingerprint !== normalized.requestFingerprint) {
|
|
8097
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8098
|
+
}
|
|
8099
|
+
return await resolveOnboardingProposalInTransaction(scopedDb, existing);
|
|
8100
|
+
}
|
|
8101
|
+
if (await getEventByOperationInTransaction(
|
|
8102
|
+
scopedDb,
|
|
8103
|
+
input.workspaceId,
|
|
8104
|
+
normalized.operationId
|
|
8105
|
+
) || await getRevisionByOperationInTransaction(
|
|
8106
|
+
scopedDb,
|
|
8107
|
+
input.workspaceId,
|
|
8108
|
+
normalized.operationId
|
|
8109
|
+
)) {
|
|
8110
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8111
|
+
}
|
|
8112
|
+
const target = {
|
|
8113
|
+
kind: normalized.kind,
|
|
8114
|
+
scope: normalized.scope,
|
|
8115
|
+
roleKey: normalized.roleKey
|
|
8116
|
+
};
|
|
8117
|
+
const currentRow = await getHeadInTransaction(scopedDb, input.workspaceId, target);
|
|
8118
|
+
const currentHead = currentRow ? headFromRow(currentRow) : null;
|
|
8119
|
+
if ((currentHead?.revisionId ?? null) !== normalized.expectedCurrentRevisionId || (currentHead?.activationVersion ?? 0) !== normalized.expectedActivationVersion) {
|
|
8120
|
+
throw new WorkspaceInstructionPolicyOnboardingProposalStaleError(currentHead);
|
|
8121
|
+
}
|
|
8122
|
+
const sourceConflict = await getOnboardingProposalBySourceVersionInTransaction(
|
|
8123
|
+
scopedDb,
|
|
8124
|
+
normalized
|
|
8125
|
+
);
|
|
8126
|
+
if (sourceConflict) {
|
|
8127
|
+
throw new WorkspaceInstructionPolicyOnboardingProposalConflictError(
|
|
8128
|
+
sourceConflict.id,
|
|
8129
|
+
sourceConflict.draftRevisionId
|
|
8130
|
+
);
|
|
8131
|
+
}
|
|
8132
|
+
const proposalId = randomUUID();
|
|
8133
|
+
const draft = await createDraftInTransaction(scopedDb, {
|
|
8134
|
+
operationId: normalized.operationId,
|
|
8135
|
+
requestFingerprint: normalized.requestFingerprint,
|
|
8136
|
+
accountId: normalized.accountId,
|
|
8137
|
+
workspaceId: normalized.workspaceId,
|
|
8138
|
+
kind: normalized.kind,
|
|
8139
|
+
scope: normalized.scope,
|
|
8140
|
+
roleKey: normalized.roleKey,
|
|
8141
|
+
content: normalized.content,
|
|
8142
|
+
provenanceSource: "onboarding",
|
|
8143
|
+
provenanceSourceId: proposalId,
|
|
8144
|
+
supersedesRevisionId: currentHead?.revisionId ?? null,
|
|
8145
|
+
createdBySubjectId: normalized.createdBySubjectId
|
|
8146
|
+
});
|
|
8147
|
+
const createdAt = /* @__PURE__ */ new Date();
|
|
8148
|
+
const [created] = await scopedDb.insert(workspaceInstructionPolicyOnboardingProposals).values({
|
|
8149
|
+
id: proposalId,
|
|
8150
|
+
operationId: normalized.operationId,
|
|
8151
|
+
requestFingerprint: normalized.requestFingerprint,
|
|
8152
|
+
accountId: normalized.accountId,
|
|
8153
|
+
workspaceId: normalized.workspaceId,
|
|
8154
|
+
kind: normalized.kind,
|
|
8155
|
+
scope: normalized.scope,
|
|
8156
|
+
roleKey: normalized.roleKey,
|
|
8157
|
+
sourceId: normalized.sourceId,
|
|
8158
|
+
sourceVersion: normalized.sourceVersion,
|
|
8159
|
+
confidenceBps: normalized.confidenceBps,
|
|
8160
|
+
baselineRevisionId: currentHead?.revisionId ?? null,
|
|
8161
|
+
baselineRevision: currentHead?.revision ?? null,
|
|
8162
|
+
baselineContentHash: currentHead?.contentHash ?? null,
|
|
8163
|
+
baselineActivationVersion: currentHead?.activationVersion ?? 0,
|
|
8164
|
+
baselineActivatedAt: currentHead ? new Date(currentHead.activatedAt) : null,
|
|
8165
|
+
draftRevisionId: draft.id,
|
|
8166
|
+
draftRevision: draft.revision,
|
|
8167
|
+
draftContentHash: draft.contentHash,
|
|
8168
|
+
status: "proposed",
|
|
8169
|
+
createdBySubjectId: normalized.createdBySubjectId,
|
|
8170
|
+
createdAt
|
|
8171
|
+
}).onConflictDoNothing().returning();
|
|
8172
|
+
if (created) {
|
|
8173
|
+
const draftRow = await getRevisionInTransaction(scopedDb, input.workspaceId, draft.id);
|
|
8174
|
+
if (!draftRow) throw new Error("Onboarding proposal draft was not recorded");
|
|
8175
|
+
return onboardingProposalFromRow(created, draftRow);
|
|
8176
|
+
}
|
|
8177
|
+
const concurrentOperation = await getOnboardingProposalByOperationInTransaction(
|
|
8178
|
+
scopedDb,
|
|
8179
|
+
input.workspaceId,
|
|
8180
|
+
normalized.operationId
|
|
8181
|
+
);
|
|
8182
|
+
if (concurrentOperation) {
|
|
8183
|
+
if (concurrentOperation.requestFingerprint !== normalized.requestFingerprint) {
|
|
8184
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8185
|
+
}
|
|
8186
|
+
return await resolveOnboardingProposalInTransaction(scopedDb, concurrentOperation);
|
|
8187
|
+
}
|
|
8188
|
+
const concurrentSource = await getOnboardingProposalBySourceVersionInTransaction(
|
|
8189
|
+
scopedDb,
|
|
8190
|
+
normalized
|
|
8191
|
+
);
|
|
8192
|
+
if (concurrentSource) {
|
|
8193
|
+
throw new WorkspaceInstructionPolicyOnboardingProposalConflictError(
|
|
8194
|
+
concurrentSource.id,
|
|
8195
|
+
concurrentSource.draftRevisionId
|
|
8196
|
+
);
|
|
8197
|
+
}
|
|
8198
|
+
throw new Error("Workspace instruction-policy onboarding proposal was not created");
|
|
8199
|
+
}
|
|
8200
|
+
);
|
|
8201
|
+
}
|
|
8202
|
+
async function listWorkspaceInstructionPolicyOnboardingProposals(db, workspaceId, query) {
|
|
8203
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
8204
|
+
const rows = await scopedDb.select().from(workspaceInstructionPolicyOnboardingProposals).where(eq12(workspaceInstructionPolicyOnboardingProposals.workspaceId, workspaceId)).orderBy(
|
|
8205
|
+
desc4(workspaceInstructionPolicyOnboardingProposals.createdAt),
|
|
8206
|
+
desc4(workspaceInstructionPolicyOnboardingProposals.id)
|
|
8207
|
+
).limit(query.limit + 1);
|
|
8208
|
+
const truncated = rows.length > query.limit;
|
|
8209
|
+
const page = rows.slice(0, query.limit);
|
|
8210
|
+
if (page.length === 0) return { proposals: [], truncated };
|
|
8211
|
+
const revisions = await scopedDb.select().from(workspaceInstructionPolicyRevisions).where(
|
|
8212
|
+
and12(
|
|
8213
|
+
eq12(workspaceInstructionPolicyRevisions.workspaceId, workspaceId),
|
|
8214
|
+
inArray7(
|
|
8215
|
+
workspaceInstructionPolicyRevisions.id,
|
|
8216
|
+
page.map((proposal) => proposal.draftRevisionId)
|
|
8217
|
+
)
|
|
8218
|
+
)
|
|
8219
|
+
);
|
|
8220
|
+
const revisionsById = new Map(revisions.map((revision) => [revision.id, revision]));
|
|
8221
|
+
return {
|
|
8222
|
+
proposals: page.map((proposal) => {
|
|
8223
|
+
const draft = revisionsById.get(proposal.draftRevisionId);
|
|
8224
|
+
if (!draft) {
|
|
8225
|
+
throw new Error("Workspace instruction-policy onboarding proposal lost its draft");
|
|
8226
|
+
}
|
|
8227
|
+
return onboardingProposalFromRow(proposal, draft);
|
|
8228
|
+
}),
|
|
8229
|
+
truncated
|
|
8230
|
+
};
|
|
8231
|
+
});
|
|
8232
|
+
}
|
|
7509
8233
|
function listFilterConditions(workspaceId, query) {
|
|
7510
8234
|
const conditions = [
|
|
7511
8235
|
eq12(workspaceInstructionPolicyRevisions.workspaceId, workspaceId)
|
|
@@ -7518,7 +8242,7 @@ function listFilterConditions(workspaceId, query) {
|
|
|
7518
8242
|
}
|
|
7519
8243
|
if (query.roleKey !== void 0) {
|
|
7520
8244
|
conditions.push(
|
|
7521
|
-
query.roleKey === null ?
|
|
8245
|
+
query.roleKey === null ? isNull4(workspaceInstructionPolicyRevisions.roleKey) : eq12(workspaceInstructionPolicyRevisions.roleKey, query.roleKey)
|
|
7522
8246
|
);
|
|
7523
8247
|
}
|
|
7524
8248
|
if (query.afterRevision !== void 0) {
|
|
@@ -7619,11 +8343,27 @@ async function diffWorkspaceInstructionPolicyRevisions(db, workspaceId, input) {
|
|
|
7619
8343
|
});
|
|
7620
8344
|
}
|
|
7621
8345
|
async function changeActiveRevision(db, input) {
|
|
8346
|
+
const requestFingerprint = activeRevisionRequestFingerprint(input);
|
|
7622
8347
|
return await withRlsContext(
|
|
7623
8348
|
db,
|
|
7624
8349
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
7625
8350
|
async (scopedDb) => {
|
|
7626
8351
|
await lockWorkspace(scopedDb, input);
|
|
8352
|
+
if (await getRevisionByOperationInTransaction(scopedDb, input.workspaceId, input.operationId)) {
|
|
8353
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8354
|
+
}
|
|
8355
|
+
const existingEvent = await getEventByOperationInTransaction(
|
|
8356
|
+
scopedDb,
|
|
8357
|
+
input.workspaceId,
|
|
8358
|
+
input.operationId
|
|
8359
|
+
);
|
|
8360
|
+
if (existingEvent) {
|
|
8361
|
+
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;
|
|
8362
|
+
if (existingEvent.requestFingerprint === null ? !legacyRequestMatches : existingEvent.requestFingerprint !== requestFingerprint) {
|
|
8363
|
+
throw new WorkspaceInstructionPolicyOperationReuseError();
|
|
8364
|
+
}
|
|
8365
|
+
return { head: headFromEventRow(existingEvent), event: eventFromRow(existingEvent) };
|
|
8366
|
+
}
|
|
7627
8367
|
const targetRow = await getRevisionInTransaction(
|
|
7628
8368
|
scopedDb,
|
|
7629
8369
|
input.workspaceId,
|
|
@@ -7638,7 +8378,7 @@ async function changeActiveRevision(db, input) {
|
|
|
7638
8378
|
};
|
|
7639
8379
|
const currentRow = await getHeadInTransaction(scopedDb, input.workspaceId, targetIdentity);
|
|
7640
8380
|
const currentHead = currentRow ? headFromRow(currentRow) : null;
|
|
7641
|
-
if ((currentHead?.revisionId ?? null) !== input.expectedCurrentRevisionId) {
|
|
8381
|
+
if ((currentHead?.revisionId ?? null) !== input.expectedCurrentRevisionId || input.expectedActivationVersion !== void 0 && (currentHead?.activationVersion ?? 0) !== input.expectedActivationVersion) {
|
|
7642
8382
|
throw new WorkspaceInstructionPolicyConflictError(currentHead);
|
|
7643
8383
|
}
|
|
7644
8384
|
if (currentHead?.revisionId === target.id) {
|
|
@@ -7667,6 +8407,8 @@ async function changeActiveRevision(db, input) {
|
|
|
7667
8407
|
const activationVersion = (currentHead?.activationVersion ?? 0) + 1;
|
|
7668
8408
|
const createdAt = /* @__PURE__ */ new Date();
|
|
7669
8409
|
const [eventRow] = await scopedDb.insert(workspaceInstructionPolicyActivationEvents).values({
|
|
8410
|
+
operationId: input.operationId,
|
|
8411
|
+
requestFingerprint,
|
|
7670
8412
|
accountId: input.accountId,
|
|
7671
8413
|
workspaceId: input.workspaceId,
|
|
7672
8414
|
...targetIdentity,
|
|
@@ -7712,12 +8454,17 @@ async function changeActiveRevision(db, input) {
|
|
|
7712
8454
|
async function activateWorkspaceInstructionPolicyRevision(db, input) {
|
|
7713
8455
|
return await changeActiveRevision(db, {
|
|
7714
8456
|
...input,
|
|
8457
|
+
operationId: input.operationId ?? randomUUID(),
|
|
7715
8458
|
targetRevisionId: input.revisionId,
|
|
7716
8459
|
type: "activate"
|
|
7717
8460
|
});
|
|
7718
8461
|
}
|
|
7719
8462
|
async function rollbackWorkspaceInstructionPolicyRevision(db, input) {
|
|
7720
|
-
return await changeActiveRevision(db, {
|
|
8463
|
+
return await changeActiveRevision(db, {
|
|
8464
|
+
...input,
|
|
8465
|
+
operationId: input.operationId ?? randomUUID(),
|
|
8466
|
+
type: "rollback"
|
|
8467
|
+
});
|
|
7721
8468
|
}
|
|
7722
8469
|
async function getOrCreateWorkspaceInstructionPolicySnapshot(db, claims) {
|
|
7723
8470
|
try {
|
|
@@ -7800,16 +8547,17 @@ import {
|
|
|
7800
8547
|
PREFERENCE_REGISTRY_DESCRIPTOR_MAX_COUNT,
|
|
7801
8548
|
PREFERENCE_REGISTRY_DESCRIPTOR_MAX_UTF8_BYTES,
|
|
7802
8549
|
PREFERENCE_REGISTRY_TITLE_MAX_CHARS,
|
|
8550
|
+
PreferenceRegistryDescriptor,
|
|
7803
8551
|
PreferenceRegistryDetailResponse,
|
|
7804
8552
|
PreferenceRegistryEvent,
|
|
7805
8553
|
PreferenceRegistryFullContent,
|
|
7806
8554
|
PreferenceRegistryListResponse,
|
|
7807
8555
|
PreferenceRegistryRecord,
|
|
7808
8556
|
PreferenceRegistryRevisionSummary,
|
|
7809
|
-
PreferenceRegistrySnapshot,
|
|
8557
|
+
PreferenceRegistrySnapshot as PreferenceRegistrySnapshot2,
|
|
7810
8558
|
PreferenceRegistryScopeTarget
|
|
7811
8559
|
} from "@opengeni/contracts";
|
|
7812
|
-
import { and as and13, asc as asc7, desc as desc5, eq as eq13, gt as
|
|
8560
|
+
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
8561
|
var importedSources = /* @__PURE__ */ new Set([
|
|
7814
8562
|
"knowledge_proposal",
|
|
7815
8563
|
"imported_document",
|
|
@@ -8120,9 +8868,9 @@ async function listPreferenceRegistryScoped(db, input) {
|
|
|
8120
8868
|
if (input.status === "active") {
|
|
8121
8869
|
conditions.push(
|
|
8122
8870
|
eq13(preferenceRegistryPreferences.status, "active"),
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8871
|
+
or4(
|
|
8872
|
+
isNull5(preferenceRegistryRevisions.expiresAt),
|
|
8873
|
+
gt4(preferenceRegistryRevisions.expiresAt, now)
|
|
8126
8874
|
)
|
|
8127
8875
|
);
|
|
8128
8876
|
} else if (input.status === "expired") {
|
|
@@ -8162,6 +8910,81 @@ async function listPreferenceRegistry(db, input) {
|
|
|
8162
8910
|
return await listPreferenceRegistryScoped(scopedDb, input);
|
|
8163
8911
|
});
|
|
8164
8912
|
}
|
|
8913
|
+
async function getCurrentPreferenceRegistryGovernanceMetadata(db, input) {
|
|
8914
|
+
return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
|
|
8915
|
+
const rows = await scopedDb.select({
|
|
8916
|
+
preference: preferenceRegistryPreferences,
|
|
8917
|
+
revision: preferenceRegistryRevisions
|
|
8918
|
+
}).from(preferenceRegistryPreferences).innerJoin(
|
|
8919
|
+
preferenceRegistryRevisions,
|
|
8920
|
+
and13(
|
|
8921
|
+
eq13(
|
|
8922
|
+
preferenceRegistryRevisions.id,
|
|
8923
|
+
preferenceRegistryPreferences.activeRevisionId
|
|
8924
|
+
),
|
|
8925
|
+
eq13(
|
|
8926
|
+
preferenceRegistryRevisions.accountId,
|
|
8927
|
+
preferenceRegistryPreferences.accountId
|
|
8928
|
+
)
|
|
8929
|
+
)
|
|
8930
|
+
).where(
|
|
8931
|
+
and13(
|
|
8932
|
+
eq13(preferenceRegistryPreferences.status, "active"),
|
|
8933
|
+
or4(
|
|
8934
|
+
isNull5(preferenceRegistryRevisions.expiresAt),
|
|
8935
|
+
gt4(preferenceRegistryRevisions.expiresAt, sql9`transaction_timestamp()`)
|
|
8936
|
+
)
|
|
8937
|
+
)
|
|
8938
|
+
).orderBy(
|
|
8939
|
+
sql9`case ${preferenceRegistryPreferences.scope}
|
|
8940
|
+
when 'organization' then 0
|
|
8941
|
+
when 'workspace' then 1
|
|
8942
|
+
when 'user' then 2
|
|
8943
|
+
else 3
|
|
8944
|
+
end`,
|
|
8945
|
+
desc5(preferenceRegistryRevisions.precedenceRank),
|
|
8946
|
+
asc7(preferenceRegistryPreferences.stableKey),
|
|
8947
|
+
asc7(preferenceRegistryPreferences.id)
|
|
8948
|
+
).limit(PREFERENCE_REGISTRY_DESCRIPTOR_MAX_COUNT + 1);
|
|
8949
|
+
const bounded = boundPreferenceRegistryDescriptors(
|
|
8950
|
+
rows.map(
|
|
8951
|
+
({ preference, revision }) => PreferenceRegistryDescriptor.parse({
|
|
8952
|
+
id: preference.id,
|
|
8953
|
+
stableKey: preference.stableKey,
|
|
8954
|
+
title: revision.title,
|
|
8955
|
+
description: revision.description,
|
|
8956
|
+
scope: preference.scope,
|
|
8957
|
+
activeVersion: preference.activationVersion,
|
|
8958
|
+
revisionId: revision.id,
|
|
8959
|
+
contentHash: revision.contentHash,
|
|
8960
|
+
precedence: {
|
|
8961
|
+
tier: preference.scope,
|
|
8962
|
+
rank: revision.precedenceRank,
|
|
8963
|
+
conflictStrategy: revision.conflictStrategy,
|
|
8964
|
+
conflictsWith: revision.conflictsWith
|
|
8965
|
+
},
|
|
8966
|
+
provenance: {
|
|
8967
|
+
source: revision.provenanceSource,
|
|
8968
|
+
sourceIdHash: revision.provenanceSourceId ? contentHash2(revision.provenanceSourceId) : null,
|
|
8969
|
+
trust: revision.trust
|
|
8970
|
+
},
|
|
8971
|
+
expiresAt: revision.expiresAt ? iso2(revision.expiresAt) : null,
|
|
8972
|
+
retrievalHandle: `preference://${preference.id}/revisions/${revision.id}?sha256=${revision.contentHash}`
|
|
8973
|
+
})
|
|
8974
|
+
)
|
|
8975
|
+
);
|
|
8976
|
+
return {
|
|
8977
|
+
descriptors: bounded.descriptors.map((descriptor) => ({
|
|
8978
|
+
id: descriptor.id,
|
|
8979
|
+
revisionId: descriptor.revisionId,
|
|
8980
|
+
contentHash: descriptor.contentHash,
|
|
8981
|
+
activeVersion: descriptor.activeVersion,
|
|
8982
|
+
scope: descriptor.scope
|
|
8983
|
+
})),
|
|
8984
|
+
truncated: bounded.truncated
|
|
8985
|
+
};
|
|
8986
|
+
});
|
|
8987
|
+
}
|
|
8165
8988
|
async function listPreferenceRegistryForAttempt(db, input) {
|
|
8166
8989
|
return await withPreferenceRegistryAttemptAuthority(db, input, async (scopedDb) => {
|
|
8167
8990
|
return await listPreferenceRegistryScoped(scopedDb, input);
|
|
@@ -8382,9 +9205,9 @@ async function supersedePreferenceRegistry(db, input) {
|
|
|
8382
9205
|
and13(
|
|
8383
9206
|
eq13(preferenceRegistryRevisions.id, replacement.activeRevisionId),
|
|
8384
9207
|
eq13(preferenceRegistryRevisions.preferenceId, replacement.id),
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
9208
|
+
or4(
|
|
9209
|
+
isNull5(preferenceRegistryRevisions.expiresAt),
|
|
9210
|
+
gt4(preferenceRegistryRevisions.expiresAt, sql9`transaction_timestamp()`)
|
|
8388
9211
|
)
|
|
8389
9212
|
)
|
|
8390
9213
|
).limit(1);
|
|
@@ -8586,7 +9409,7 @@ async function getOrCreatePreferenceRegistrySnapshot(db, claims) {
|
|
|
8586
9409
|
"Preference snapshot authority conflicts with the accepted human attempt"
|
|
8587
9410
|
);
|
|
8588
9411
|
}
|
|
8589
|
-
return
|
|
9412
|
+
return PreferenceRegistrySnapshot2.parse({
|
|
8590
9413
|
...snapshot,
|
|
8591
9414
|
createdAt: iso2(snapshot.createdAt)
|
|
8592
9415
|
});
|
|
@@ -11970,7 +12793,7 @@ var defaultDeps2 = {
|
|
|
11970
12793
|
var inflight2 = /* @__PURE__ */ new Map();
|
|
11971
12794
|
var REFRESH_WINDOW_MS = 6e4;
|
|
11972
12795
|
var CONNECTION_REFRESH_TIMEOUT_MS = 1e4;
|
|
11973
|
-
function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
12796
|
+
function buildConnectionTokenResolver(db, settings, deps = defaultDeps2, options = {}) {
|
|
11974
12797
|
const load = async (input) => {
|
|
11975
12798
|
const subjectOwned = input.connectionRef.subjectScope === "subject";
|
|
11976
12799
|
if (subjectOwned && !input.subjectId) {
|
|
@@ -12033,6 +12856,7 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
12033
12856
|
status: "ok",
|
|
12034
12857
|
headers,
|
|
12035
12858
|
connectionId: cred.id,
|
|
12859
|
+
connectionVersion: cred.version,
|
|
12036
12860
|
expiresAt: cred.expiresAt
|
|
12037
12861
|
};
|
|
12038
12862
|
};
|
|
@@ -12041,7 +12865,7 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
12041
12865
|
if (!key) {
|
|
12042
12866
|
throw new Error("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is not configured");
|
|
12043
12867
|
}
|
|
12044
|
-
const refreshed = await deps.refresh(cred, ref, settings);
|
|
12868
|
+
const refreshed = await deps.refresh(cred, ref, settings, options.refreshTransport);
|
|
12045
12869
|
const refreshRecord = {
|
|
12046
12870
|
id: cred.id,
|
|
12047
12871
|
version: cred.version,
|
|
@@ -12117,17 +12941,29 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
12117
12941
|
cred = await refreshSingleFlight(cred, ref);
|
|
12118
12942
|
} catch (error) {
|
|
12119
12943
|
if (isPermanentRefreshError(error)) {
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12944
|
+
let handled = false;
|
|
12945
|
+
if (options.transitionPermanentRefreshFailure) {
|
|
12946
|
+
try {
|
|
12947
|
+
handled = await options.transitionPermanentRefreshFailure({
|
|
12948
|
+
workspaceId: cred.workspaceId,
|
|
12949
|
+
connectionId: cred.id,
|
|
12950
|
+
connectionVersion: cred.version,
|
|
12951
|
+
subjectId: cred.subjectId,
|
|
12952
|
+
providerDomain: cred.providerDomain,
|
|
12953
|
+
httpStatus: error.httpStatus,
|
|
12954
|
+
oauthErrorCode: error.oauthErrorCode
|
|
12955
|
+
});
|
|
12956
|
+
} catch {
|
|
12957
|
+
handled = false;
|
|
12958
|
+
}
|
|
12959
|
+
}
|
|
12960
|
+
if (!handled) {
|
|
12961
|
+
await deps.setStatus(db, input.workspaceId, "needs_reauth", error.message, {
|
|
12126
12962
|
id: cred.id,
|
|
12127
12963
|
version: cred.version,
|
|
12128
12964
|
subjectId: cred.subjectId
|
|
12129
|
-
}
|
|
12130
|
-
|
|
12965
|
+
}).catch(() => void 0);
|
|
12966
|
+
}
|
|
12131
12967
|
}
|
|
12132
12968
|
return authNeeded(ref, "refresh_failed", cred.id);
|
|
12133
12969
|
}
|
|
@@ -12182,10 +13018,12 @@ function canonicalResource(value) {
|
|
|
12182
13018
|
}
|
|
12183
13019
|
var ConnectionRefreshHttpError = class extends Error {
|
|
12184
13020
|
httpStatus;
|
|
12185
|
-
|
|
13021
|
+
oauthErrorCode;
|
|
13022
|
+
constructor(httpStatus, oauthErrorCode = null) {
|
|
12186
13023
|
super(`connection refresh failed with HTTP ${httpStatus}`);
|
|
12187
13024
|
this.name = "ConnectionRefreshHttpError";
|
|
12188
13025
|
this.httpStatus = httpStatus;
|
|
13026
|
+
this.oauthErrorCode = oauthErrorCode;
|
|
12189
13027
|
}
|
|
12190
13028
|
};
|
|
12191
13029
|
function isPermanentRefreshError(error) {
|
|
@@ -12317,8 +13155,10 @@ async function refreshOAuthConnectionCredential(cred, ref, settings, transportOp
|
|
|
12317
13155
|
throw new ConnectionRefreshHttpError(response.status);
|
|
12318
13156
|
}
|
|
12319
13157
|
if (!response.ok) {
|
|
12320
|
-
|
|
12321
|
-
|
|
13158
|
+
throw new ConnectionRefreshHttpError(
|
|
13159
|
+
response.status,
|
|
13160
|
+
await readOAuthRefreshErrorCode(response)
|
|
13161
|
+
);
|
|
12322
13162
|
}
|
|
12323
13163
|
const payload = await readResponseJsonBounded(
|
|
12324
13164
|
response,
|
|
@@ -12347,6 +13187,20 @@ async function refreshOAuthConnectionCredential(cred, ref, settings, transportOp
|
|
|
12347
13187
|
...scopeText ? { grantedScopes: scopeText.split(/\s+/).filter(Boolean) } : {}
|
|
12348
13188
|
};
|
|
12349
13189
|
}
|
|
13190
|
+
async function readOAuthRefreshErrorCode(response) {
|
|
13191
|
+
try {
|
|
13192
|
+
const payload = await readResponseJsonBounded(
|
|
13193
|
+
response,
|
|
13194
|
+
OAUTH_MAX_RESPONSE_BYTES,
|
|
13195
|
+
"OAuth refresh error response"
|
|
13196
|
+
);
|
|
13197
|
+
const code = stringValue(payload.error);
|
|
13198
|
+
return code && /^[a-z0-9_.-]{1,64}$/i.test(code) ? code : null;
|
|
13199
|
+
} catch {
|
|
13200
|
+
await cancelResponseBody(response);
|
|
13201
|
+
return null;
|
|
13202
|
+
}
|
|
13203
|
+
}
|
|
12350
13204
|
function expiresAtFromTokenResponse(payload, fallback) {
|
|
12351
13205
|
const expiresAt = stringValue(payload.expires_at);
|
|
12352
13206
|
if (expiresAt) {
|
|
@@ -12380,7 +13234,7 @@ async function cancelResponseBody(response) {
|
|
|
12380
13234
|
}
|
|
12381
13235
|
|
|
12382
13236
|
// src/workspace-artifacts.ts
|
|
12383
|
-
import { and as and16, desc as desc7, eq as eq16, lt as lt3, or as
|
|
13237
|
+
import { and as and16, desc as desc7, eq as eq16, lt as lt3, or as or5, sql as sql13 } from "drizzle-orm";
|
|
12384
13238
|
var WorkspaceArtifactNotFoundError = class extends Error {
|
|
12385
13239
|
name = "WorkspaceArtifactNotFoundError";
|
|
12386
13240
|
};
|
|
@@ -12475,7 +13329,7 @@ async function listWorkspaceArtifacts(db, workspaceId, options = {}) {
|
|
|
12475
13329
|
const cursor = options.cursor ? decodeListCursor(options.cursor) : null;
|
|
12476
13330
|
const visibility = cursor ? and16(
|
|
12477
13331
|
eq16(workspaceArtifacts.workspaceId, workspaceId),
|
|
12478
|
-
|
|
13332
|
+
or5(
|
|
12479
13333
|
lt3(workspaceArtifacts.updatedAt, cursor.updatedAt),
|
|
12480
13334
|
and16(
|
|
12481
13335
|
eq16(workspaceArtifacts.updatedAt, cursor.updatedAt),
|
|
@@ -13345,6 +14199,29 @@ async function withWorkspaceUsageLock(db, workspaceId, fn) {
|
|
|
13345
14199
|
return await fn(scopedDb);
|
|
13346
14200
|
});
|
|
13347
14201
|
}
|
|
14202
|
+
async function resolveDocumentIndexAuthority(db, input) {
|
|
14203
|
+
const rows = await rawRows(
|
|
14204
|
+
db,
|
|
14205
|
+
sql14`
|
|
14206
|
+
select authority_kind, authority_workspace_id, authority_subject_id
|
|
14207
|
+
from opengeni_private.resolve_document_index_authority(
|
|
14208
|
+
${input.accountId},
|
|
14209
|
+
${input.workspaceId},
|
|
14210
|
+
${input.documentId}
|
|
14211
|
+
)
|
|
14212
|
+
`
|
|
14213
|
+
);
|
|
14214
|
+
const row = rows[0];
|
|
14215
|
+
if (!row) return null;
|
|
14216
|
+
if (row.authority_kind !== "organization" && row.authority_kind !== "workspace" && row.authority_kind !== "personal") {
|
|
14217
|
+
throw new Error(`Stored document authority kind is invalid: ${row.authority_kind}`);
|
|
14218
|
+
}
|
|
14219
|
+
return {
|
|
14220
|
+
authorityKind: row.authority_kind,
|
|
14221
|
+
authorityWorkspaceId: row.authority_workspace_id,
|
|
14222
|
+
authoritySubjectId: row.authority_subject_id
|
|
14223
|
+
};
|
|
14224
|
+
}
|
|
13348
14225
|
async function withAccountRls(db, accountId, fn) {
|
|
13349
14226
|
return await withRlsContext(db, { accountId, workspaceId: null }, fn);
|
|
13350
14227
|
}
|
|
@@ -13967,7 +14844,7 @@ async function upsertGitHubInstallation(db, input) {
|
|
|
13967
14844
|
},
|
|
13968
14845
|
// This legacy metadata helper cannot mutate an owner-authorized row.
|
|
13969
14846
|
// New bindings use bindAuthorizedGitHubInstallationRepositories.
|
|
13970
|
-
setWhere:
|
|
14847
|
+
setWhere: isNull6(githubInstallations.authorityNonce)
|
|
13971
14848
|
}).returning();
|
|
13972
14849
|
if (!row) {
|
|
13973
14850
|
throw new Error("Failed to upsert GitHub installation");
|
|
@@ -14010,7 +14887,7 @@ async function bindGitHubInstallationRepositories(db, input) {
|
|
|
14010
14887
|
updatedAt: /* @__PURE__ */ new Date()
|
|
14011
14888
|
},
|
|
14012
14889
|
// Preserve the immutable authority + allowlist audit boundary.
|
|
14013
|
-
setWhere:
|
|
14890
|
+
setWhere: isNull6(githubInstallations.authorityNonce)
|
|
14014
14891
|
}).returning();
|
|
14015
14892
|
if (!row) {
|
|
14016
14893
|
throw new Error("Failed to bind GitHub installation");
|
|
@@ -14463,7 +15340,7 @@ async function sumUsageQuantity(db, input) {
|
|
|
14463
15340
|
eq17(usageEvents.eventType, input.eventType),
|
|
14464
15341
|
...input.accountId ? [eq17(usageEvents.accountId, input.accountId)] : [],
|
|
14465
15342
|
...input.workspaceId ? [eq17(usageEvents.workspaceId, input.workspaceId)] : [],
|
|
14466
|
-
...input.since ? [
|
|
15343
|
+
...input.since ? [gt5(usageEvents.occurredAt, input.since)] : []
|
|
14467
15344
|
];
|
|
14468
15345
|
const [{ total } = { total: 0 }] = await scopedDb.select({
|
|
14469
15346
|
total: sql14`coalesce(sum(${usageEvents.quantity}), 0)`
|
|
@@ -14619,6 +15496,20 @@ async function countScheduledTasksForWorkspace(db, workspaceId) {
|
|
|
14619
15496
|
return Number(count);
|
|
14620
15497
|
});
|
|
14621
15498
|
}
|
|
15499
|
+
var ConnectionDisconnectIdempotencyError = class extends Error {
|
|
15500
|
+
code = "IDEMPOTENCY_KEY_REUSED";
|
|
15501
|
+
constructor() {
|
|
15502
|
+
super("The disconnect idempotency key was already used with different input");
|
|
15503
|
+
this.name = "ConnectionDisconnectIdempotencyError";
|
|
15504
|
+
}
|
|
15505
|
+
};
|
|
15506
|
+
var ConnectionDisconnectGenerationError = class extends Error {
|
|
15507
|
+
code = "CONNECTION_GENERATION_CHANGED";
|
|
15508
|
+
constructor() {
|
|
15509
|
+
super("The disconnect operation belongs to an older connection generation");
|
|
15510
|
+
this.name = "ConnectionDisconnectGenerationError";
|
|
15511
|
+
}
|
|
15512
|
+
};
|
|
14622
15513
|
async function createFileUpload(db, input) {
|
|
14623
15514
|
return await withRlsContext(
|
|
14624
15515
|
db,
|
|
@@ -14674,6 +15565,25 @@ async function requireFile(db, workspaceId, fileId) {
|
|
|
14674
15565
|
}
|
|
14675
15566
|
return file;
|
|
14676
15567
|
}
|
|
15568
|
+
async function getFiles(db, workspaceId, fileIds) {
|
|
15569
|
+
const ids = [...new Set(fileIds)];
|
|
15570
|
+
if (ids.length === 0) return [];
|
|
15571
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
15572
|
+
const rows = await scopedDb.select().from(files).where(and17(eq17(files.workspaceId, workspaceId), inArray11(files.id, ids)));
|
|
15573
|
+
return rows.map(mapFile);
|
|
15574
|
+
});
|
|
15575
|
+
}
|
|
15576
|
+
function durableUserHistoryItem(prompt, resources) {
|
|
15577
|
+
const attachmentRefs = resources.filter(
|
|
15578
|
+
(resource) => resource.kind === "file"
|
|
15579
|
+
);
|
|
15580
|
+
return sanitizeModelPayload({
|
|
15581
|
+
type: "message",
|
|
15582
|
+
role: "user",
|
|
15583
|
+
content: prompt,
|
|
15584
|
+
...attachmentRefs.length > 0 ? { [MODEL_ATTACHMENT_REFS_FIELD]: attachmentRefs } : {}
|
|
15585
|
+
});
|
|
15586
|
+
}
|
|
14677
15587
|
async function getRetainedFileArtifact(db, workspaceId, fileId) {
|
|
14678
15588
|
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
14679
15589
|
const [row] = await scopedDb.select({
|
|
@@ -15251,11 +16161,11 @@ async function upsertCapabilityCatalogItem(db, input) {
|
|
|
15251
16161
|
async function listCapabilityCatalogItems(db, workspaceId) {
|
|
15252
16162
|
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
15253
16163
|
const rows = await scopedDb.select().from(capabilityCatalogItems).where(
|
|
15254
|
-
|
|
16164
|
+
or6(
|
|
15255
16165
|
eq17(capabilityCatalogItems.workspaceId, workspaceId),
|
|
15256
16166
|
and17(
|
|
15257
|
-
|
|
15258
|
-
|
|
16167
|
+
isNull6(capabilityCatalogItems.workspaceId),
|
|
16168
|
+
or6(
|
|
15259
16169
|
ne(capabilityCatalogItems.source, registryCapabilitySource),
|
|
15260
16170
|
eq17(capabilityCatalogItems.stale, false)
|
|
15261
16171
|
)
|
|
@@ -15277,9 +16187,9 @@ async function getCapabilityCatalogItem(db, workspaceId, capabilityId) {
|
|
|
15277
16187
|
const [row] = await scopedDb.select().from(capabilityCatalogItems).where(
|
|
15278
16188
|
and17(
|
|
15279
16189
|
eq17(capabilityCatalogItems.id, capabilityId),
|
|
15280
|
-
|
|
16190
|
+
or6(
|
|
15281
16191
|
eq17(capabilityCatalogItems.workspaceId, workspaceId),
|
|
15282
|
-
|
|
16192
|
+
isNull6(capabilityCatalogItems.workspaceId)
|
|
15283
16193
|
)
|
|
15284
16194
|
)
|
|
15285
16195
|
).orderBy(asc8(sql14`(${capabilityCatalogItems.workspaceId} is null)`)).limit(1);
|
|
@@ -15387,12 +16297,12 @@ async function listEnabledMcpCapabilityServers(db, workspaceId) {
|
|
|
15387
16297
|
}).from(capabilityInstallations).innerJoin(
|
|
15388
16298
|
capabilityCatalogItems,
|
|
15389
16299
|
and17(
|
|
15390
|
-
|
|
16300
|
+
or6(
|
|
15391
16301
|
eq17(
|
|
15392
16302
|
capabilityInstallations.workspaceId,
|
|
15393
16303
|
capabilityCatalogItems.workspaceId
|
|
15394
16304
|
),
|
|
15395
|
-
|
|
16305
|
+
isNull6(capabilityCatalogItems.workspaceId)
|
|
15396
16306
|
),
|
|
15397
16307
|
eq17(capabilityInstallations.capabilityId, capabilityCatalogItems.id)
|
|
15398
16308
|
)
|
|
@@ -15501,10 +16411,10 @@ var connectionMetadataColumns = {
|
|
|
15501
16411
|
updatedAt: connections.updatedAt
|
|
15502
16412
|
};
|
|
15503
16413
|
function connectionSubjectVisibility(subjectId) {
|
|
15504
|
-
return subjectId ?
|
|
16414
|
+
return subjectId ? or6(isNull6(connections.subjectId), eq17(connections.subjectId, subjectId)) : isNull6(connections.subjectId);
|
|
15505
16415
|
}
|
|
15506
16416
|
function connectionExactSubject(subjectId) {
|
|
15507
|
-
return subjectId ? eq17(connections.subjectId, subjectId) :
|
|
16417
|
+
return subjectId ? eq17(connections.subjectId, subjectId) : isNull6(connections.subjectId);
|
|
15508
16418
|
}
|
|
15509
16419
|
function personalSlackCanonicalConnectionOrder() {
|
|
15510
16420
|
return [
|
|
@@ -15617,6 +16527,124 @@ async function updateConnection(db, input) {
|
|
|
15617
16527
|
async (scopedDb) => await updateConnectionInScope(scopedDb, input)
|
|
15618
16528
|
);
|
|
15619
16529
|
}
|
|
16530
|
+
async function transitionConnectionStateInScope(db, input) {
|
|
16531
|
+
const [row] = await db.update(connections).set({
|
|
16532
|
+
...input.status !== void 0 ? { status: input.status } : {},
|
|
16533
|
+
metadata: input.metadata,
|
|
16534
|
+
...input.lastError !== void 0 ? { lastError: input.lastError } : {},
|
|
16535
|
+
version: sql14`${connections.version} + 1`,
|
|
16536
|
+
...input.updatedBySubjectId !== void 0 ? { updatedBySubjectId: input.updatedBySubjectId } : {},
|
|
16537
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
16538
|
+
}).where(
|
|
16539
|
+
and17(
|
|
16540
|
+
eq17(connections.workspaceId, input.workspaceId),
|
|
16541
|
+
eq17(connections.id, input.connectionId),
|
|
16542
|
+
connectionSubjectVisibility(input.visibleToSubjectId),
|
|
16543
|
+
eq17(connections.version, input.expectedVersion)
|
|
16544
|
+
)
|
|
16545
|
+
).returning(connectionMetadataColumns);
|
|
16546
|
+
return row ? mapConnectionMetadata(row) : null;
|
|
16547
|
+
}
|
|
16548
|
+
async function transitionConnectionState(db, input) {
|
|
16549
|
+
return await withConnectionSubjectRls(
|
|
16550
|
+
db,
|
|
16551
|
+
input.workspaceId,
|
|
16552
|
+
input.visibleToSubjectId,
|
|
16553
|
+
async (scopedDb) => await transitionConnectionStateInScope(scopedDb, input)
|
|
16554
|
+
);
|
|
16555
|
+
}
|
|
16556
|
+
var connectionDisconnectOperationColumns = {
|
|
16557
|
+
accountId: connectionDisconnectOperations.accountId,
|
|
16558
|
+
workspaceId: connectionDisconnectOperations.workspaceId,
|
|
16559
|
+
connectionId: connectionDisconnectOperations.connectionId,
|
|
16560
|
+
subjectId: connectionDisconnectOperations.subjectId,
|
|
16561
|
+
idempotencyKey: connectionDisconnectOperations.idempotencyKey,
|
|
16562
|
+
expectedVersion: connectionDisconnectOperations.expectedVersion,
|
|
16563
|
+
resultVersion: connectionDisconnectOperations.resultVersion
|
|
16564
|
+
};
|
|
16565
|
+
async function disconnectConnectionIdempotently(db, input) {
|
|
16566
|
+
return await withConnectionSubjectRls(
|
|
16567
|
+
db,
|
|
16568
|
+
input.workspaceId,
|
|
16569
|
+
input.subjectId,
|
|
16570
|
+
async (scopedDb) => {
|
|
16571
|
+
const [connectionRow] = await scopedDb.select(connectionMetadataColumns).from(connections).where(
|
|
16572
|
+
and17(
|
|
16573
|
+
eq17(connections.accountId, input.accountId),
|
|
16574
|
+
eq17(connections.workspaceId, input.workspaceId),
|
|
16575
|
+
eq17(connections.id, input.connectionId),
|
|
16576
|
+
eq17(connections.subjectId, input.subjectId)
|
|
16577
|
+
)
|
|
16578
|
+
).for("update");
|
|
16579
|
+
if (!connectionRow) return null;
|
|
16580
|
+
const connection = mapConnectionMetadata(connectionRow);
|
|
16581
|
+
const [replayed] = await scopedDb.select(connectionDisconnectOperationColumns).from(connectionDisconnectOperations).where(
|
|
16582
|
+
and17(
|
|
16583
|
+
eq17(connectionDisconnectOperations.workspaceId, input.workspaceId),
|
|
16584
|
+
eq17(connectionDisconnectOperations.subjectId, input.subjectId),
|
|
16585
|
+
eq17(connectionDisconnectOperations.idempotencyKey, input.idempotencyKey)
|
|
16586
|
+
)
|
|
16587
|
+
).limit(1);
|
|
16588
|
+
if (replayed) {
|
|
16589
|
+
if (replayed.accountId !== input.accountId || replayed.connectionId !== input.connectionId || replayed.expectedVersion !== input.expectedVersion) {
|
|
16590
|
+
throw new ConnectionDisconnectIdempotencyError();
|
|
16591
|
+
}
|
|
16592
|
+
if (connection.version !== replayed.resultVersion) {
|
|
16593
|
+
throw new ConnectionDisconnectGenerationError();
|
|
16594
|
+
}
|
|
16595
|
+
return connection;
|
|
16596
|
+
}
|
|
16597
|
+
if (connection.version !== input.expectedVersion) {
|
|
16598
|
+
throw new ConnectionDisconnectGenerationError();
|
|
16599
|
+
}
|
|
16600
|
+
const [inserted] = await scopedDb.insert(connectionDisconnectOperations).values({
|
|
16601
|
+
accountId: input.accountId,
|
|
16602
|
+
workspaceId: input.workspaceId,
|
|
16603
|
+
connectionId: input.connectionId,
|
|
16604
|
+
subjectId: input.subjectId,
|
|
16605
|
+
idempotencyKey: input.idempotencyKey,
|
|
16606
|
+
expectedVersion: input.expectedVersion,
|
|
16607
|
+
resultVersion: input.expectedVersion + 1
|
|
16608
|
+
}).onConflictDoNothing().returning(connectionDisconnectOperationColumns);
|
|
16609
|
+
if (!inserted) {
|
|
16610
|
+
const [conflictingKey] = await scopedDb.select(connectionDisconnectOperationColumns).from(connectionDisconnectOperations).where(
|
|
16611
|
+
and17(
|
|
16612
|
+
eq17(connectionDisconnectOperations.workspaceId, input.workspaceId),
|
|
16613
|
+
eq17(connectionDisconnectOperations.subjectId, input.subjectId),
|
|
16614
|
+
eq17(connectionDisconnectOperations.idempotencyKey, input.idempotencyKey)
|
|
16615
|
+
)
|
|
16616
|
+
).limit(1);
|
|
16617
|
+
if (!conflictingKey || conflictingKey.accountId !== input.accountId || conflictingKey.connectionId !== input.connectionId || conflictingKey.expectedVersion !== input.expectedVersion) {
|
|
16618
|
+
throw new ConnectionDisconnectIdempotencyError();
|
|
16619
|
+
}
|
|
16620
|
+
if (connection.version !== conflictingKey.resultVersion) {
|
|
16621
|
+
throw new ConnectionDisconnectGenerationError();
|
|
16622
|
+
}
|
|
16623
|
+
return connection;
|
|
16624
|
+
}
|
|
16625
|
+
const [updated] = await scopedDb.update(connections).set({
|
|
16626
|
+
status: "revoked",
|
|
16627
|
+
metadata: input.metadata,
|
|
16628
|
+
...input.lastError !== void 0 ? { lastError: input.lastError } : {},
|
|
16629
|
+
version: sql14`${connections.version} + 1`,
|
|
16630
|
+
...input.updatedBySubjectId !== void 0 ? { updatedBySubjectId: input.updatedBySubjectId } : {},
|
|
16631
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
16632
|
+
}).where(
|
|
16633
|
+
and17(
|
|
16634
|
+
eq17(connections.accountId, input.accountId),
|
|
16635
|
+
eq17(connections.workspaceId, input.workspaceId),
|
|
16636
|
+
eq17(connections.id, input.connectionId),
|
|
16637
|
+
eq17(connections.subjectId, input.subjectId),
|
|
16638
|
+
eq17(connections.version, input.expectedVersion)
|
|
16639
|
+
)
|
|
16640
|
+
).returning(connectionMetadataColumns);
|
|
16641
|
+
if (!updated) {
|
|
16642
|
+
throw new ConnectionDisconnectGenerationError();
|
|
16643
|
+
}
|
|
16644
|
+
return mapConnectionMetadata(updated);
|
|
16645
|
+
}
|
|
16646
|
+
);
|
|
16647
|
+
}
|
|
15620
16648
|
async function revokeConnectionInScope(db, workspaceId, connectionId, updatedBySubjectId, expectedVersion) {
|
|
15621
16649
|
const [row] = await db.update(connections).set({
|
|
15622
16650
|
status: "revoked",
|
|
@@ -15645,12 +16673,18 @@ async function revokeConnectionInScope(db, workspaceId, connectionId, updatedByS
|
|
|
15645
16673
|
).returning(connectionMetadataColumns);
|
|
15646
16674
|
return row ? mapConnectionMetadata(row) : null;
|
|
15647
16675
|
}
|
|
15648
|
-
async function revokeConnection(db, workspaceId, connectionId, updatedBySubjectId) {
|
|
16676
|
+
async function revokeConnection(db, workspaceId, connectionId, updatedBySubjectId, expectedVersion) {
|
|
15649
16677
|
return await withConnectionSubjectRls(
|
|
15650
16678
|
db,
|
|
15651
16679
|
workspaceId,
|
|
15652
16680
|
updatedBySubjectId,
|
|
15653
|
-
async (scopedDb) => await revokeConnectionInScope(
|
|
16681
|
+
async (scopedDb) => await revokeConnectionInScope(
|
|
16682
|
+
scopedDb,
|
|
16683
|
+
workspaceId,
|
|
16684
|
+
connectionId,
|
|
16685
|
+
updatedBySubjectId,
|
|
16686
|
+
expectedVersion
|
|
16687
|
+
)
|
|
15654
16688
|
);
|
|
15655
16689
|
}
|
|
15656
16690
|
async function resolveSlackInstallationRoute(db, slackTeamId) {
|
|
@@ -15715,7 +16749,7 @@ async function enqueueSlackInteractionInbox(db, input) {
|
|
|
15715
16749
|
const [existing] = await scopedDb.select().from(slackInteractionInbox).where(
|
|
15716
16750
|
and17(
|
|
15717
16751
|
eq17(slackInteractionInbox.connectionId, input.connectionId),
|
|
15718
|
-
|
|
16752
|
+
or6(
|
|
15719
16753
|
eq17(slackInteractionInbox.providerEventId, input.providerEventId),
|
|
15720
16754
|
eq17(slackInteractionInbox.providerMessageId, input.providerMessageId)
|
|
15721
16755
|
)
|
|
@@ -15929,7 +16963,7 @@ async function bindSlackInteractionSession(db, input) {
|
|
|
15929
16963
|
eq17(slackInteractions.id, input.id),
|
|
15930
16964
|
eq17(slackInteractions.owningSubjectId, input.owningSubjectId),
|
|
15931
16965
|
eq17(slackInteractions.sessionReservationId, input.sessionId),
|
|
15932
|
-
|
|
16966
|
+
isNull6(slackInteractions.sessionId)
|
|
15933
16967
|
)
|
|
15934
16968
|
).returning();
|
|
15935
16969
|
if (row) return mapSlackInteraction(row);
|
|
@@ -16431,8 +17465,8 @@ async function claimSlackBotPostOperation(db, input) {
|
|
|
16431
17465
|
}).where(
|
|
16432
17466
|
and17(
|
|
16433
17467
|
eq17(slackBotPostOperations.id, existing.id),
|
|
16434
|
-
|
|
16435
|
-
|
|
17468
|
+
or6(
|
|
17469
|
+
isNull6(slackBotPostOperations.claimHolderId),
|
|
16436
17470
|
lte2(slackBotPostOperations.claimExpiresAt, sql14`now()`)
|
|
16437
17471
|
)
|
|
16438
17472
|
)
|
|
@@ -16628,7 +17662,7 @@ async function markSlackBotDeleteOperationProviderStarted(db, input) {
|
|
|
16628
17662
|
eq17(slackBotDeleteOperations.connectionId, input.connectionId),
|
|
16629
17663
|
eq17(slackBotDeleteOperations.operationId, input.operationId),
|
|
16630
17664
|
eq17(slackBotDeleteOperations.claimHolderId, input.claimHolderId),
|
|
16631
|
-
|
|
17665
|
+
or6(
|
|
16632
17666
|
eq17(slackBotDeleteOperations.status, "pending"),
|
|
16633
17667
|
eq17(slackBotDeleteOperations.status, "outcome_unknown")
|
|
16634
17668
|
)
|
|
@@ -16736,7 +17770,7 @@ async function loadConnectionCredentialForBroker(db, settings, input) {
|
|
|
16736
17770
|
"connection credential present but OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is not configured"
|
|
16737
17771
|
);
|
|
16738
17772
|
}
|
|
16739
|
-
const subjectPredicate = input.allowSubjectOwned ? connectionExactSubject(input.subjectId) :
|
|
17773
|
+
const subjectPredicate = input.allowSubjectOwned ? connectionExactSubject(input.subjectId) : isNull6(connections.subjectId);
|
|
16740
17774
|
const conditions = [
|
|
16741
17775
|
eq17(connections.workspaceId, input.workspaceId),
|
|
16742
17776
|
subjectPredicate
|
|
@@ -20219,7 +21253,7 @@ async function withCodexCapacityMutation(db, input, mutate) {
|
|
|
20219
21253
|
eq17(codexCapacityWaiters.workspaceId, input.workspaceId),
|
|
20220
21254
|
eq17(codexCapacityWaiters.status, "waiting"),
|
|
20221
21255
|
...input.policyHash !== void 0 ? [
|
|
20222
|
-
input.policyHash === null ?
|
|
21256
|
+
input.policyHash === null ? isNull6(codexCapacityWaiters.policyHash) : eq17(codexCapacityWaiters.policyHash, input.policyHash)
|
|
20223
21257
|
] : []
|
|
20224
21258
|
)
|
|
20225
21259
|
).returning();
|
|
@@ -20312,7 +21346,7 @@ async function supersedeCodexCapacityWaitInTransaction(tx, input) {
|
|
|
20312
21346
|
eq17(sessionTurns.workspaceId, input.session.workspaceId),
|
|
20313
21347
|
eq17(sessionTurns.id, input.blockedTurn.id),
|
|
20314
21348
|
eq17(sessionTurns.status, "waiting_capacity"),
|
|
20315
|
-
|
|
21349
|
+
isNull6(sessionTurns.activeAttemptId),
|
|
20316
21350
|
eq17(sessionTurns.executionGeneration, input.waiter.blockedTurnGeneration)
|
|
20317
21351
|
)
|
|
20318
21352
|
).returning({ id: sessionTurns.id });
|
|
@@ -20590,7 +21624,7 @@ async function reconcileCodexCapacityWait(db, input, decide, policy) {
|
|
|
20590
21624
|
eq17(sessionTurns.workspaceId, input.workspaceId),
|
|
20591
21625
|
eq17(sessionTurns.id, blockedTurn.id),
|
|
20592
21626
|
eq17(sessionTurns.status, "waiting_capacity"),
|
|
20593
|
-
|
|
21627
|
+
isNull6(sessionTurns.activeAttemptId),
|
|
20594
21628
|
eq17(sessionTurns.executionGeneration, waiter.blockedTurnGeneration)
|
|
20595
21629
|
)
|
|
20596
21630
|
).returning({ id: sessionTurns.id });
|
|
@@ -21411,7 +22445,7 @@ async function setInitialActiveCodexCredential(db, workspaceId, credentialId) {
|
|
|
21411
22445
|
const rows = await scopedDb.update(codexRotationSettings).set({ activeCredentialId: credentialId, updatedAt: /* @__PURE__ */ new Date() }).where(
|
|
21412
22446
|
and17(
|
|
21413
22447
|
eq17(codexRotationSettings.workspaceId, workspaceId),
|
|
21414
|
-
|
|
22448
|
+
isNull6(codexRotationSettings.activeCredentialId)
|
|
21415
22449
|
)
|
|
21416
22450
|
).returning({ id: codexRotationSettings.id });
|
|
21417
22451
|
return rows.length > 0;
|
|
@@ -21560,8 +22594,8 @@ async function setSessionCodexPin(db, workspaceId, sessionId, pinnedCredentialId
|
|
|
21560
22594
|
];
|
|
21561
22595
|
if (options.expected) {
|
|
21562
22596
|
conditions.push(
|
|
21563
|
-
options.expected.pinnedCredentialId === null ?
|
|
21564
|
-
options.expected.pinSource === null ?
|
|
22597
|
+
options.expected.pinnedCredentialId === null ? isNull6(sessions.codexPinnedCredentialId) : eq17(sessions.codexPinnedCredentialId, options.expected.pinnedCredentialId),
|
|
22598
|
+
options.expected.pinSource === null ? isNull6(sessions.codexPinSource) : eq17(sessions.codexPinSource, options.expected.pinSource)
|
|
21565
22599
|
);
|
|
21566
22600
|
}
|
|
21567
22601
|
const updated = await scopedDb.update(sessions).set({
|
|
@@ -22911,7 +23945,7 @@ async function listSessionSpawnDenials(db, workspaceId, options = {}) {
|
|
|
22911
23945
|
const rows = await scopedDb.select().from(sessionSpawnDenials).where(
|
|
22912
23946
|
and17(
|
|
22913
23947
|
eq17(sessionSpawnDenials.workspaceId, workspaceId),
|
|
22914
|
-
options.parentSessionId === void 0 ? void 0 : options.parentSessionId === null ?
|
|
23948
|
+
options.parentSessionId === void 0 ? void 0 : options.parentSessionId === null ? isNull6(sessionSpawnDenials.parentSessionId) : eq17(sessionSpawnDenials.parentSessionId, options.parentSessionId)
|
|
22915
23949
|
)
|
|
22916
23950
|
).orderBy(desc8(sessionSpawnDenials.createdAt), desc8(sessionSpawnDenials.id)).limit(Math.max(1, Math.min(options.limit ?? 100, 1e3)));
|
|
22917
23951
|
return rows.map(mapSessionSpawnDenial);
|
|
@@ -23229,7 +24263,7 @@ function sessionFilters(options) {
|
|
|
23229
24263
|
if (Object.prototype.hasOwnProperty.call(options, "parentSessionId")) {
|
|
23230
24264
|
const parentSessionId = options.parentSessionId;
|
|
23231
24265
|
if (parentSessionId === null) {
|
|
23232
|
-
filters.push(
|
|
24266
|
+
filters.push(isNull6(sessions.parentSessionId));
|
|
23233
24267
|
} else if (parentSessionId !== void 0) {
|
|
23234
24268
|
filters.push(eq17(sessions.parentSessionId, parentSessionId));
|
|
23235
24269
|
}
|
|
@@ -23238,7 +24272,7 @@ function sessionFilters(options) {
|
|
|
23238
24272
|
if (search) {
|
|
23239
24273
|
const pattern = `%${search.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_")}%`;
|
|
23240
24274
|
filters.push(
|
|
23241
|
-
|
|
24275
|
+
or6(ilike(sessions.title, pattern), ilike(sessions.initialMessage, pattern))
|
|
23242
24276
|
);
|
|
23243
24277
|
}
|
|
23244
24278
|
return filters;
|
|
@@ -23271,7 +24305,7 @@ function sessionAuthorizationScopeFilter(scope) {
|
|
|
23271
24305
|
)
|
|
23272
24306
|
select id from authorized_sessions
|
|
23273
24307
|
)` : sql14`false`;
|
|
23274
|
-
return
|
|
24308
|
+
return or6(exact, descendants);
|
|
23275
24309
|
}
|
|
23276
24310
|
async function sessionIdsCoveredByAuthorizationRoots(db, workspaceId, sessionIds, scope) {
|
|
23277
24311
|
if (sessionIds.length === 0) return /* @__PURE__ */ new Set();
|
|
@@ -23453,7 +24487,7 @@ async function listSessionsForSubject(db, workspaceId, options) {
|
|
|
23453
24487
|
).where(
|
|
23454
24488
|
and17(
|
|
23455
24489
|
...filters,
|
|
23456
|
-
|
|
24490
|
+
or6(isNull6(sessionPins.id), eq17(sessionPins.pinned, false))
|
|
23457
24491
|
)
|
|
23458
24492
|
).orderBy(desc8(sessions.updatedAt), desc8(sessions.id)).limit(limit);
|
|
23459
24493
|
pageIds = ordinaryIdRows.map((row) => row.id);
|
|
@@ -23466,14 +24500,14 @@ async function listSessionsForSubject(db, workspaceId, options) {
|
|
|
23466
24500
|
lte2(sessionListSnapshots.expiresAt, now)
|
|
23467
24501
|
)
|
|
23468
24502
|
);
|
|
23469
|
-
const snapshotSearchFilter = searchFilter ? eq17(sessionListSnapshots.search, searchFilter) :
|
|
24503
|
+
const snapshotSearchFilter = searchFilter ? eq17(sessionListSnapshots.search, searchFilter) : isNull6(sessionListSnapshots.search);
|
|
23470
24504
|
const [reusableSnapshot] = await tx.select().from(sessionListSnapshots).where(
|
|
23471
24505
|
and17(
|
|
23472
24506
|
eq17(sessionListSnapshots.workspaceId, workspaceId),
|
|
23473
24507
|
eq17(sessionListSnapshots.subjectId, options.subjectId),
|
|
23474
24508
|
eq17(sessionListSnapshots.parentSessionFilter, parentFilter),
|
|
23475
24509
|
snapshotSearchFilter,
|
|
23476
|
-
|
|
24510
|
+
gt5(
|
|
23477
24511
|
sessionListSnapshots.createdAt,
|
|
23478
24512
|
new Date(now.getTime() - SESSION_LIST_SNAPSHOT_REUSE_MS)
|
|
23479
24513
|
)
|
|
@@ -23494,7 +24528,7 @@ async function listSessionsForSubject(db, workspaceId, options) {
|
|
|
23494
24528
|
).where(
|
|
23495
24529
|
and17(
|
|
23496
24530
|
...filters,
|
|
23497
|
-
|
|
24531
|
+
or6(isNull6(sessionPins.id), eq17(sessionPins.pinned, false))
|
|
23498
24532
|
)
|
|
23499
24533
|
).orderBy(desc8(sessions.updatedAt), desc8(sessions.id)).limit(SESSION_LIST_SNAPSHOT_MAX_IDS + 1);
|
|
23500
24534
|
if (ordinaryIdRows.length > SESSION_LIST_SNAPSHOT_MAX_IDS) {
|
|
@@ -23568,7 +24602,7 @@ async function listSessionsForSubject(db, workspaceId, options) {
|
|
|
23568
24602
|
eq17(sessions.workspaceId, workspaceId),
|
|
23569
24603
|
inArray11(sessions.id, pageIds),
|
|
23570
24604
|
...options.authorizationScope ? [sessionAuthorizationScopeFilter(options.authorizationScope)] : [],
|
|
23571
|
-
|
|
24605
|
+
or6(isNull6(sessionPins.id), eq17(sessionPins.pinned, false))
|
|
23572
24606
|
)
|
|
23573
24607
|
);
|
|
23574
24608
|
const ordinaryById = new Map(ordinaryRows.map((row) => [row.session.id, row]));
|
|
@@ -23758,7 +24792,7 @@ async function listSessions(db, workspaceId, limitOrOptions = 50) {
|
|
|
23758
24792
|
if (Object.prototype.hasOwnProperty.call(options, "parentSessionId")) {
|
|
23759
24793
|
const parentSessionId = options.parentSessionId;
|
|
23760
24794
|
if (parentSessionId === null) {
|
|
23761
|
-
filters.push(
|
|
24795
|
+
filters.push(isNull6(sessions.parentSessionId));
|
|
23762
24796
|
} else if (parentSessionId !== void 0) {
|
|
23763
24797
|
filters.push(eq17(sessions.parentSessionId, parentSessionId));
|
|
23764
24798
|
}
|
|
@@ -23899,11 +24933,11 @@ async function listSessionDiscoverySummaries(db, workspaceId, options) {
|
|
|
23899
24933
|
if (orderBy === "createdAt" && options.cursor && (cursorSortRevision !== "0" || options.cursor.snapshotRevision !== "0")) {
|
|
23900
24934
|
throw new Error("sessions_list created-order cursor cannot carry activity revisions");
|
|
23901
24935
|
}
|
|
23902
|
-
const cursorPredicate = options.cursor ? orderBy === "updatedAt" ?
|
|
24936
|
+
const cursorPredicate = options.cursor ? orderBy === "updatedAt" ? or6(
|
|
23903
24937
|
sql14`${sessions.activityRevision} < ${cursorSortRevision}::text::bigint`,
|
|
23904
24938
|
and17(
|
|
23905
24939
|
sql14`${sessions.activityRevision} = ${cursorSortRevision}::text::bigint`,
|
|
23906
|
-
|
|
24940
|
+
or6(
|
|
23907
24941
|
sql14`${sessions.updatedAt} < ${options.cursor.sortAt}::text::timestamptz`,
|
|
23908
24942
|
and17(
|
|
23909
24943
|
sql14`${sessions.updatedAt} = ${options.cursor.sortAt}::text::timestamptz`,
|
|
@@ -23911,7 +24945,7 @@ async function listSessionDiscoverySummaries(db, workspaceId, options) {
|
|
|
23911
24945
|
)
|
|
23912
24946
|
)
|
|
23913
24947
|
)
|
|
23914
|
-
) :
|
|
24948
|
+
) : or6(
|
|
23915
24949
|
// Cast through text deliberately. postgres.js otherwise infers a
|
|
23916
24950
|
// timestamptz parameter and serializes this exact cursor string via
|
|
23917
24951
|
// JS Date, which discards PostgreSQL's sub-millisecond precision.
|
|
@@ -24282,16 +25316,16 @@ async function listSessionEventPage(db, workspaceId, sessionId, options = {}) {
|
|
|
24282
25316
|
const filters = [
|
|
24283
25317
|
eq17(sessionEvents.workspaceId, workspaceId),
|
|
24284
25318
|
eq17(sessionEvents.sessionId, sessionId),
|
|
24285
|
-
|
|
25319
|
+
gt5(sessionEvents.sequence, after)
|
|
24286
25320
|
];
|
|
24287
25321
|
if (options.authoritativeLatest) {
|
|
24288
25322
|
filters.push(
|
|
24289
25323
|
and17(
|
|
24290
|
-
|
|
24291
|
-
|
|
25324
|
+
or6(
|
|
25325
|
+
isNull6(sessionEvents.turnAssociation),
|
|
24292
25326
|
eq17(sessionEvents.turnAssociation, "current")
|
|
24293
25327
|
),
|
|
24294
|
-
|
|
25328
|
+
isNull6(sessionEvents.duplicateOfEventId)
|
|
24295
25329
|
)
|
|
24296
25330
|
);
|
|
24297
25331
|
}
|
|
@@ -24306,7 +25340,7 @@ async function listSessionEventPage(db, workspaceId, sessionId, options = {}) {
|
|
|
24306
25340
|
filters.push(lt4(sessionEvents.sequence, cursor));
|
|
24307
25341
|
}
|
|
24308
25342
|
} else if (cursor !== void 0) {
|
|
24309
|
-
filters.push(
|
|
25343
|
+
filters.push(gt5(sessionEvents.sequence, cursor));
|
|
24310
25344
|
}
|
|
24311
25345
|
if (direction === "after" && before !== void 0 && before <= POSTGRES_INT_MAX) {
|
|
24312
25346
|
filters.push(lt4(sessionEvents.sequence, before));
|
|
@@ -24489,7 +25523,7 @@ async function listSessionEvents(db, workspaceId, sessionId, afterOrOptions = 0,
|
|
|
24489
25523
|
const filters = [
|
|
24490
25524
|
eq17(sessionEvents.workspaceId, workspaceId),
|
|
24491
25525
|
eq17(sessionEvents.sessionId, sessionId),
|
|
24492
|
-
|
|
25526
|
+
gt5(sessionEvents.sequence, after)
|
|
24493
25527
|
];
|
|
24494
25528
|
if (typeFilters.includeTypes.length > 0) {
|
|
24495
25529
|
filters.push(inArray11(sessionEvents.type, typeFilters.includeTypes));
|
|
@@ -24623,7 +25657,7 @@ async function listWorkspaceControlEvents(db, workspaceId, after = 0, limit = 50
|
|
|
24623
25657
|
const rows = await scopedDb.select().from(workspaceControlEvents).where(
|
|
24624
25658
|
and17(
|
|
24625
25659
|
eq17(workspaceControlEvents.workspaceId, workspaceId),
|
|
24626
|
-
|
|
25660
|
+
gt5(workspaceControlEvents.revision, cursor)
|
|
24627
25661
|
)
|
|
24628
25662
|
).orderBy(asc8(workspaceControlEvents.revision)).limit(boundedLimit);
|
|
24629
25663
|
return rows.map(mapWorkspaceControlEvent);
|
|
@@ -24769,21 +25803,6 @@ function validateAnsweredHumanInput(questions, answers) {
|
|
|
24769
25803
|
`Text question ${question.id} accepts one value`
|
|
24770
25804
|
);
|
|
24771
25805
|
}
|
|
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
25806
|
continue;
|
|
24788
25807
|
}
|
|
24789
25808
|
if (other && !question.allowOther) {
|
|
@@ -24856,7 +25875,7 @@ async function hasAcceptedRequiresActionAdvance(tx, input) {
|
|
|
24856
25875
|
eq17(sessionEvents.turnId, input.turnId),
|
|
24857
25876
|
eq17(sessionEvents.turnGeneration, input.turnGeneration),
|
|
24858
25877
|
inArray11(sessionEvents.type, ["user.approvalDecision", "user.humanInputResponse"]),
|
|
24859
|
-
|
|
25878
|
+
gt5(sessionEvents.sequence, trigger.sequence)
|
|
24860
25879
|
)
|
|
24861
25880
|
).limit(1);
|
|
24862
25881
|
return accepted !== void 0;
|
|
@@ -24994,6 +26013,26 @@ async function acceptSessionHumanInputResponse(db, input) {
|
|
|
24994
26013
|
occurredAt: now
|
|
24995
26014
|
}).returning();
|
|
24996
26015
|
if (!event) throw new Error("Failed to append human-input response");
|
|
26016
|
+
await mirrorSessionRealtimeContextInTransaction(tx, {
|
|
26017
|
+
accountId: session.accountId,
|
|
26018
|
+
workspaceId: input.workspaceId,
|
|
26019
|
+
sessionId: input.sessionId,
|
|
26020
|
+
sourceKind: "human_input_response",
|
|
26021
|
+
sourceId: event.id,
|
|
26022
|
+
turnId: turn.id,
|
|
26023
|
+
channel: response.outcome === "answered" || response.outcome === "skipped" ? "speakable" : null,
|
|
26024
|
+
text: renderRealtimeHumanInputResponseContext({
|
|
26025
|
+
requestId: request.id,
|
|
26026
|
+
questions: request.questions,
|
|
26027
|
+
response
|
|
26028
|
+
}),
|
|
26029
|
+
payload: {
|
|
26030
|
+
requestId: request.id,
|
|
26031
|
+
outcome: response.outcome,
|
|
26032
|
+
sourceEventId: event.id
|
|
26033
|
+
},
|
|
26034
|
+
now
|
|
26035
|
+
});
|
|
24997
26036
|
await tx.update(sessions).set({ lastSequence: session.lastSequence + 1, updatedAt: now }).where(eq17(sessions.id, session.id));
|
|
24998
26037
|
const workflowWakeRevision = await enqueueSessionWorkflowWakeInTransaction(
|
|
24999
26038
|
tx,
|
|
@@ -26054,7 +27093,7 @@ function exactPtyIdentityPredicates(identity) {
|
|
|
26054
27093
|
eq17(sandboxPtySessions.providerBackend, identity.providerBackend),
|
|
26055
27094
|
eq17(sandboxPtySessions.providerInstanceId, identity.providerInstanceId),
|
|
26056
27095
|
eq17(sandboxPtySessions.routeKind, identity.routeKind),
|
|
26057
|
-
identity.routeTargetId === null ?
|
|
27096
|
+
identity.routeTargetId === null ? isNull6(sandboxPtySessions.routeTargetId) : eq17(sandboxPtySessions.routeTargetId, identity.routeTargetId),
|
|
26058
27097
|
eq17(sandboxPtySessions.routeEpoch, identity.routeEpoch)
|
|
26059
27098
|
];
|
|
26060
27099
|
}
|
|
@@ -28079,7 +29118,7 @@ async function settleExactLostProviderWorkspaceBlockersTx(tx, input) {
|
|
|
28079
29118
|
eq17(sandboxWorkspaceMutationAdmissions.sandboxGroupId, input.sandboxGroupId),
|
|
28080
29119
|
eq17(sandboxWorkspaceMutationAdmissions.leaseEpoch, input.lostEpoch),
|
|
28081
29120
|
eq17(sandboxWorkspaceMutationAdmissions.providerInstanceId, input.lostInstanceId),
|
|
28082
|
-
|
|
29121
|
+
isNull6(sandboxWorkspaceMutationAdmissions.settledAt)
|
|
28083
29122
|
)
|
|
28084
29123
|
).returning({ id: sandboxWorkspaceMutationAdmissions.id });
|
|
28085
29124
|
const closedPtys = await tx.update(sandboxPtySessions).set({ status: "closed", closedAt: /* @__PURE__ */ new Date() }).where(
|
|
@@ -29711,8 +30750,8 @@ async function retainWorkspaceMutationProcess(db, input) {
|
|
|
29711
30750
|
and17(
|
|
29712
30751
|
eq17(sandboxRetainedProcesses.id, process.id),
|
|
29713
30752
|
eq17(sandboxRetainedProcesses.state, "active"),
|
|
29714
|
-
|
|
29715
|
-
|
|
30753
|
+
isNull6(sandboxRetainedProcesses.providerBindingKey),
|
|
30754
|
+
isNull6(sandboxRetainedProcesses.providerBinding)
|
|
29716
30755
|
)
|
|
29717
30756
|
).returning();
|
|
29718
30757
|
if (!bound) {
|
|
@@ -29917,8 +30956,8 @@ async function bindRetainedProcessProviderIdentity(db, input) {
|
|
|
29917
30956
|
eq17(sandboxRetainedProcesses.id, process.id),
|
|
29918
30957
|
eq17(sandboxRetainedProcesses.state, "active"),
|
|
29919
30958
|
eq17(sandboxRetainedProcesses.reconcileClaimId, input.claimId),
|
|
29920
|
-
|
|
29921
|
-
|
|
30959
|
+
isNull6(sandboxRetainedProcesses.providerBindingKey),
|
|
30960
|
+
isNull6(sandboxRetainedProcesses.providerBinding)
|
|
29922
30961
|
)
|
|
29923
30962
|
).returning();
|
|
29924
30963
|
if (!updated) {
|
|
@@ -31893,7 +32932,7 @@ async function setEnrollmentDisplayState(db, input) {
|
|
|
31893
32932
|
// Only write on a CHANGE to EITHER field — an unchanged display state must
|
|
31894
32933
|
// not churn a write on every reconnect Hello. `IS DISTINCT FROM` is the
|
|
31895
32934
|
// null-safe inequality (a plain `ne` skips NULL rows).
|
|
31896
|
-
|
|
32935
|
+
or6(
|
|
31897
32936
|
ne(enrollments.hasDisplay, input.hasDisplay),
|
|
31898
32937
|
sql14`${enrollments.desktopUnavailableReason} IS DISTINCT FROM ${input.desktopUnavailableReason}`
|
|
31899
32938
|
)
|
|
@@ -32588,7 +33627,7 @@ async function forceDrainOverLimitViewerOnlyBoxes(db, input) {
|
|
|
32588
33627
|
and17(
|
|
32589
33628
|
eq17(usageEvents.workspaceId, input.workspaceId),
|
|
32590
33629
|
eq17(usageEvents.eventType, "sandbox.warm_seconds"),
|
|
32591
|
-
|
|
33630
|
+
gt5(usageEvents.occurredAt, since)
|
|
32592
33631
|
)
|
|
32593
33632
|
);
|
|
32594
33633
|
if (Number(total) >= input.maxWarmSecondsPerWorkspace) {
|
|
@@ -34575,7 +35614,7 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
34575
35614
|
"acknowledged",
|
|
34576
35615
|
"settled"
|
|
34577
35616
|
]),
|
|
34578
|
-
|
|
35617
|
+
isNull6(sessionTurnAttempts.quiescedAt)
|
|
34579
35618
|
)
|
|
34580
35619
|
).orderBy(desc8(sessionAttemptInterruptions.requestedAt)).limit(1);
|
|
34581
35620
|
if (unquiescedInterruption) {
|
|
@@ -34623,7 +35662,7 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
34623
35662
|
eq17(sessions.accountId, session.accountId),
|
|
34624
35663
|
eq17(sessions.workspaceId, workspaceId),
|
|
34625
35664
|
eq17(sessions.id, sessionId),
|
|
34626
|
-
|
|
35665
|
+
isNull6(sessions.activeTurnId)
|
|
34627
35666
|
)
|
|
34628
35667
|
).returning({ id: sessions.id });
|
|
34629
35668
|
if (!claimedSession) {
|
|
@@ -35328,11 +36367,10 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
35328
36367
|
sessionId,
|
|
35329
36368
|
turnId: row.id,
|
|
35330
36369
|
position: Number(historyPosition),
|
|
35331
|
-
item:
|
|
35332
|
-
|
|
35333
|
-
|
|
35334
|
-
|
|
35335
|
-
}),
|
|
36370
|
+
item: durableUserHistoryItem(
|
|
36371
|
+
row.prompt,
|
|
36372
|
+
Array.isArray(row.resources) ? row.resources : []
|
|
36373
|
+
),
|
|
35336
36374
|
producerCodexCredentialId: null
|
|
35337
36375
|
});
|
|
35338
36376
|
const providerDelegatedTurn = isSessionRealtimeDelegationTurnMetadata(row.metadata);
|
|
@@ -35449,7 +36487,7 @@ async function markSessionAttemptQuiesced(db, input) {
|
|
|
35449
36487
|
and17(
|
|
35450
36488
|
eq17(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
35451
36489
|
eq17(sessionTurnAttempts.id, input.attemptId),
|
|
35452
|
-
|
|
36490
|
+
isNull6(sessionTurnAttempts.quiescedAt)
|
|
35453
36491
|
)
|
|
35454
36492
|
).returning({ id: sessionTurnAttempts.id });
|
|
35455
36493
|
if (!marked) {
|
|
@@ -35987,7 +37025,7 @@ async function peekSessionWork(db, workspaceId, sessionId) {
|
|
|
35987
37025
|
"user.approvalDecision",
|
|
35988
37026
|
"user.humanInputResponse"
|
|
35989
37027
|
]),
|
|
35990
|
-
|
|
37028
|
+
gt5(sessionEvents.sequence, currentTrigger.sequence)
|
|
35991
37029
|
)
|
|
35992
37030
|
).orderBy(desc8(sessionEvents.sequence), desc8(sessionEvents.id)).limit(1);
|
|
35993
37031
|
if (actionResponse) {
|
|
@@ -36452,11 +37490,11 @@ async function applySessionTurnSettlement(db, workspaceId, input, hooks = {}) {
|
|
|
36452
37490
|
activeTurnId: session.activeTurnId
|
|
36453
37491
|
};
|
|
36454
37492
|
}
|
|
37493
|
+
const humanInputRequests = input.runState?.humanInputRequests ?? [];
|
|
36455
37494
|
if (input.runState) {
|
|
36456
37495
|
if (input.turnStatus !== "requires_action" || input.sessionStatus !== "requires_action") {
|
|
36457
37496
|
throw new Error("A frozen run state requires a requires_action settlement");
|
|
36458
37497
|
}
|
|
36459
|
-
const humanInputRequests = input.runState.humanInputRequests ?? [];
|
|
36460
37498
|
for (const request of humanInputRequests) {
|
|
36461
37499
|
const parsedQuestions = request.questions.map(
|
|
36462
37500
|
(question) => HumanInputQuestionContract.parse(question)
|
|
@@ -36656,7 +37694,10 @@ async function applySessionTurnSettlement(db, workspaceId, input, hooks = {}) {
|
|
|
36656
37694
|
eq17(sessionHumanInputRequests.turnId, input.turnId),
|
|
36657
37695
|
eq17(sessionHumanInputRequests.status, "pending")
|
|
36658
37696
|
)
|
|
36659
|
-
).returning({
|
|
37697
|
+
).returning({
|
|
37698
|
+
id: sessionHumanInputRequests.id,
|
|
37699
|
+
questions: sessionHumanInputRequests.questions
|
|
37700
|
+
}) : [];
|
|
36660
37701
|
const terminalHumanInputEvents = terminalHumanInputRows.map(
|
|
36661
37702
|
(request) => ({
|
|
36662
37703
|
type: "user.humanInputResponse",
|
|
@@ -36721,6 +37762,69 @@ async function applySessionTurnSettlement(db, workspaceId, input, hooks = {}) {
|
|
|
36721
37762
|
};
|
|
36722
37763
|
});
|
|
36723
37764
|
const inserted = values.length > 0 ? await tx.insert(sessionEvents).values(values).returning() : [];
|
|
37765
|
+
const requestedEvents = inserted.filter(
|
|
37766
|
+
(event) => event.type === "session.humanInput.requested"
|
|
37767
|
+
);
|
|
37768
|
+
if (requestedEvents.length > 0) {
|
|
37769
|
+
const requestedIds = new Set(
|
|
37770
|
+
requestedEvents.flatMap((event) => {
|
|
37771
|
+
const request = sessionEventPayloadRecord(event.payload).request;
|
|
37772
|
+
if (!request || typeof request !== "object" || Array.isArray(request)) return [];
|
|
37773
|
+
const id = request.id;
|
|
37774
|
+
return typeof id === "string" ? [id] : [];
|
|
37775
|
+
})
|
|
37776
|
+
);
|
|
37777
|
+
const requests = humanInputRequests.filter((request) => requestedIds.has(request.id));
|
|
37778
|
+
if (requests.length !== requestedIds.size) {
|
|
37779
|
+
throw new Error("Human-input realtime projection lost its durable request contract");
|
|
37780
|
+
}
|
|
37781
|
+
await mirrorSessionRealtimeContextInTransaction(tx, {
|
|
37782
|
+
accountId: session.accountId,
|
|
37783
|
+
workspaceId,
|
|
37784
|
+
sessionId: input.sessionId,
|
|
37785
|
+
sourceKind: "human_input_request",
|
|
37786
|
+
sourceId: requestedEvents.map((event) => event.id).join(":"),
|
|
37787
|
+
turnId: input.turnId,
|
|
37788
|
+
channel: "speakable",
|
|
37789
|
+
text: renderRealtimeHumanInputRequestContext({ requests }),
|
|
37790
|
+
payload: {
|
|
37791
|
+
status: "waiting_for_user",
|
|
37792
|
+
requestIds: requests.map((request) => request.id),
|
|
37793
|
+
sourceEventIds: requestedEvents.map((event) => event.id)
|
|
37794
|
+
},
|
|
37795
|
+
now
|
|
37796
|
+
});
|
|
37797
|
+
}
|
|
37798
|
+
const terminalHumanInputById = new Map(
|
|
37799
|
+
terminalHumanInputRows.map((request) => [request.id, request])
|
|
37800
|
+
);
|
|
37801
|
+
for (const event of inserted) {
|
|
37802
|
+
if (event.type !== "user.humanInputResponse") continue;
|
|
37803
|
+
const payload = sessionEventPayloadRecord(event.payload);
|
|
37804
|
+
const requestId = typeof payload.requestId === "string" ? payload.requestId : null;
|
|
37805
|
+
const request = requestId ? terminalHumanInputById.get(requestId) : null;
|
|
37806
|
+
if (!request) continue;
|
|
37807
|
+
await mirrorSessionRealtimeContextInTransaction(tx, {
|
|
37808
|
+
accountId: session.accountId,
|
|
37809
|
+
workspaceId,
|
|
37810
|
+
sessionId: input.sessionId,
|
|
37811
|
+
sourceKind: "human_input_response",
|
|
37812
|
+
sourceId: event.id,
|
|
37813
|
+
turnId: input.turnId,
|
|
37814
|
+
channel: null,
|
|
37815
|
+
text: renderRealtimeHumanInputResponseContext({
|
|
37816
|
+
requestId: request.id,
|
|
37817
|
+
questions: request.questions,
|
|
37818
|
+
response: { outcome: "cancelled" }
|
|
37819
|
+
}),
|
|
37820
|
+
payload: {
|
|
37821
|
+
requestId: request.id,
|
|
37822
|
+
outcome: "cancelled",
|
|
37823
|
+
sourceEventId: event.id
|
|
37824
|
+
},
|
|
37825
|
+
now
|
|
37826
|
+
});
|
|
37827
|
+
}
|
|
36724
37828
|
const terminal = isTerminalSessionTurnStatus(input.turnStatus);
|
|
36725
37829
|
if (isTerminalSessionTurnStatus(input.turnStatus)) {
|
|
36726
37830
|
const terminalType = terminalSessionTurnEventType(input.turnStatus);
|
|
@@ -37217,7 +38321,7 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
|
|
|
37217
38321
|
sessionHistoryItems.producerCodexCredentialId,
|
|
37218
38322
|
input.providerArtifactInvalidation.codexCredentialId
|
|
37219
38323
|
),
|
|
37220
|
-
|
|
38324
|
+
isNull6(sessionHistoryItems.providerArtifactInvalidatedAt),
|
|
37221
38325
|
sql14`${sessionHistoryItems.item} ->> 'type' in ('reasoning', 'compaction')`
|
|
37222
38326
|
)
|
|
37223
38327
|
).returning({ id: sessionHistoryItems.id });
|
|
@@ -37231,7 +38335,7 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
|
|
|
37231
38335
|
agentRunStates.frozenCodexCredentialId,
|
|
37232
38336
|
input.providerArtifactInvalidation.codexCredentialId
|
|
37233
38337
|
),
|
|
37234
|
-
|
|
38338
|
+
isNull6(agentRunStates.providerArtifactInvalidatedAt)
|
|
37235
38339
|
)
|
|
37236
38340
|
).orderBy(desc8(agentRunStates.stateVersion)).limit(1);
|
|
37237
38341
|
const invalidatedRunState = latestRunState ? await tx.update(agentRunStates).set({
|
|
@@ -37241,7 +38345,7 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
|
|
|
37241
38345
|
}).where(
|
|
37242
38346
|
and17(
|
|
37243
38347
|
eq17(agentRunStates.id, latestRunState.id),
|
|
37244
|
-
|
|
38348
|
+
isNull6(agentRunStates.providerArtifactInvalidatedAt)
|
|
37245
38349
|
)
|
|
37246
38350
|
).returning({ id: agentRunStates.id }) : [];
|
|
37247
38351
|
providerArtifactsInvalidated = invalidatedHistory.length + invalidatedRunState.length;
|
|
@@ -38029,7 +39133,7 @@ async function markSessionWorkflowWakeDelivered(db, input) {
|
|
|
38029
39133
|
and17(
|
|
38030
39134
|
eq17(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
38031
39135
|
eq17(sessionTurnAttempts.sessionId, input.sessionId),
|
|
38032
|
-
|
|
39136
|
+
isNull6(sessionTurnAttempts.quiescedAt),
|
|
38033
39137
|
inArray11(sessionAttemptInterruptions.state, ["settled", "rejected_stale"])
|
|
38034
39138
|
)
|
|
38035
39139
|
).limit(1);
|
|
@@ -39744,6 +40848,8 @@ export {
|
|
|
39744
40848
|
CODEX_CREDENTIAL_LEASE_TTL_MS,
|
|
39745
40849
|
CODEX_RESET_REDEMPTION_OUTCOMES,
|
|
39746
40850
|
CODEX_ROTATION_STRATEGIES,
|
|
40851
|
+
ConnectionDisconnectGenerationError,
|
|
40852
|
+
ConnectionDisconnectIdempotencyError,
|
|
39747
40853
|
ConnectionRefreshHttpError,
|
|
39748
40854
|
FORCE_RLS_TABLES,
|
|
39749
40855
|
GitHubInstallationAuthorityCommitError,
|
|
@@ -39852,6 +40958,10 @@ export {
|
|
|
39852
40958
|
WorkspaceInstructionPolicyInvalidOperationError,
|
|
39853
40959
|
WorkspaceInstructionPolicyLegacyUnavailableError,
|
|
39854
40960
|
WorkspaceInstructionPolicyNotFoundError,
|
|
40961
|
+
WorkspaceInstructionPolicyOnboardingProposalConflictError,
|
|
40962
|
+
WorkspaceInstructionPolicyOnboardingProposalContentError,
|
|
40963
|
+
WorkspaceInstructionPolicyOnboardingProposalStaleError,
|
|
40964
|
+
WorkspaceInstructionPolicyOperationReuseError,
|
|
39855
40965
|
WorkspaceInstructionPolicySnapshotAuthorityError,
|
|
39856
40966
|
abandonCodexResetRedemptionBeforeProvider,
|
|
39857
40967
|
abandonRecordingForTurnAttempt,
|
|
@@ -40022,6 +41132,7 @@ export {
|
|
|
40022
41132
|
createWorkspaceArtifact,
|
|
40023
41133
|
createWorkspaceEnvironment,
|
|
40024
41134
|
createWorkspaceInstructionPolicyDraft,
|
|
41135
|
+
createWorkspaceInstructionPolicyOnboardingProposal,
|
|
40025
41136
|
databaseFailureCode,
|
|
40026
41137
|
sql15 as dbSql,
|
|
40027
41138
|
deactivatePreferenceRegistry,
|
|
@@ -40053,6 +41164,8 @@ export {
|
|
|
40053
41164
|
disableHostExportConsumer,
|
|
40054
41165
|
disconnectAllCodexAccounts,
|
|
40055
41166
|
disconnectCodexAccount,
|
|
41167
|
+
disconnectConnectionIdempotently,
|
|
41168
|
+
durableUserHistoryItem,
|
|
40056
41169
|
editQueuedTurnInTransaction,
|
|
40057
41170
|
enableCapabilityInstallation,
|
|
40058
41171
|
enablePackInstallation,
|
|
@@ -40101,11 +41214,13 @@ export {
|
|
|
40101
41214
|
getCodexRotationSettings,
|
|
40102
41215
|
getComposerDraftInTransaction,
|
|
40103
41216
|
getConnectionMetadata,
|
|
41217
|
+
getCurrentPreferenceRegistryGovernanceMetadata,
|
|
40104
41218
|
getDeviceEnrollmentRequestByDeviceCode,
|
|
40105
41219
|
getEligibleKnowledgeClaim,
|
|
40106
41220
|
getEnrollment,
|
|
40107
41221
|
getFile,
|
|
40108
41222
|
getFileUpload,
|
|
41223
|
+
getFiles,
|
|
40109
41224
|
getHostExportConsumerStatus,
|
|
40110
41225
|
getHumanInputResumeForEvent,
|
|
40111
41226
|
getKnowledgeMemory,
|
|
@@ -40188,6 +41303,7 @@ export {
|
|
|
40188
41303
|
getWorkspaceInstructionPolicyRevision,
|
|
40189
41304
|
getWorkspaceModelPolicy,
|
|
40190
41305
|
getWorkspacePack,
|
|
41306
|
+
getWorkspaceStateAcceptedAttemptGovernance,
|
|
40191
41307
|
grantWorkspaceAccess,
|
|
40192
41308
|
hasAuditableGitHubInstallationAuthority,
|
|
40193
41309
|
hasCreditLedgerEntry,
|
|
@@ -40282,6 +41398,7 @@ export {
|
|
|
40282
41398
|
listWorkspaceArtifacts,
|
|
40283
41399
|
listWorkspaceControlEvents,
|
|
40284
41400
|
listWorkspaceEnvironments,
|
|
41401
|
+
listWorkspaceInstructionPolicyOnboardingProposals,
|
|
40285
41402
|
listWorkspaceInstructionPolicyRevisions,
|
|
40286
41403
|
listWorkspaceMembers,
|
|
40287
41404
|
listWorkspacePacks,
|
|
@@ -40422,6 +41539,7 @@ export {
|
|
|
40422
41539
|
reserveSessionCommandReceipt,
|
|
40423
41540
|
reserveToolspaceCallForAttempt,
|
|
40424
41541
|
resolveConnectorActionPolicy,
|
|
41542
|
+
resolveDocumentIndexAuthority,
|
|
40425
41543
|
resolveSlackInstallationRoute,
|
|
40426
41544
|
resolveWorkspaceMemoryBlock,
|
|
40427
41545
|
restoreKnowledgeSourceObject,
|
|
@@ -40514,6 +41632,7 @@ export {
|
|
|
40514
41632
|
syncSessionRealtimeLedgerInTransaction,
|
|
40515
41633
|
touchEnrollmentLastSeen,
|
|
40516
41634
|
touchLeaseHolder,
|
|
41635
|
+
transitionConnectionState,
|
|
40517
41636
|
updateCodexAllocatorEligibility,
|
|
40518
41637
|
updateCodexRotationSettings,
|
|
40519
41638
|
updateConnection,
|