@opengeni/db 4.2.2 → 4.3.0-canary.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/canonical-human-identities.js +3 -3
- package/dist/{chunk-6GAPAYZR.js → chunk-7JPLGBSA.js} +562 -86
- package/dist/chunk-7JPLGBSA.js.map +1 -0
- package/dist/{chunk-SNLIKELL.js → chunk-IHADTE7C.js} +2 -2
- package/dist/{chunk-7WX3R3YL.js → chunk-ILKJKDII.js} +7 -2
- package/dist/chunk-ILKJKDII.js.map +1 -0
- package/dist/{chunk-MGVOYVPS.js → chunk-NCSPNCQK.js} +3 -3
- package/dist/{chunk-B2KR3TEM.js → chunk-SGJDIF2D.js} +2 -2
- package/dist/{chunk-S4Y42VSX.js → chunk-Y5XFVJJM.js} +207 -3
- package/dist/chunk-Y5XFVJJM.js.map +1 -0
- package/dist/{chunk-FPTI4NP4.js → chunk-YGBOVXC7.js} +2 -2
- package/dist/{chunk-6Y7ZDVJ5.js → chunk-YGRT6VZO.js} +2 -2
- package/dist/{chunk-6IQ4X35Y.js → chunk-YVYBAWBU.js} +46 -1
- package/dist/chunk-YVYBAWBU.js.map +1 -0
- package/dist/connect-attempts.d.ts +60 -0
- package/dist/connection-setup-authority.d.ts +10 -0
- package/dist/connection-token-resolver.d.ts +11 -2
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/external-identities.d.ts +16 -0
- package/dist/external-identity-links.d.ts +74 -0
- package/dist/external-link-work.d.ts +51 -0
- package/dist/host-mcp-binding-match.d.ts +5 -0
- package/dist/host-mcp-bindings.d.ts +73 -0
- package/dist/host-mcp-task-authority.d.ts +47 -0
- package/dist/host-selection-identity.d.ts +6 -0
- package/dist/index.d.ts +51 -35
- package/dist/index.js +15171 -13775
- package/dist/index.js.map +1 -1
- package/dist/live-session-attempt.d.ts +275 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/memory-domain.d.ts +3 -3
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.js +2 -2
- package/dist/runtime-posture.d.ts +6 -6
- package/dist/scheduled-task-revision-authority.d.ts +11 -0
- package/dist/schema.d.ts +1279 -9
- package/dist/schema.js +11 -1
- package/dist/session-background-commands.js +4 -4
- package/dist/session-command-output.js +4 -4
- package/dist/session-event-slices.js +2 -2
- package/dist/session-queue-commands.d.ts +7 -0
- package/dist/session-tenancy.d.ts +2 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +4 -4
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0437_organization_scoped_external_workspaces.sql +71 -0
- package/drizzle/0438_durable_connect_attempts.sql +95 -0
- package/drizzle/0439_external_identity_provisioning.sql +128 -0
- package/drizzle/0440_external_workspace_member_removal.sql +59 -0
- package/drizzle/0441_external_identity_membership_lifecycle.sql +163 -0
- package/drizzle/0442_external_owning_user_authority.sql +81 -0
- package/drizzle/0443_host_mcp_binding_registry.sql +93 -0
- package/drizzle/0444_host_mcp_delegations.sql +129 -0
- package/drizzle/0445_host_mcp_turn_authorities.sql +145 -0
- package/drizzle/0446_host_mcp_causal_continuation.sql +126 -0
- package/drizzle/0447_host_mcp_task_authorities.sql +264 -0
- package/drizzle/0448_host_mcp_child_authority.sql +115 -0
- package/drizzle/0449_external_identity_link_lifecycle.sql +107 -0
- package/drizzle/0450_external_identity_link_work.sql +179 -0
- package/drizzle/0451_external_link_preview_and_permission_ceiling.sql +82 -0
- package/drizzle/0452_external_link_scheduled_origin.sql +34 -0
- package/drizzle/0453_host_mcp_native_owner.sql +35 -0
- package/drizzle/0454_connect_origin_authority.sql +42 -0
- package/drizzle/0455_external_link_inventory_labels.sql +44 -0
- package/drizzle/0456_social_connection_versions.sql +14 -0
- package/drizzle/0457_canonical_session_scope_subject.sql +46 -0
- package/drizzle/0458_skill_review_wire_compatibility.sql +36 -0
- package/package.json +6 -6
- package/src/connect-attempts.ts +317 -0
- package/src/connection-authority.ts +9 -0
- package/src/connection-setup-authority.ts +35 -0
- package/src/connection-token-resolver.ts +177 -48
- package/src/external-identities.ts +70 -0
- package/src/external-identity-links.ts +364 -0
- package/src/external-link-work.ts +228 -0
- package/src/host-mcp-binding-match.ts +33 -0
- package/src/host-mcp-bindings.ts +741 -0
- package/src/host-mcp-task-authority.ts +383 -0
- package/src/host-selection-identity.ts +29 -0
- package/src/index.ts +398 -147
- package/src/live-session-attempt.ts +85 -0
- package/src/managed-human-provisioning.ts +6 -1
- package/src/memory-domain.ts +6 -11
- package/src/provision-roles.ts +3 -0
- package/src/runtime-posture.ts +47 -0
- package/src/scheduled-task-revision-authority.ts +30 -0
- package/src/schema.ts +228 -3
- package/src/session-control.ts +14 -0
- package/src/session-queue-commands.ts +11 -0
- package/src/session-tenancy.ts +7 -0
- package/src/workspace-authority.ts +5 -5
- package/dist/chunk-6GAPAYZR.js.map +0 -1
- package/dist/chunk-6IQ4X35Y.js.map +0 -1
- package/dist/chunk-7WX3R3YL.js.map +0 -1
- package/dist/chunk-S4Y42VSX.js.map +0 -1
- /package/dist/{chunk-SNLIKELL.js.map → chunk-IHADTE7C.js.map} +0 -0
- /package/dist/{chunk-MGVOYVPS.js.map → chunk-NCSPNCQK.js.map} +0 -0
- /package/dist/{chunk-B2KR3TEM.js.map → chunk-SGJDIF2D.js.map} +0 -0
- /package/dist/{chunk-FPTI4NP4.js.map → chunk-YGBOVXC7.js.map} +0 -0
- /package/dist/{chunk-6Y7ZDVJ5.js.map → chunk-YGRT6VZO.js.map} +0 -0
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cancelUnacceptedVideoGenerationsForToolCallsInTransaction
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NCSPNCQK.js";
|
|
4
4
|
import {
|
|
5
5
|
nestedPostgresSqlState,
|
|
6
|
+
rawRows,
|
|
7
|
+
setSubjectRlsContext,
|
|
8
|
+
withAccountRls,
|
|
6
9
|
withRlsContext,
|
|
7
|
-
withSessionActivityRlsContext
|
|
8
|
-
|
|
10
|
+
withSessionActivityRlsContext,
|
|
11
|
+
withWorkspaceRls,
|
|
12
|
+
withWorkspaceSubjectRls
|
|
13
|
+
} from "./chunk-SGJDIF2D.js";
|
|
9
14
|
import {
|
|
10
15
|
LOSSLESS_CONTENT_CODEC_VERSION,
|
|
11
16
|
auditEvents,
|
|
@@ -32,22 +37,460 @@ import {
|
|
|
32
37
|
workspaceControlEvents,
|
|
33
38
|
workspaceInferenceControls,
|
|
34
39
|
workspaces
|
|
35
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-Y5XFVJJM.js";
|
|
36
41
|
|
|
37
42
|
// src/session-background-commands.ts
|
|
38
43
|
import { SessionCommandFailure } from "@opengeni/contracts";
|
|
39
44
|
import { isDeepStrictEqual } from "util";
|
|
40
|
-
import { and as and4, asc as asc2, desc as desc2, eq as eq4, getTableColumns, gte, inArray as inArray3, sql as
|
|
45
|
+
import { and as and4, asc as asc2, desc as desc2, eq as eq4, getTableColumns, gte, inArray as inArray3, sql as sql7 } from "drizzle-orm";
|
|
41
46
|
|
|
42
47
|
// src/session-control.ts
|
|
43
|
-
import { createHash as
|
|
48
|
+
import { createHash as createHash3 } from "crypto";
|
|
44
49
|
import {
|
|
45
50
|
boundWorkspaceControlEvent,
|
|
46
51
|
childPausedClassification,
|
|
47
52
|
McpPersonalConnectionDelegations,
|
|
48
53
|
workspaceControlUtf8Bytes
|
|
49
54
|
} from "@opengeni/contracts";
|
|
50
|
-
import { and as and3, eq as eq3, inArray as inArray2, sql as
|
|
55
|
+
import { and as and3, eq as eq3, inArray as inArray2, sql as sql6 } from "drizzle-orm";
|
|
56
|
+
|
|
57
|
+
// src/external-link-work.ts
|
|
58
|
+
import { sql as sql3 } from "drizzle-orm";
|
|
59
|
+
import { stableJson } from "@opengeni/contracts";
|
|
60
|
+
import { ExternalLinkWorkSnapshot } from "@opengeni/contracts/external-identities";
|
|
61
|
+
|
|
62
|
+
// src/external-identities.ts
|
|
63
|
+
import { sql } from "drizzle-orm";
|
|
64
|
+
import { Permission } from "@opengeni/contracts";
|
|
65
|
+
import {
|
|
66
|
+
ExternalIdentity,
|
|
67
|
+
ExternalIdentityReference
|
|
68
|
+
} from "@opengeni/contracts/external-identities";
|
|
69
|
+
async function lockActiveExternalOrganizationKey(tx, accountId, keyId) {
|
|
70
|
+
return withAccountRls(tx, accountId, async (scoped) => {
|
|
71
|
+
const [row] = await rawRows(
|
|
72
|
+
scoped,
|
|
73
|
+
sql`select permissions from api_keys
|
|
74
|
+
where account_id = ${accountId}::uuid and id = ${keyId}::uuid
|
|
75
|
+
and workspace_id is null and credential_kind = 'organization'
|
|
76
|
+
and revoked_at is null and (expires_at is null or expires_at > clock_timestamp()) for share`
|
|
77
|
+
);
|
|
78
|
+
return row ? Permission.array().parse(row.permissions) : null;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function lockExternalWorkspaceMembershipLifecycle(tx, accountId) {
|
|
82
|
+
await tx.execute(
|
|
83
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`organization-membership:${accountId}`}, 0))`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
async function ensureExternalIdentity(db, input) {
|
|
87
|
+
const accountId = input.accountId;
|
|
88
|
+
const reference = ExternalIdentityReference.parse({
|
|
89
|
+
externalId: input.externalId,
|
|
90
|
+
source: input.source
|
|
91
|
+
});
|
|
92
|
+
return withAccountRls(db, accountId, async (tx) => {
|
|
93
|
+
const [row] = await rawRows(
|
|
94
|
+
tx,
|
|
95
|
+
sql`select ensure_external_identity(
|
|
96
|
+
${accountId}::uuid, ${reference.source}, ${reference.externalId}) as result`
|
|
97
|
+
);
|
|
98
|
+
const result = ExternalIdentity.parse(row?.result);
|
|
99
|
+
if (result.accountId !== accountId || result.source !== reference.source || result.externalId !== reference.externalId) {
|
|
100
|
+
throw new Error("External identity scope mismatch");
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/external-identity-links.ts
|
|
107
|
+
import { createHash, randomBytes, randomUUID } from "crypto";
|
|
108
|
+
import { sql as sql2 } from "drizzle-orm";
|
|
109
|
+
import {
|
|
110
|
+
BeginExternalIdentityLinkRequest,
|
|
111
|
+
BeginExternalIdentityLinkResponse,
|
|
112
|
+
ConfirmExternalIdentityLinkRequest,
|
|
113
|
+
ExternalIdentityLink,
|
|
114
|
+
ExternalIdentityLinkPage,
|
|
115
|
+
ExternalIdentityReference as ExternalIdentityReference2
|
|
116
|
+
} from "@opengeni/contracts/external-identities";
|
|
117
|
+
var ExternalIdentityLinkConflictError = class extends Error {
|
|
118
|
+
name = "ExternalIdentityLinkConflictError";
|
|
119
|
+
constructor() {
|
|
120
|
+
super("Identity link changed or is unavailable");
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var digest = (value) => createHash("sha256").update(value).digest("hex");
|
|
124
|
+
var iso = (value) => new Date(value).toISOString();
|
|
125
|
+
function project(row) {
|
|
126
|
+
const expiry = row.status === "pending" ? row.confirm_before : row.expires_at;
|
|
127
|
+
return ExternalIdentityLink.parse({
|
|
128
|
+
id: row.id,
|
|
129
|
+
accountId: row.account_id,
|
|
130
|
+
externalIdentityId: row.external_identity_id,
|
|
131
|
+
nativeSubjectId: row.native_subject_id,
|
|
132
|
+
status: row.status !== "revoked" && expiry !== null && new Date(expiry).getTime() <= Date.now() ? "expired" : row.status,
|
|
133
|
+
revision: Number(row.revision),
|
|
134
|
+
permissions: row.permissions,
|
|
135
|
+
expiresAt: row.expires_at === null ? null : iso(row.expires_at)
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async function activeMembership(tx, accountId, subjectId) {
|
|
139
|
+
const [prior] = await rawRows(
|
|
140
|
+
tx,
|
|
141
|
+
sql2`select coalesce(current_setting('opengeni.subject_id', true), '') as subject`
|
|
142
|
+
);
|
|
143
|
+
let succeeded = false;
|
|
144
|
+
let row;
|
|
145
|
+
try {
|
|
146
|
+
await setSubjectRlsContext(tx, subjectId);
|
|
147
|
+
[row] = await rawRows(
|
|
148
|
+
tx,
|
|
149
|
+
sql2`select value as membership from jsonb_array_elements(list_self_organization_memberships(${subjectId}))
|
|
150
|
+
where value ->> 'organizationId' = ${accountId} and value ->> 'status' = 'active'`
|
|
151
|
+
);
|
|
152
|
+
succeeded = true;
|
|
153
|
+
} finally {
|
|
154
|
+
const restore = tx.execute(
|
|
155
|
+
sql2`select set_config('opengeni.subject_id', ${prior?.subject ?? ""}, true)`
|
|
156
|
+
);
|
|
157
|
+
if (succeeded) await restore;
|
|
158
|
+
else await restore.catch(() => void 0);
|
|
159
|
+
}
|
|
160
|
+
if (!row) throw new ExternalIdentityLinkConflictError();
|
|
161
|
+
return row.membership;
|
|
162
|
+
}
|
|
163
|
+
async function beginExternalIdentityLink(db, identity, raw) {
|
|
164
|
+
const input = BeginExternalIdentityLinkRequest.parse(raw);
|
|
165
|
+
return withAccountRls(db, identity.accountId, async (tx) => {
|
|
166
|
+
await lockExternalWorkspaceMembershipLifecycle(tx, identity.accountId);
|
|
167
|
+
const membership = await activeMembership(tx, identity.accountId, identity.subjectId);
|
|
168
|
+
if (membership.id !== identity.organizationMembershipId || Number(membership.authorizationRevision) !== identity.authorizationRevision || identity.status !== "active")
|
|
169
|
+
throw new ExternalIdentityLinkConflictError();
|
|
170
|
+
const [quota] = await rawRows(
|
|
171
|
+
tx,
|
|
172
|
+
sql2`select count(*) as count
|
|
173
|
+
from external_identity_links where account_id = ${identity.accountId}::uuid
|
|
174
|
+
and external_identity_id = ${identity.id}::uuid and status = 'pending'
|
|
175
|
+
and confirm_before > clock_timestamp()`
|
|
176
|
+
);
|
|
177
|
+
if (Number(quota?.count ?? 0) >= 32) throw new ExternalIdentityLinkConflictError();
|
|
178
|
+
const challenge = randomBytes(32).toString("base64url");
|
|
179
|
+
const permissions = [...new Set(input.permissions)].sort();
|
|
180
|
+
const [row] = await rawRows(
|
|
181
|
+
tx,
|
|
182
|
+
sql2`insert into external_identity_links
|
|
183
|
+
(id, account_id, external_identity_id, external_subject_id, external_authorization_revision,
|
|
184
|
+
permissions, challenge_digest, confirm_before, expires_at)
|
|
185
|
+
values (${randomUUID()}::uuid, ${identity.accountId}::uuid, ${identity.id}::uuid,
|
|
186
|
+
${identity.subjectId}, ${identity.authorizationRevision}, ${JSON.stringify(permissions)}::jsonb,
|
|
187
|
+
${digest(challenge)}, clock_timestamp() + interval '10 minutes', ${input.expiresAt}::timestamptz)
|
|
188
|
+
returning *`
|
|
189
|
+
);
|
|
190
|
+
if (!row) throw new Error("Identity link insert returned no row");
|
|
191
|
+
return BeginExternalIdentityLinkResponse.parse({
|
|
192
|
+
link: project(row),
|
|
193
|
+
challenge,
|
|
194
|
+
confirmBefore: iso(row.confirm_before)
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
async function getExternalIdentityLink(db, input) {
|
|
199
|
+
return withAccountRls(db, input.accountId, async (tx) => {
|
|
200
|
+
const [row] = await rawRows(
|
|
201
|
+
tx,
|
|
202
|
+
sql2`select * from external_identity_links
|
|
203
|
+
where account_id = ${input.accountId}::uuid and id = ${input.linkId}::uuid
|
|
204
|
+
and (external_subject_id = ${input.subjectId} or native_subject_id = ${input.subjectId})`
|
|
205
|
+
);
|
|
206
|
+
return row ? project(row) : null;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async function listExternalIdentityLinks(db, input) {
|
|
210
|
+
return withAccountRls(db, input.accountId, async (tx) => {
|
|
211
|
+
const rows = await rawRows(
|
|
212
|
+
tx,
|
|
213
|
+
sql2`
|
|
214
|
+
select * from external_identity_links
|
|
215
|
+
where account_id = ${input.accountId}::uuid
|
|
216
|
+
and (external_subject_id = ${input.subjectId} or native_subject_id = ${input.subjectId})
|
|
217
|
+
${input.cursor ? sql2`and id > ${input.cursor}::uuid` : sql2``}
|
|
218
|
+
order by id limit 51`
|
|
219
|
+
);
|
|
220
|
+
const links = rows.slice(0, 50).map(project);
|
|
221
|
+
await setSubjectRlsContext(tx, input.subjectId);
|
|
222
|
+
const [labels] = links.length ? await rawRows(
|
|
223
|
+
tx,
|
|
224
|
+
sql2`select get_external_identity_link_inventory_references(${input.accountId}::uuid, ARRAY[${sql2.join(
|
|
225
|
+
links.map((link) => sql2`${link.id}::uuid`),
|
|
226
|
+
sql2`, `
|
|
227
|
+
)}]) as references`
|
|
228
|
+
) : [];
|
|
229
|
+
return ExternalIdentityLinkPage.parse({
|
|
230
|
+
links: links.map((link) => ({
|
|
231
|
+
...link,
|
|
232
|
+
...labels?.references[link.id] ? { externalIdentity: ExternalIdentityReference2.parse(labels.references[link.id]) } : {}
|
|
233
|
+
})),
|
|
234
|
+
nextCursor: rows.length > 50 ? links.at(-1).id : null
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
async function previewExternalIdentityLink(db, input) {
|
|
239
|
+
return withAccountRls(db, input.accountId, async (tx) => {
|
|
240
|
+
const [row] = await rawRows(
|
|
241
|
+
tx,
|
|
242
|
+
sql2`select * from external_identity_links
|
|
243
|
+
where account_id = ${input.accountId}::uuid and id = ${input.linkId}::uuid
|
|
244
|
+
and challenge_digest = ${digest(input.challenge)} and status = 'pending'
|
|
245
|
+
and confirm_before > clock_timestamp()
|
|
246
|
+
and (expires_at is null or expires_at > clock_timestamp())`
|
|
247
|
+
);
|
|
248
|
+
if (!row) return null;
|
|
249
|
+
const [identity] = await rawRows(
|
|
250
|
+
tx,
|
|
251
|
+
sql2`select get_external_identity_link_reference(${input.accountId}::uuid, ${input.linkId}::uuid, ${digest(input.challenge)}) as reference`
|
|
252
|
+
);
|
|
253
|
+
return identity?.reference ? {
|
|
254
|
+
link: project(row),
|
|
255
|
+
externalIdentity: ExternalIdentityReference2.parse(identity.reference)
|
|
256
|
+
} : null;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async function confirmExternalIdentityLink(db, input) {
|
|
260
|
+
const request = ConfirmExternalIdentityLinkRequest.parse(input.request);
|
|
261
|
+
if (!/^user:[^\r\n]+$/.test(input.nativeSubjectId)) throw new ExternalIdentityLinkConflictError();
|
|
262
|
+
return withAccountRls(db, input.accountId, async (tx) => {
|
|
263
|
+
await lockExternalWorkspaceMembershipLifecycle(tx, input.accountId);
|
|
264
|
+
const [row] = await rawRows(
|
|
265
|
+
tx,
|
|
266
|
+
sql2`select * from external_identity_links
|
|
267
|
+
where account_id = ${input.accountId}::uuid and id = ${input.linkId}::uuid
|
|
268
|
+
and challenge_digest = ${digest(request.challenge)} for update`
|
|
269
|
+
);
|
|
270
|
+
if (!row) throw new ExternalIdentityLinkConflictError();
|
|
271
|
+
const external = await activeMembership(tx, input.accountId, row.external_subject_id);
|
|
272
|
+
const native = await activeMembership(tx, input.accountId, input.nativeSubjectId);
|
|
273
|
+
if (Number(external.authorizationRevision) !== Number(row.external_authorization_revision))
|
|
274
|
+
throw new ExternalIdentityLinkConflictError();
|
|
275
|
+
const permissions = [...new Set(request.permissions)].sort();
|
|
276
|
+
if (row.status === "active" && Number(row.revision) === request.expectedRevision + 1 && row.native_subject_id === input.nativeSubjectId && row.native_membership_id === native.id && Number(row.native_authorization_revision) === Number(native.authorizationRevision) && JSON.stringify(row.permissions) === JSON.stringify(permissions) && project(row).status === "active")
|
|
277
|
+
return project(row);
|
|
278
|
+
if (row.status !== "pending" || Number(row.revision) !== request.expectedRevision || project(row).status !== "pending" || permissions.some((permission) => !row.permissions.includes(permission)))
|
|
279
|
+
throw new ExternalIdentityLinkConflictError();
|
|
280
|
+
const [confirmed] = await rawRows(
|
|
281
|
+
tx,
|
|
282
|
+
sql2`update external_identity_links set
|
|
283
|
+
status = 'active', revision = revision + 1, native_subject_id = ${input.nativeSubjectId},
|
|
284
|
+
native_membership_id = ${native.id}::uuid, native_authorization_revision = ${Number(native.authorizationRevision)},
|
|
285
|
+
permissions = ${JSON.stringify(permissions)}::jsonb, confirmed_at = clock_timestamp()
|
|
286
|
+
where id = ${row.id}::uuid returning *`
|
|
287
|
+
);
|
|
288
|
+
if (!confirmed) throw new ExternalIdentityLinkConflictError();
|
|
289
|
+
return project(confirmed);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
async function revokeExternalIdentityLink(db, input) {
|
|
293
|
+
return withAccountRls(db, input.accountId, async (tx) => {
|
|
294
|
+
await lockExternalWorkspaceMembershipLifecycle(tx, input.accountId);
|
|
295
|
+
const [row] = await rawRows(
|
|
296
|
+
tx,
|
|
297
|
+
sql2`select * from external_identity_links
|
|
298
|
+
where account_id = ${input.accountId}::uuid and id = ${input.linkId}::uuid
|
|
299
|
+
and (external_subject_id = ${input.subjectId} or native_subject_id = ${input.subjectId}) for update`
|
|
300
|
+
);
|
|
301
|
+
if (!row) throw new ExternalIdentityLinkConflictError();
|
|
302
|
+
if (row.status === "revoked" && Number(row.revision) === input.expectedRevision + 1)
|
|
303
|
+
return project(row);
|
|
304
|
+
if (row.status === "revoked" || Number(row.revision) !== input.expectedRevision)
|
|
305
|
+
throw new ExternalIdentityLinkConflictError();
|
|
306
|
+
const [revoked] = await rawRows(
|
|
307
|
+
tx,
|
|
308
|
+
sql2`update external_identity_links
|
|
309
|
+
set status = 'revoked', revision = revision + 1, revoked_at = clock_timestamp()
|
|
310
|
+
where id = ${row.id}::uuid returning *`
|
|
311
|
+
);
|
|
312
|
+
if (!revoked) throw new ExternalIdentityLinkConflictError();
|
|
313
|
+
return project(revoked);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
async function resolveExternalIdentityLink(db, input) {
|
|
317
|
+
return withAccountRls(db, input.identity.accountId, async (tx) => {
|
|
318
|
+
await lockExternalWorkspaceMembershipLifecycle(tx, input.identity.accountId);
|
|
319
|
+
const [row] = await rawRows(
|
|
320
|
+
tx,
|
|
321
|
+
sql2`select * from external_identity_links
|
|
322
|
+
where id = ${input.linkId}::uuid and account_id = ${input.identity.accountId}::uuid
|
|
323
|
+
and external_identity_id = ${input.identity.id}::uuid for share`
|
|
324
|
+
);
|
|
325
|
+
if (!row || row.status !== "active" || !row.native_subject_id || Number(row.revision) !== input.expectedRevision || Number(row.external_authorization_revision) !== input.identity.authorizationRevision || input.identity.status !== "active" || project(row).status !== "active")
|
|
326
|
+
return null;
|
|
327
|
+
try {
|
|
328
|
+
const external = await activeMembership(
|
|
329
|
+
tx,
|
|
330
|
+
input.identity.accountId,
|
|
331
|
+
input.identity.subjectId
|
|
332
|
+
);
|
|
333
|
+
const native = await activeMembership(tx, input.identity.accountId, row.native_subject_id);
|
|
334
|
+
if (external.id !== input.identity.organizationMembershipId || Number(external.authorizationRevision) !== input.identity.authorizationRevision || native.id !== row.native_membership_id || Number(native.authorizationRevision) !== Number(row.native_authorization_revision))
|
|
335
|
+
return null;
|
|
336
|
+
return { link: project(row), personalWorkspaceId: native.personalWorkspaceId };
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (error instanceof ExternalIdentityLinkConflictError) return null;
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/external-link-work.ts
|
|
345
|
+
async function externalLinkWorkSnapshotIsLive(db, raw) {
|
|
346
|
+
const snapshot = ExternalLinkWorkSnapshot.parse(raw);
|
|
347
|
+
try {
|
|
348
|
+
const identity = await ensureExternalIdentity(db, {
|
|
349
|
+
accountId: snapshot.actor.accountId,
|
|
350
|
+
...snapshot.identity
|
|
351
|
+
});
|
|
352
|
+
if (identity.id !== snapshot.actor.externalIdentityId || identity.subjectId !== snapshot.actor.externalSubjectId || identity.authorizationRevision !== snapshot.actor.externalAuthorizationRevision)
|
|
353
|
+
return false;
|
|
354
|
+
const resolved = await resolveExternalIdentityLink(db, {
|
|
355
|
+
identity,
|
|
356
|
+
linkId: snapshot.actor.linkId,
|
|
357
|
+
expectedRevision: snapshot.actor.linkRevision
|
|
358
|
+
});
|
|
359
|
+
return resolved !== null && resolved.link.nativeSubjectId === snapshot.actor.effectiveSubjectId && snapshot.permissions.every(
|
|
360
|
+
(permission) => resolved.link.permissions.includes(permission) || permission !== "secrets:read" && resolved.link.permissions.includes("workspace:admin")
|
|
361
|
+
);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
if (nestedPostgresSqlState(error) === "42501") return false;
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async function getExternalLinkTurnSnapshot(db, scope, turnId) {
|
|
368
|
+
return withWorkspaceRls(db, scope.workspaceId, async (tx) => {
|
|
369
|
+
const [row] = await rawRows(
|
|
370
|
+
tx,
|
|
371
|
+
sql3`select canonical_snapshot
|
|
372
|
+
from external_link_turn_authorities where account_id = ${scope.accountId}::uuid
|
|
373
|
+
and workspace_id = ${scope.workspaceId}::uuid and turn_id = ${turnId}::uuid`
|
|
374
|
+
);
|
|
375
|
+
return row ? ExternalLinkWorkSnapshot.parse(row.canonical_snapshot) : null;
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
async function getExternalLinkTurnAuthorization(db, scope, turnId) {
|
|
379
|
+
const snapshot = await getExternalLinkTurnSnapshot(db, scope, turnId);
|
|
380
|
+
if (!snapshot) return null;
|
|
381
|
+
return {
|
|
382
|
+
authorized: await externalLinkWorkSnapshotIsLive(db, snapshot),
|
|
383
|
+
permissions: snapshot.permissions
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
async function captureExternalLinkTurnAuthority(db, input) {
|
|
387
|
+
const snapshot = ExternalLinkWorkSnapshot.parse(input.snapshot);
|
|
388
|
+
const source = input.source ?? { kind: "direct" };
|
|
389
|
+
if (snapshot.actor.accountId !== input.accountId)
|
|
390
|
+
throw new Error("Linked turn organization mismatch");
|
|
391
|
+
if (source.kind === "direct" && !await externalLinkWorkSnapshotIsLive(db, snapshot))
|
|
392
|
+
throw new Error("Linked turn authority revoked");
|
|
393
|
+
await withWorkspaceSubjectRls(
|
|
394
|
+
db,
|
|
395
|
+
input.workspaceId,
|
|
396
|
+
snapshot.actor.effectiveSubjectId,
|
|
397
|
+
async (tx) => {
|
|
398
|
+
const existing = await getExternalLinkTurnSnapshot(tx, input, input.turnId);
|
|
399
|
+
if (existing) {
|
|
400
|
+
if (stableJson(existing) !== stableJson(snapshot))
|
|
401
|
+
throw new Error("Linked turn replay changed authority");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
await tx.execute(sql3`insert into external_link_turn_authorities
|
|
405
|
+
(turn_id, account_id, workspace_id, session_id, link_id, link_revision, canonical_snapshot,
|
|
406
|
+
source_kind, source_turn_id, source_task_id, source_task_revision)
|
|
407
|
+
values (${input.turnId}::uuid, ${input.accountId}::uuid, ${input.workspaceId}::uuid, ${input.sessionId}::uuid,
|
|
408
|
+
${snapshot.actor.linkId}::uuid, ${snapshot.actor.linkRevision}, ${JSON.stringify(snapshot)}::jsonb, ${source.kind},
|
|
409
|
+
${source.kind === "causal" || source.kind === "child" ? source.turnId : null}::uuid,
|
|
410
|
+
${source.kind === "scheduled" ? source.taskId : null}::uuid,
|
|
411
|
+
${source.kind === "scheduled" ? source.taskRevision : null})`);
|
|
412
|
+
}
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
async function inheritExternalLinkTurnAuthority(db, input) {
|
|
416
|
+
const snapshot = await getExternalLinkTurnSnapshot(db, input, input.sourceTurnId);
|
|
417
|
+
if (!snapshot) return;
|
|
418
|
+
await captureExternalLinkTurnAuthority(db, {
|
|
419
|
+
...input,
|
|
420
|
+
snapshot,
|
|
421
|
+
source: { kind: input.kind, turnId: input.sourceTurnId }
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
async function getExternalLinkTaskSnapshot(db, input) {
|
|
425
|
+
return withWorkspaceRls(db, input.workspaceId, async (tx) => {
|
|
426
|
+
const [row] = await rawRows(
|
|
427
|
+
tx,
|
|
428
|
+
sql3`select canonical_snapshot
|
|
429
|
+
from external_link_task_authorities where account_id = ${input.accountId}::uuid
|
|
430
|
+
and workspace_id = ${input.workspaceId}::uuid and task_id = ${input.taskId}::uuid and task_revision = ${input.taskRevision}`
|
|
431
|
+
);
|
|
432
|
+
return row ? ExternalLinkWorkSnapshot.parse(row.canonical_snapshot) : null;
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
async function captureExternalLinkTaskAuthority(db, task, raw) {
|
|
436
|
+
const snapshot = ExternalLinkWorkSnapshot.parse(raw);
|
|
437
|
+
if (task.accountId !== snapshot.actor.accountId)
|
|
438
|
+
throw new Error("Linked task organization mismatch");
|
|
439
|
+
await withWorkspaceSubjectRls(
|
|
440
|
+
db,
|
|
441
|
+
task.workspaceId,
|
|
442
|
+
snapshot.actor.effectiveSubjectId,
|
|
443
|
+
async (tx) => {
|
|
444
|
+
const existing = await getExternalLinkTaskSnapshot(tx, {
|
|
445
|
+
...task,
|
|
446
|
+
taskId: task.id,
|
|
447
|
+
taskRevision: task.authorityRevision
|
|
448
|
+
});
|
|
449
|
+
if (existing) {
|
|
450
|
+
if (stableJson(existing) !== stableJson(snapshot))
|
|
451
|
+
throw new Error("Linked task replay changed authority");
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
await tx.execute(sql3`insert into external_link_task_authorities
|
|
455
|
+
(task_id, task_revision, account_id, workspace_id, link_id, link_revision, canonical_snapshot)
|
|
456
|
+
values (${task.id}::uuid, ${task.authorityRevision}, ${task.accountId}::uuid, ${task.workspaceId}::uuid,
|
|
457
|
+
${snapshot.actor.linkId}::uuid, ${snapshot.actor.linkRevision}, ${JSON.stringify(snapshot)}::jsonb)`);
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
async function cloneExternalLinkTaskAuthority(db, task, sourceRevision) {
|
|
462
|
+
if (task.authorityRevision === sourceRevision) return;
|
|
463
|
+
const snapshot = await getExternalLinkTaskSnapshot(db, {
|
|
464
|
+
...task,
|
|
465
|
+
taskId: task.id,
|
|
466
|
+
taskRevision: sourceRevision
|
|
467
|
+
});
|
|
468
|
+
if (snapshot) await captureExternalLinkTaskAuthority(db, task, snapshot);
|
|
469
|
+
}
|
|
470
|
+
async function captureScheduledExternalLinkTurnAuthority(db, input) {
|
|
471
|
+
const run = await withWorkspaceRls(db, input.workspaceId, async (tx) => {
|
|
472
|
+
const [row] = await rawRows(
|
|
473
|
+
tx,
|
|
474
|
+
sql3`select task_id, task_authority_revision from scheduled_task_runs
|
|
475
|
+
where account_id = ${input.accountId}::uuid and workspace_id = ${input.workspaceId}::uuid
|
|
476
|
+
and session_id = ${input.sessionId}::uuid and id = ${input.runId}::uuid`
|
|
477
|
+
);
|
|
478
|
+
return row;
|
|
479
|
+
});
|
|
480
|
+
if (!run) throw new Error("Linked schedule run unavailable");
|
|
481
|
+
const taskRevision = Number(run.task_authority_revision);
|
|
482
|
+
const snapshot = await getExternalLinkTaskSnapshot(db, {
|
|
483
|
+
...input,
|
|
484
|
+
taskId: run.task_id,
|
|
485
|
+
taskRevision
|
|
486
|
+
});
|
|
487
|
+
if (snapshot)
|
|
488
|
+
await captureExternalLinkTurnAuthority(db, {
|
|
489
|
+
...input,
|
|
490
|
+
snapshot,
|
|
491
|
+
source: { kind: "scheduled", taskId: run.task_id, taskRevision }
|
|
492
|
+
});
|
|
493
|
+
}
|
|
51
494
|
|
|
52
495
|
// src/child-lifecycle-notices.ts
|
|
53
496
|
import {
|
|
@@ -194,7 +637,7 @@ function boundedChildProgressNote(note) {
|
|
|
194
637
|
|
|
195
638
|
// src/session-tool-call-settlement.ts
|
|
196
639
|
import { canonicalizePersistedHistoryItem, omitOutputOnlyHistoryItemFields } from "@opengeni/codex";
|
|
197
|
-
import { and, asc, eq, inArray, sql } from "drizzle-orm";
|
|
640
|
+
import { and, asc, eq, inArray, sql as sql4 } from "drizzle-orm";
|
|
198
641
|
var TOOL_RESULT_TYPE_BY_CALL_TYPE = {
|
|
199
642
|
function_call: "function_call_result",
|
|
200
643
|
computer_call: "computer_call_result",
|
|
@@ -313,13 +756,13 @@ async function closePendingSessionToolCallsInTransaction(tx, input) {
|
|
|
313
756
|
...row,
|
|
314
757
|
item: fromPostgresLosslessJson(row.item, row.itemCodecVersion)
|
|
315
758
|
}));
|
|
316
|
-
const [{ maxPosition } = { maxPosition: -1 }] = await tx.select({ maxPosition:
|
|
759
|
+
const [{ maxPosition } = { maxPosition: -1 }] = await tx.select({ maxPosition: sql4`coalesce(max(${sessionHistoryItems.position}), -1)` }).from(sessionHistoryItems).where(
|
|
317
760
|
and(
|
|
318
761
|
eq(sessionHistoryItems.workspaceId, input.workspaceId),
|
|
319
762
|
eq(sessionHistoryItems.sessionId, input.sessionId)
|
|
320
763
|
)
|
|
321
764
|
);
|
|
322
|
-
const existingOutputEvents = await tx.select({ callId:
|
|
765
|
+
const existingOutputEvents = await tx.select({ callId: sql4`${sessionEvents.payload} ->> 'id'` }).from(sessionEvents).where(
|
|
323
766
|
and(
|
|
324
767
|
eq(sessionEvents.workspaceId, input.workspaceId),
|
|
325
768
|
eq(sessionEvents.sessionId, input.sessionId),
|
|
@@ -460,13 +903,13 @@ async function closePendingSessionToolCallsInTransaction(tx, input) {
|
|
|
460
903
|
}
|
|
461
904
|
|
|
462
905
|
// src/session-realtime-mirror.ts
|
|
463
|
-
import { createHash } from "crypto";
|
|
464
|
-
import { and as and2, desc, eq as eq2, gt, sql as
|
|
906
|
+
import { createHash as createHash2 } from "crypto";
|
|
907
|
+
import { and as and2, desc, eq as eq2, gt, sql as sql5 } from "drizzle-orm";
|
|
465
908
|
var SESSION_REALTIME_MIRROR_MAX_TEXT_BYTES = 131072;
|
|
466
909
|
var SESSION_REALTIME_MIRROR_MAX_PAYLOAD_BYTES = 131072;
|
|
467
910
|
var TRUNCATION_MARKER = "\n\u2026realtime context truncated\u2026";
|
|
468
911
|
function deterministicUuid(seed) {
|
|
469
|
-
const bytes =
|
|
912
|
+
const bytes = createHash2("sha256").update(seed, "utf8").digest().subarray(0, 16);
|
|
470
913
|
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
471
914
|
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
472
915
|
const hex = bytes.toString("hex");
|
|
@@ -517,7 +960,7 @@ async function mirrorSessionRealtimeContextInTransaction(db, input) {
|
|
|
517
960
|
)
|
|
518
961
|
).limit(1);
|
|
519
962
|
if (existing) return { entry: existing, replay: true };
|
|
520
|
-
const [sequenceRow] = await db.select({ next:
|
|
963
|
+
const [sequenceRow] = await db.select({ next: sql5`coalesce(max(${sessionRealtimeEntries.sequence}), 0) + 1` }).from(sessionRealtimeEntries).where(eq2(sessionRealtimeEntries.realtimeId, mode.id));
|
|
521
964
|
const payload = boundedPayload({
|
|
522
965
|
...input.payload ?? {},
|
|
523
966
|
source: input.sourceKind,
|
|
@@ -718,7 +1161,7 @@ async function assertAgentCommandAuthorityInTransaction(db, input) {
|
|
|
718
1161
|
if (!targetSession) {
|
|
719
1162
|
throw new SessionControlInvariantError(`Target session not found: ${input.targetSessionId}`);
|
|
720
1163
|
}
|
|
721
|
-
const rows = await db.execute(
|
|
1164
|
+
const rows = await db.execute(sql6`
|
|
722
1165
|
select
|
|
723
1166
|
attempt.id as "attemptId",
|
|
724
1167
|
attempt.state as "attemptState",
|
|
@@ -756,6 +1199,19 @@ async function assertAgentCommandAuthorityInTransaction(db, input) {
|
|
|
756
1199
|
"The calling agent attempt is being interrupted"
|
|
757
1200
|
);
|
|
758
1201
|
}
|
|
1202
|
+
const linked = await getExternalLinkTurnAuthorization(
|
|
1203
|
+
db,
|
|
1204
|
+
{
|
|
1205
|
+
accountId: callerSession.accountId,
|
|
1206
|
+
workspaceId: input.workspaceId
|
|
1207
|
+
},
|
|
1208
|
+
input.actor.turnId
|
|
1209
|
+
);
|
|
1210
|
+
if (linked && !linked.authorized)
|
|
1211
|
+
throw new AgentCommandAuthorityError(
|
|
1212
|
+
"CALLER_STALE",
|
|
1213
|
+
"The calling agent's native identity link is no longer active"
|
|
1214
|
+
);
|
|
759
1215
|
if (input.action === "steer" && input.targetSessionId === input.actor.sessionId) {
|
|
760
1216
|
throw new AgentCommandAuthorityError("SELF_STEER", "An agent cannot steer its own session");
|
|
761
1217
|
}
|
|
@@ -782,14 +1238,14 @@ function canonicalize(value) {
|
|
|
782
1238
|
return value;
|
|
783
1239
|
}
|
|
784
1240
|
function canonicalSessionCommandHash(value) {
|
|
785
|
-
return
|
|
1241
|
+
return createHash3("sha256").update(JSON.stringify(canonicalize(value))).digest("hex");
|
|
786
1242
|
}
|
|
787
1243
|
function controlEtag(value) {
|
|
788
1244
|
return `sc1:${canonicalSessionCommandHash(value)}`;
|
|
789
1245
|
}
|
|
790
1246
|
function lockClause(mode) {
|
|
791
|
-
if (mode === "none") return
|
|
792
|
-
return mode === "update" ?
|
|
1247
|
+
if (mode === "none") return sql6.empty();
|
|
1248
|
+
return mode === "update" ? sql6.raw("for update") : sql6.raw("for share");
|
|
793
1249
|
}
|
|
794
1250
|
var WORKSPACE_CONTROL_REQUEST_LOCK_TIMEOUT_MS = 2e4;
|
|
795
1251
|
var configuredRequestLockTimeoutMs = WORKSPACE_CONTROL_REQUEST_LOCK_TIMEOUT_MS;
|
|
@@ -819,7 +1275,7 @@ function workspaceControlLockBudget(lockTimeoutMs) {
|
|
|
819
1275
|
return { totalMs, deadlineAt: Date.now() + totalMs };
|
|
820
1276
|
}
|
|
821
1277
|
function advisoryLockFunction(mode) {
|
|
822
|
-
return mode === "update" ?
|
|
1278
|
+
return mode === "update" ? sql6.raw("pg_advisory_xact_lock") : sql6.raw("pg_advisory_xact_lock_shared");
|
|
823
1279
|
}
|
|
824
1280
|
function lockBudgetFor(options) {
|
|
825
1281
|
if (options.budget) return options.budget;
|
|
@@ -831,7 +1287,7 @@ async function boundedLockStep(db, workspaceId, budget, step) {
|
|
|
831
1287
|
const remainingMs = budget.deadlineAt - Date.now();
|
|
832
1288
|
if (remainingMs <= 0) throw new WorkspaceControlBusyError(workspaceId, budget.totalMs);
|
|
833
1289
|
await db.execute(
|
|
834
|
-
|
|
1290
|
+
sql6`select set_config('lock_timeout', ${`${Math.max(1, Math.floor(remainingMs))}ms`}, true)`
|
|
835
1291
|
);
|
|
836
1292
|
try {
|
|
837
1293
|
return await step();
|
|
@@ -844,14 +1300,14 @@ async function boundedLockStep(db, workspaceId, budget, step) {
|
|
|
844
1300
|
}
|
|
845
1301
|
async function lockWorkspaceInferenceControl(db, workspaceId, mode, options = {}) {
|
|
846
1302
|
const budget = lockBudgetFor(options);
|
|
847
|
-
const [prior] = budget ? await db.execute(
|
|
1303
|
+
const [prior] = budget ? await db.execute(sql6`select current_setting('lock_timeout') as value`) : [];
|
|
848
1304
|
if (mode !== "none") {
|
|
849
1305
|
await boundedLockStep(
|
|
850
1306
|
db,
|
|
851
1307
|
workspaceId,
|
|
852
1308
|
budget,
|
|
853
1309
|
() => db.execute(
|
|
854
|
-
|
|
1310
|
+
sql6`select ${advisoryLockFunction(mode)}(hashtextextended(${`workspace-control:${workspaceId}`}, 0))`
|
|
855
1311
|
)
|
|
856
1312
|
);
|
|
857
1313
|
}
|
|
@@ -859,7 +1315,7 @@ async function lockWorkspaceInferenceControl(db, workspaceId, mode, options = {}
|
|
|
859
1315
|
db,
|
|
860
1316
|
workspaceId,
|
|
861
1317
|
budget,
|
|
862
|
-
() => db.execute(
|
|
1318
|
+
() => db.execute(sql6`
|
|
863
1319
|
select
|
|
864
1320
|
workspace_id as "workspaceId",
|
|
865
1321
|
account_id as "accountId",
|
|
@@ -878,7 +1334,7 @@ async function lockWorkspaceInferenceControl(db, workspaceId, mode, options = {}
|
|
|
878
1334
|
`)
|
|
879
1335
|
);
|
|
880
1336
|
if (budget) {
|
|
881
|
-
await db.execute(
|
|
1337
|
+
await db.execute(sql6`select set_config('lock_timeout', ${prior?.value ?? "0"}, true)`);
|
|
882
1338
|
}
|
|
883
1339
|
const row = rows[0];
|
|
884
1340
|
if (!row) {
|
|
@@ -891,21 +1347,21 @@ async function lockWorkspaceInferenceControl(db, workspaceId, mode, options = {}
|
|
|
891
1347
|
var WORKSPACE_CONTROL_ADMISSION_SAVEPOINT = "opengeni_workspace_control_admission";
|
|
892
1348
|
async function lockWorkspaceInferenceControlForAdmission(db, input) {
|
|
893
1349
|
const lockOptions = input.lockTimeoutMs === void 0 ? {} : { budget: workspaceControlLockBudget(input.lockTimeoutMs) };
|
|
894
|
-
await db.execute(
|
|
1350
|
+
await db.execute(sql6.raw(`savepoint ${WORKSPACE_CONTROL_ADMISSION_SAVEPOINT}`));
|
|
895
1351
|
const shared = await lockWorkspaceInferenceControl(db, input.workspaceId, "share", lockOptions);
|
|
896
1352
|
if (!input.resumePausedBranch) {
|
|
897
|
-
await db.execute(
|
|
1353
|
+
await db.execute(sql6.raw(`release savepoint ${WORKSPACE_CONTROL_ADMISSION_SAVEPOINT}`));
|
|
898
1354
|
return { control: shared, mode: "share" };
|
|
899
1355
|
}
|
|
900
1356
|
const effective = await evaluateSessionControl(db, input.workspaceId, input.sessionId, {
|
|
901
1357
|
workspaceControl: shared
|
|
902
1358
|
});
|
|
903
1359
|
if (effective.state === "active") {
|
|
904
|
-
await db.execute(
|
|
1360
|
+
await db.execute(sql6.raw(`release savepoint ${WORKSPACE_CONTROL_ADMISSION_SAVEPOINT}`));
|
|
905
1361
|
return { control: shared, mode: "share" };
|
|
906
1362
|
}
|
|
907
|
-
await db.execute(
|
|
908
|
-
await db.execute(
|
|
1363
|
+
await db.execute(sql6.raw(`rollback to savepoint ${WORKSPACE_CONTROL_ADMISSION_SAVEPOINT}`));
|
|
1364
|
+
await db.execute(sql6.raw(`release savepoint ${WORKSPACE_CONTROL_ADMISSION_SAVEPOINT}`));
|
|
909
1365
|
return {
|
|
910
1366
|
control: await lockWorkspaceInferenceControl(db, input.workspaceId, "update", lockOptions),
|
|
911
1367
|
mode: "update"
|
|
@@ -1068,7 +1524,7 @@ async function closeSessionTurnAttemptInTransaction(db, input) {
|
|
|
1068
1524
|
and3(
|
|
1069
1525
|
eq3(sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
1070
1526
|
eq3(sessionTurnAttempts.id, input.id),
|
|
1071
|
-
|
|
1527
|
+
sql6`${sessionTurnAttempts.state} in ('claimed', 'running')`
|
|
1072
1528
|
)
|
|
1073
1529
|
).returning({ id: sessionTurnAttempts.id });
|
|
1074
1530
|
if (!closed) {
|
|
@@ -1080,14 +1536,14 @@ function targetValues(sessionIds) {
|
|
|
1080
1536
|
if (sessionIds.length === 0) {
|
|
1081
1537
|
throw new SessionControlInvariantError("At least one target session is required");
|
|
1082
1538
|
}
|
|
1083
|
-
return
|
|
1084
|
-
sessionIds.map((id) =>
|
|
1085
|
-
|
|
1539
|
+
return sql6.join(
|
|
1540
|
+
sessionIds.map((id) => sql6`(${id}::uuid)`),
|
|
1541
|
+
sql6`, `
|
|
1086
1542
|
);
|
|
1087
1543
|
}
|
|
1088
1544
|
var TARGET_PATH_PROJECTION_LIMIT = 128;
|
|
1089
1545
|
async function loadTargetAncestryRows(db, workspaceId, sessionIds) {
|
|
1090
|
-
return await db.execute(
|
|
1546
|
+
return await db.execute(sql6`
|
|
1091
1547
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1092
1548
|
ancestry as (
|
|
1093
1549
|
select
|
|
@@ -1150,7 +1606,7 @@ async function loadTargetAncestryRows(db, workspaceId, sessionIds) {
|
|
|
1150
1606
|
`);
|
|
1151
1607
|
}
|
|
1152
1608
|
async function loadAncestryNodes(db, workspaceId, sessionIds) {
|
|
1153
|
-
const rows = await db.execute(
|
|
1609
|
+
const rows = await db.execute(sql6`
|
|
1154
1610
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1155
1611
|
ancestry_ids(id) as (
|
|
1156
1612
|
select session.id
|
|
@@ -1344,7 +1800,7 @@ function projectEffectiveControl(workspace, targetId, rows, settlementAttempts,
|
|
|
1344
1800
|
};
|
|
1345
1801
|
}
|
|
1346
1802
|
async function stoppingBackgroundCommandCounts(db, workspaceId, sessionIds) {
|
|
1347
|
-
const rows = await db.execute(
|
|
1803
|
+
const rows = await db.execute(sql6`
|
|
1348
1804
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1349
1805
|
descendants(target_id, session_id, depth, path) as (
|
|
1350
1806
|
select target.id, target.id, 0::integer, array[target.id]::uuid[] from targets target
|
|
@@ -1370,7 +1826,7 @@ async function stoppingBackgroundCommandCounts(db, workspaceId, sessionIds) {
|
|
|
1370
1826
|
);
|
|
1371
1827
|
}
|
|
1372
1828
|
async function settlementAttemptCounts(db, workspaceId, sessionIds) {
|
|
1373
|
-
const rows = await db.execute(
|
|
1829
|
+
const rows = await db.execute(sql6`
|
|
1374
1830
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1375
1831
|
descendant_sessions(target_id, session_id, depth, path) as (
|
|
1376
1832
|
select target.id, target.id, 0::integer, array[target.id]::uuid[]
|
|
@@ -1524,7 +1980,7 @@ async function evaluateSessionDiscoveryControls(db, workspaceId, sessionIds) {
|
|
|
1524
1980
|
if (workspace.workspaceState === "paused" && workspacePauseRevision === null) {
|
|
1525
1981
|
throw new SessionControlInvariantError("Paused workspace is missing its pause revision");
|
|
1526
1982
|
}
|
|
1527
|
-
const rows = await db.execute(
|
|
1983
|
+
const rows = await db.execute(sql6`
|
|
1528
1984
|
with recursive targets(id) as (values ${targetValues(uniqueIds)}),
|
|
1529
1985
|
ancestry as (
|
|
1530
1986
|
select
|
|
@@ -1696,16 +2152,16 @@ async function findCommandReceipt(db, input) {
|
|
|
1696
2152
|
eq3(sessionCommandReceipts.actorType, "agent_attempt"),
|
|
1697
2153
|
eq3(sessionCommandReceipts.action, input.action),
|
|
1698
2154
|
eq3(sessionCommandReceipts.targetSessionId, input.targetSessionId),
|
|
1699
|
-
|
|
2155
|
+
sql6`${sessionCommandReceipts.targetTurnId} is null`,
|
|
1700
2156
|
eq3(sessionCommandReceipts.operationKey, input.operationKey)
|
|
1701
2157
|
) : and3(
|
|
1702
2158
|
eq3(sessionCommandReceipts.workspaceId, input.workspaceId),
|
|
1703
2159
|
eq3(sessionCommandReceipts.actorType, input.actor.type),
|
|
1704
|
-
|
|
1705
|
-
|
|
2160
|
+
sql6`${sessionCommandReceipts.actorSubjectId} is not distinct from ${actorSubjectId}`,
|
|
2161
|
+
sql6`${sessionCommandReceipts.actorAttemptId} is not distinct from ${actorAttemptId}::uuid`,
|
|
1706
2162
|
eq3(sessionCommandReceipts.action, input.action),
|
|
1707
|
-
|
|
1708
|
-
|
|
2163
|
+
sql6`${sessionCommandReceipts.targetSessionId} is not distinct from ${input.targetSessionId}::uuid`,
|
|
2164
|
+
sql6`${sessionCommandReceipts.targetTurnId} is not distinct from ${input.targetTurnId}::uuid`,
|
|
1709
2165
|
eq3(sessionCommandReceipts.operationKey, input.operationKey)
|
|
1710
2166
|
);
|
|
1711
2167
|
const rows = await db.select().from(sessionCommandReceipts).where(identity).for("update");
|
|
@@ -1718,8 +2174,8 @@ async function findPromptCommandReceipts(db, input) {
|
|
|
1718
2174
|
and3(
|
|
1719
2175
|
eq3(sessionCommandReceipts.workspaceId, input.workspaceId),
|
|
1720
2176
|
eq3(sessionCommandReceipts.actorType, input.actor.type),
|
|
1721
|
-
|
|
1722
|
-
|
|
2177
|
+
sql6`${sessionCommandReceipts.actorSubjectId} is not distinct from ${actorSubjectId}`,
|
|
2178
|
+
sql6`${sessionCommandReceipts.actorAttemptId} is not distinct from ${actorAttemptId}::uuid`,
|
|
1723
2179
|
inArray2(sessionCommandReceipts.action, ["prompt.send", "prompt.steer"]),
|
|
1724
2180
|
eq3(sessionCommandReceipts.operationKey, input.operationKey)
|
|
1725
2181
|
)
|
|
@@ -1780,7 +2236,7 @@ async function reserveSessionPromptCommandReceipt(db, input) {
|
|
|
1780
2236
|
async function lockSessionPromptCommandOperation(db, input) {
|
|
1781
2237
|
const actorIdentity = input.actor.type === "agent_attempt" ? `attempt:${input.actor.attemptId}` : `${input.actor.type}:${input.actor.subjectId}`;
|
|
1782
2238
|
await db.execute(
|
|
1783
|
-
|
|
2239
|
+
sql6`select pg_advisory_xact_lock(hashtextextended(${`session-prompt:${input.workspaceId}:${actorIdentity}:${input.operationKey}`}, 0))`
|
|
1784
2240
|
);
|
|
1785
2241
|
}
|
|
1786
2242
|
async function getCompletedSessionPromptCommandReceipt(db, input) {
|
|
@@ -1857,7 +2313,7 @@ async function advanceWorkspaceRevision(db, workspaceId, revision) {
|
|
|
1857
2313
|
}
|
|
1858
2314
|
}
|
|
1859
2315
|
async function registerContinuableWakes(db, input) {
|
|
1860
|
-
const rows = await db.execute(
|
|
2316
|
+
const rows = await db.execute(sql6`
|
|
1861
2317
|
with eligible as (
|
|
1862
2318
|
select *
|
|
1863
2319
|
from opengeni_private.list_continuable_sessions(
|
|
@@ -1899,7 +2355,7 @@ async function registerWorkspaceWakes(db, input) {
|
|
|
1899
2355
|
});
|
|
1900
2356
|
}
|
|
1901
2357
|
async function registerInterruptionWakes(db, input) {
|
|
1902
|
-
const rows = await db.execute(
|
|
2358
|
+
const rows = await db.execute(sql6`
|
|
1903
2359
|
with eligible as (
|
|
1904
2360
|
select distinct
|
|
1905
2361
|
session.id as session_id,
|
|
@@ -1935,11 +2391,11 @@ async function registerInterruptionWakes(db, input) {
|
|
|
1935
2391
|
}
|
|
1936
2392
|
async function registerCancellationWakes(db, input) {
|
|
1937
2393
|
if (input.sessionIds.length === 0) return 0;
|
|
1938
|
-
const sessionIds =
|
|
1939
|
-
input.sessionIds.map((id) =>
|
|
1940
|
-
|
|
2394
|
+
const sessionIds = sql6.join(
|
|
2395
|
+
input.sessionIds.map((id) => sql6`${id}::uuid`),
|
|
2396
|
+
sql6`, `
|
|
1941
2397
|
);
|
|
1942
|
-
const rows = await db.execute(
|
|
2398
|
+
const rows = await db.execute(sql6`
|
|
1943
2399
|
with eligible as (
|
|
1944
2400
|
select
|
|
1945
2401
|
session.id as session_id,
|
|
@@ -1983,8 +2439,8 @@ async function registerSessionWorkflowWakeInTransaction(db, input) {
|
|
|
1983
2439
|
target: sessionWorkflowWakeOutbox.sessionId,
|
|
1984
2440
|
set: {
|
|
1985
2441
|
temporalWorkflowId: input.temporalWorkflowId,
|
|
1986
|
-
wakeRevision:
|
|
1987
|
-
controlRevision: input.controlRequested ?
|
|
2442
|
+
wakeRevision: sql6`${sessionWorkflowWakeOutbox.wakeRevision} + 1`,
|
|
2443
|
+
controlRevision: input.controlRequested ? sql6`${sessionWorkflowWakeOutbox.wakeRevision} + 1` : sql6`${sessionWorkflowWakeOutbox.controlRevision}`,
|
|
1988
2444
|
reason: input.reason,
|
|
1989
2445
|
attempts: 0,
|
|
1990
2446
|
nextAttemptAt: /* @__PURE__ */ new Date(),
|
|
@@ -2023,7 +2479,7 @@ async function registerInternalUpdateWakeInTransaction(db, input) {
|
|
|
2023
2479
|
};
|
|
2024
2480
|
}
|
|
2025
2481
|
async function interruptDescendantAttempts(db, input) {
|
|
2026
|
-
const rows = await db.execute(
|
|
2482
|
+
const rows = await db.execute(sql6`
|
|
2027
2483
|
with recursive live_attempts as (
|
|
2028
2484
|
select attempt.id, attempt.account_id, attempt.workspace_id, attempt.session_id
|
|
2029
2485
|
from ${sessionTurnAttempts} attempt
|
|
@@ -2071,7 +2527,7 @@ async function interruptDescendantAttempts(db, input) {
|
|
|
2071
2527
|
return Number(rows[0]?.count ?? 0);
|
|
2072
2528
|
}
|
|
2073
2529
|
async function interruptWorkspaceAttempts(db, input) {
|
|
2074
|
-
const rows = await db.execute(
|
|
2530
|
+
const rows = await db.execute(sql6`
|
|
2075
2531
|
with inserted as (
|
|
2076
2532
|
insert into ${sessionAttemptInterruptions} (
|
|
2077
2533
|
account_id, workspace_id, session_id, operation_id, attempt_id,
|
|
@@ -2127,7 +2583,7 @@ async function pendingSessionControlWorkflowWake(db, workspaceId, sessionId, wak
|
|
|
2127
2583
|
};
|
|
2128
2584
|
}
|
|
2129
2585
|
async function sessionPauseEffectsNeeded(db, input) {
|
|
2130
|
-
const rows = await db.execute(
|
|
2586
|
+
const rows = await db.execute(sql6`
|
|
2131
2587
|
with recursive subtree(id, path, cycle) as (
|
|
2132
2588
|
select session.id, array[session.id]::uuid[], false
|
|
2133
2589
|
from ${sessions} session
|
|
@@ -2178,7 +2634,7 @@ async function sessionPauseEffectsNeeded(db, input) {
|
|
|
2178
2634
|
return rows[0]?.needed ?? false;
|
|
2179
2635
|
}
|
|
2180
2636
|
async function workspacePauseEffectsNeeded(db, input) {
|
|
2181
|
-
const rows = await db.execute(
|
|
2637
|
+
const rows = await db.execute(sql6`
|
|
2182
2638
|
select exists (
|
|
2183
2639
|
select 1
|
|
2184
2640
|
from ${sessions} session
|
|
@@ -2207,7 +2663,7 @@ async function workspacePauseEffectsNeeded(db, input) {
|
|
|
2207
2663
|
return rows[0]?.needed ?? false;
|
|
2208
2664
|
}
|
|
2209
2665
|
async function continuableWakeRepairNeeded(db, input) {
|
|
2210
|
-
const rows = await db.execute(
|
|
2666
|
+
const rows = await db.execute(sql6`
|
|
2211
2667
|
select exists (
|
|
2212
2668
|
select 1
|
|
2213
2669
|
from opengeni_private.list_continuable_sessions(
|
|
@@ -2224,7 +2680,7 @@ async function continuableWakeRepairNeeded(db, input) {
|
|
|
2224
2680
|
return rows[0]?.needed ?? false;
|
|
2225
2681
|
}
|
|
2226
2682
|
async function loadSessionSubtreeIds(db, workspaceId, rootSessionId) {
|
|
2227
|
-
const rows = await db.execute(
|
|
2683
|
+
const rows = await db.execute(sql6`
|
|
2228
2684
|
with recursive subtree(id, root_parent_session_id, depth, path, cycle) as (
|
|
2229
2685
|
select session.id, session.parent_session_id, 0::integer, array[session.id]::uuid[], false
|
|
2230
2686
|
from ${sessions} session
|
|
@@ -2418,7 +2874,7 @@ async function enqueueCancelledChildRequiresActionResolvedOutboxInTransaction(db
|
|
|
2418
2874
|
}
|
|
2419
2875
|
}
|
|
2420
2876
|
async function assertSessionBranchIsNotCancelled(db, workspaceId, sessionId) {
|
|
2421
|
-
const rows = await db.execute(
|
|
2877
|
+
const rows = await db.execute(sql6`
|
|
2422
2878
|
with recursive ancestry(id, parent_id, status, depth, path, cycle) as (
|
|
2423
2879
|
select session.id, session.parent_session_id, session.status, 0::integer,
|
|
2424
2880
|
array[session.id]::uuid[], false
|
|
@@ -2493,7 +2949,7 @@ async function cancelSessionSubtreeInTransaction(db, input) {
|
|
|
2493
2949
|
activeAttemptId: null,
|
|
2494
2950
|
cancelledBy: input.actor,
|
|
2495
2951
|
cancelReason: input.reason ?? "session_cancelled",
|
|
2496
|
-
version:
|
|
2952
|
+
version: sql6`${sessionTurns.version} + 1`,
|
|
2497
2953
|
finishedAt: now,
|
|
2498
2954
|
updatedAt: now
|
|
2499
2955
|
}).where(inArray2(sessionTurns.id, immediatelyCancelledTurnIds));
|
|
@@ -2677,7 +3133,7 @@ async function cancelSessionSubtreeInTransaction(db, input) {
|
|
|
2677
3133
|
await db.update(sessions).set({
|
|
2678
3134
|
status: "cancelled",
|
|
2679
3135
|
activeTurnId: liveTurnId,
|
|
2680
|
-
queueVersion:
|
|
3136
|
+
queueVersion: sql6`${sessions.queueVersion} + 1`,
|
|
2681
3137
|
queueHeadPosition: 0,
|
|
2682
3138
|
queueTailPosition: 0,
|
|
2683
3139
|
lastSequence: sequence,
|
|
@@ -2826,7 +3282,7 @@ async function mutateSessionControlInTransaction(db, input) {
|
|
|
2826
3282
|
rootSessionId: input.sessionId
|
|
2827
3283
|
});
|
|
2828
3284
|
if (!changed) {
|
|
2829
|
-
const [quiescence] = input.action === "pause" ? await db.execute(
|
|
3285
|
+
const [quiescence] = input.action === "pause" ? await db.execute(sql6`
|
|
2830
3286
|
select exists (
|
|
2831
3287
|
select 1 from ${sessionTurnAttempts} attempt
|
|
2832
3288
|
where attempt.workspace_id = ${input.workspaceId}
|
|
@@ -3372,7 +3828,7 @@ async function setWorkspacePauseTimerInTransaction(db, input) {
|
|
|
3372
3828
|
}
|
|
3373
3829
|
}
|
|
3374
3830
|
const hasTimer = input.action === "set" && delay > 0;
|
|
3375
|
-
const [clock] = await db.execute(
|
|
3831
|
+
const [clock] = await db.execute(sql6`select clock_timestamp() as now`);
|
|
3376
3832
|
const revision = nextRevision(workspace);
|
|
3377
3833
|
await db.update(workspaceInferenceControls).set({
|
|
3378
3834
|
...clearWorkspaceTimerFields,
|
|
@@ -3407,7 +3863,7 @@ async function fireWorkspacePauseTimerInTransaction(db, input) {
|
|
|
3407
3863
|
const workspace = await lockWorkspaceInferenceControl(db, input.workspaceId, "update", {
|
|
3408
3864
|
lockTimeoutMs: 1e3
|
|
3409
3865
|
});
|
|
3410
|
-
const [clock] = await db.execute(
|
|
3866
|
+
const [clock] = await db.execute(sql6`select clock_timestamp() as now`);
|
|
3411
3867
|
if (workspace.timerId !== input.timerId || !workspace.timerDueAt || new Date(workspace.timerDueAt).getTime() > new Date(clock.now).getTime())
|
|
3412
3868
|
return null;
|
|
3413
3869
|
const action = workspace.timerAction;
|
|
@@ -3423,7 +3879,7 @@ async function fireWorkspacePauseTimerInTransaction(db, input) {
|
|
|
3423
3879
|
action,
|
|
3424
3880
|
timerExecution: true
|
|
3425
3881
|
});
|
|
3426
|
-
const [applied] = await db.execute(
|
|
3882
|
+
const [applied] = await db.execute(sql6`select clock_timestamp() as now`);
|
|
3427
3883
|
await db.update(workspaceInferenceControls).set({
|
|
3428
3884
|
...clearWorkspaceTimerFields,
|
|
3429
3885
|
...action === "pause" && workspace.timerPauseForSeconds !== null ? {
|
|
@@ -3457,7 +3913,7 @@ function commandPreview(value) {
|
|
|
3457
3913
|
const normalized = value.replace(/\s+/g, " ").trim();
|
|
3458
3914
|
return Array.from(normalized).slice(0, 512).join("");
|
|
3459
3915
|
}
|
|
3460
|
-
var commandObservationUnavailable =
|
|
3916
|
+
var commandObservationUnavailable = sql7`
|
|
3461
3917
|
${sessionBackgroundCommands.state} in ('running','stopping') and exists (
|
|
3462
3918
|
select 1 from sandbox_retained_processes process
|
|
3463
3919
|
where process.id = ${sessionBackgroundCommands.retainedProcessId}
|
|
@@ -3510,9 +3966,9 @@ async function backgroundCommandActivityForSessions(db, input) {
|
|
|
3510
3966
|
async (scopedDb) => {
|
|
3511
3967
|
const rows = await scopedDb.select({
|
|
3512
3968
|
sessionId: sessionBackgroundCommands.sessionId,
|
|
3513
|
-
count:
|
|
3514
|
-
stoppingCount:
|
|
3515
|
-
unavailableCount:
|
|
3969
|
+
count: sql7`count(*)::int`,
|
|
3970
|
+
stoppingCount: sql7`count(*) filter (where ${sessionBackgroundCommands.state} = 'stopping')::int`,
|
|
3971
|
+
unavailableCount: sql7`count(*) filter (where ${commandObservationUnavailable})::int`
|
|
3516
3972
|
}).from(sessionBackgroundCommands).where(
|
|
3517
3973
|
and4(
|
|
3518
3974
|
eq4(sessionBackgroundCommands.workspaceId, input.workspaceId),
|
|
@@ -3581,7 +4037,7 @@ async function insertManagedSessionBackgroundCommandInTransaction(db, input) {
|
|
|
3581
4037
|
launchExecutionGeneration: input.executionGeneration ?? null
|
|
3582
4038
|
}).onConflictDoUpdate({
|
|
3583
4039
|
target: sessionBackgroundCommands.retainedProcessId,
|
|
3584
|
-
targetWhere:
|
|
4040
|
+
targetWhere: sql7`${sessionBackgroundCommands.retainedProcessId} is not null`,
|
|
3585
4041
|
set: { updatedAt: /* @__PURE__ */ new Date() }
|
|
3586
4042
|
}).returning();
|
|
3587
4043
|
if (!row) throw new Error("Managed background command adoption returned no row");
|
|
@@ -3614,7 +4070,7 @@ async function insertConnectedMachineSessionBackgroundCommandInTransaction(db, i
|
|
|
3614
4070
|
sessionBackgroundCommands.connectionInstanceId,
|
|
3615
4071
|
sessionBackgroundCommands.opId
|
|
3616
4072
|
],
|
|
3617
|
-
targetWhere:
|
|
4073
|
+
targetWhere: sql7`${sessionBackgroundCommands.provider} = 'connected_machine'`,
|
|
3618
4074
|
set: { updatedAt: /* @__PURE__ */ new Date() }
|
|
3619
4075
|
}).returning();
|
|
3620
4076
|
if (!row) throw new Error("Connected Machine background command adoption returned no row");
|
|
@@ -3632,7 +4088,7 @@ async function claimConnectedMachineSessionBackgroundCommands(db, input) {
|
|
|
3632
4088
|
if (!Number.isSafeInteger(input.claimTtlMs) || input.claimTtlMs < 0 || input.claimTtlMs > 36e5) {
|
|
3633
4089
|
throw new Error("Connected command reconciliation claim TTL is invalid");
|
|
3634
4090
|
}
|
|
3635
|
-
const rows = await db.execute(
|
|
4091
|
+
const rows = await db.execute(sql7`
|
|
3636
4092
|
select account_id as "accountId",
|
|
3637
4093
|
workspace_id as "workspaceId",
|
|
3638
4094
|
session_id as "sessionId",
|
|
@@ -3767,7 +4223,7 @@ async function settleClaimedConnectedMachineBackgroundCommandWithMutation(db, in
|
|
|
3767
4223
|
{ accountId: input.claim.accountId, workspaceId: input.claim.workspaceId },
|
|
3768
4224
|
async (tx) => {
|
|
3769
4225
|
await mutateTerminal.prepare(tx);
|
|
3770
|
-
const rows = await tx.execute(
|
|
4226
|
+
const rows = await tx.execute(sql7`
|
|
3771
4227
|
update ${sessionBackgroundCommands} command set
|
|
3772
4228
|
state = command.reconcile_proof_outcome,
|
|
3773
4229
|
exit_code = case
|
|
@@ -3834,7 +4290,7 @@ async function requestSessionBackgroundCommandCancellation(db, input) {
|
|
|
3834
4290
|
and4(
|
|
3835
4291
|
eq4(sandboxRetainedProcesses.id, current.retainedProcessId),
|
|
3836
4292
|
eq4(sandboxRetainedProcesses.state, "active"),
|
|
3837
|
-
|
|
4293
|
+
sql7`${sandboxRetainedProcesses.reconcileClaimId} is null`
|
|
3838
4294
|
)
|
|
3839
4295
|
);
|
|
3840
4296
|
}
|
|
@@ -3845,11 +4301,11 @@ async function requestSessionBackgroundCommandCancellation(db, input) {
|
|
|
3845
4301
|
async function requestSessionBackgroundCommandCancellationsInTransaction(db, input) {
|
|
3846
4302
|
const rootSessionIds = [...new Set(input.rootSessionIds)];
|
|
3847
4303
|
if (rootSessionIds.length === 0) return 0;
|
|
3848
|
-
const rows = await db.execute(
|
|
4304
|
+
const rows = await db.execute(sql7`
|
|
3849
4305
|
with recursive roots(id) as (
|
|
3850
|
-
values ${
|
|
3851
|
-
rootSessionIds.map((sessionId) =>
|
|
3852
|
-
|
|
4306
|
+
values ${sql7.join(
|
|
4307
|
+
rootSessionIds.map((sessionId) => sql7`(${sessionId}::uuid)`),
|
|
4308
|
+
sql7`, `
|
|
3853
4309
|
)}
|
|
3854
4310
|
), subtree(id, path, cycle) as (
|
|
3855
4311
|
select session.id, array[session.id]::uuid[], false
|
|
@@ -3896,7 +4352,7 @@ async function requestSessionBackgroundCommandCancellationsInTransaction(db, inp
|
|
|
3896
4352
|
return Number(rows[0]?.commandCount ?? 0);
|
|
3897
4353
|
}
|
|
3898
4354
|
async function requestWorkspaceBackgroundCommandCancellationsInTransaction(db, input) {
|
|
3899
|
-
const rows = await db.execute(
|
|
4355
|
+
const rows = await db.execute(sql7`
|
|
3900
4356
|
with stopped as (
|
|
3901
4357
|
update ${sessionBackgroundCommands} command set
|
|
3902
4358
|
state = 'stopping',
|
|
@@ -3968,7 +4424,7 @@ async function settleSessionBackgroundCommandForRetainedProcessInTransaction(tx,
|
|
|
3968
4424
|
eq4(sessionEvents.workspaceId, input.workspaceId),
|
|
3969
4425
|
eq4(sessionEvents.sessionId, input.sessionId),
|
|
3970
4426
|
eq4(sessionEvents.type, "session.command.finished"),
|
|
3971
|
-
|
|
4427
|
+
sql7`${sessionEvents.payload} ->> 'commandId' = ${current.id}`
|
|
3972
4428
|
)
|
|
3973
4429
|
).limit(1);
|
|
3974
4430
|
if (existingEvent) return mapCommand(current);
|
|
@@ -4024,7 +4480,7 @@ async function settleConnectedMachineSessionBackgroundCommandWithMutation(db, in
|
|
|
4024
4480
|
eq4(sessionBackgroundCommands.opId, input.opId),
|
|
4025
4481
|
// A fast owner settlement cannot erase or replace metadata already
|
|
4026
4482
|
// checkpointed by the reconciler under the exact command identity.
|
|
4027
|
-
|
|
4483
|
+
sql7`(${sessionBackgroundCommands.runnerFailure} is null or
|
|
4028
4484
|
${sessionBackgroundCommands.runnerFailure} = ${failure ? JSON.stringify(failure) : null}::jsonb)`,
|
|
4029
4485
|
inArray3(sessionBackgroundCommands.state, ["running", "stopping"])
|
|
4030
4486
|
)
|
|
@@ -4173,7 +4629,7 @@ async function readSessionBackgroundCommandOutput(db, input) {
|
|
|
4173
4629
|
// Bound legacy payloads in SQL too: LIMIT alone cannot bound one old event.
|
|
4174
4630
|
// Oversized historical rows carry explicit loss rather than allocating an
|
|
4175
4631
|
// unbounded JSON value in the API process.
|
|
4176
|
-
payload:
|
|
4632
|
+
payload: sql7`case when octet_length(${sessionEvents.payload}::text) <= 262144
|
|
4177
4633
|
then ${sessionEvents.payload}
|
|
4178
4634
|
else jsonb_build_object('commandReadProjectionGap', true) end`,
|
|
4179
4635
|
payloadCodecVersion: sessionEvents.payloadCodecVersion
|
|
@@ -4182,7 +4638,7 @@ async function readSessionBackgroundCommandOutput(db, input) {
|
|
|
4182
4638
|
eq4(sessionEvents.workspaceId, input.workspaceId),
|
|
4183
4639
|
eq4(sessionEvents.sessionId, input.sessionId),
|
|
4184
4640
|
eq4(sessionEvents.type, "sandbox.command.output.delta"),
|
|
4185
|
-
|
|
4641
|
+
sql7`${sessionEvents.payload} ->> 'commandId' = ${input.commandId}`,
|
|
4186
4642
|
gte(sessionEvents.sequence, cursor.sequence)
|
|
4187
4643
|
)
|
|
4188
4644
|
).orderBy(asc2(sessionEvents.sequence)).limit(COMMAND_OUTPUT_PAGE_ROWS + 1)
|
|
@@ -4203,6 +4659,26 @@ async function readSessionBackgroundCommandOutput(db, input) {
|
|
|
4203
4659
|
}
|
|
4204
4660
|
|
|
4205
4661
|
export {
|
|
4662
|
+
lockActiveExternalOrganizationKey,
|
|
4663
|
+
lockExternalWorkspaceMembershipLifecycle,
|
|
4664
|
+
ensureExternalIdentity,
|
|
4665
|
+
ExternalIdentityLinkConflictError,
|
|
4666
|
+
beginExternalIdentityLink,
|
|
4667
|
+
getExternalIdentityLink,
|
|
4668
|
+
listExternalIdentityLinks,
|
|
4669
|
+
previewExternalIdentityLink,
|
|
4670
|
+
confirmExternalIdentityLink,
|
|
4671
|
+
revokeExternalIdentityLink,
|
|
4672
|
+
resolveExternalIdentityLink,
|
|
4673
|
+
externalLinkWorkSnapshotIsLive,
|
|
4674
|
+
getExternalLinkTurnSnapshot,
|
|
4675
|
+
getExternalLinkTurnAuthorization,
|
|
4676
|
+
captureExternalLinkTurnAuthority,
|
|
4677
|
+
inheritExternalLinkTurnAuthority,
|
|
4678
|
+
getExternalLinkTaskSnapshot,
|
|
4679
|
+
captureExternalLinkTaskAuthority,
|
|
4680
|
+
cloneExternalLinkTaskAuthority,
|
|
4681
|
+
captureScheduledExternalLinkTurnAuthority,
|
|
4206
4682
|
configureChildLifecycleNotices,
|
|
4207
4683
|
childLifecycleNoticesEnabled,
|
|
4208
4684
|
boundedChildNoticeText,
|
|
@@ -4293,4 +4769,4 @@ export {
|
|
|
4293
4769
|
setWorkspacePauseTimerInTransaction,
|
|
4294
4770
|
fireWorkspacePauseTimerInTransaction
|
|
4295
4771
|
};
|
|
4296
|
-
//# sourceMappingURL=chunk-
|
|
4772
|
+
//# sourceMappingURL=chunk-7JPLGBSA.js.map
|