@opengeni/core 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +86 -3
- package/dist/index.js +485 -93
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/application/new-session-drafts.ts +126 -0
- package/src/domain/scheduled-tasks.ts +23 -1
- package/src/domain/sessions.ts +115 -50
- package/src/index.ts +1 -0
- package/src/sandbox/fleet.ts +149 -39
- package/src/sandbox/routing.ts +261 -3
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getSandbox as getSandbox2,
|
|
10
10
|
listSandboxes,
|
|
11
11
|
readActiveSandbox as readActiveSandbox2,
|
|
12
|
+
readLease,
|
|
12
13
|
requireSession,
|
|
13
14
|
setActiveSandbox
|
|
14
15
|
} from "@opengeni/db";
|
|
@@ -21,8 +22,20 @@ import {
|
|
|
21
22
|
import { HTTPException } from "hono/http-exception";
|
|
22
23
|
|
|
23
24
|
// src/sandbox/routing.ts
|
|
24
|
-
import { getSandbox, readActiveSandbox } from "@opengeni/db";
|
|
25
25
|
import {
|
|
26
|
+
advanceWorkspaceGenerationForDirectRequest,
|
|
27
|
+
advanceWorkspaceGenerationForRetainedProcess,
|
|
28
|
+
getSandbox,
|
|
29
|
+
markWarmLeaseInstanceLost,
|
|
30
|
+
readActiveSandbox,
|
|
31
|
+
retainWorkspaceMutationProcess,
|
|
32
|
+
settleRetainedProcess,
|
|
33
|
+
verifyDirectWorkspaceMutationSettlement,
|
|
34
|
+
verifyRetainedProcessMutationSettlement
|
|
35
|
+
} from "@opengeni/db";
|
|
36
|
+
import { appendAndPublishEvents } from "@opengeni/events";
|
|
37
|
+
import {
|
|
38
|
+
isProviderSandboxGoneDuringRoutedOperation,
|
|
26
39
|
makeActiveBackendResolver,
|
|
27
40
|
NatsControlRpc,
|
|
28
41
|
RoutingSandboxSession
|
|
@@ -63,6 +76,129 @@ function routingEnabled(settings) {
|
|
|
63
76
|
}
|
|
64
77
|
function wrapChannelABoxWithRouting(services, ids, established) {
|
|
65
78
|
const { db, settings, bus } = services;
|
|
79
|
+
const beforeMutation = async ({
|
|
80
|
+
op,
|
|
81
|
+
backend
|
|
82
|
+
}) => {
|
|
83
|
+
if (backend.sandboxId !== null || backend.leaseEpoch === void 0 || backend.providerInstanceId === void 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (backend.activeEpoch === void 0) {
|
|
87
|
+
throw new Error("API-direct workspace mutation resolved without an active route epoch");
|
|
88
|
+
}
|
|
89
|
+
return await advanceWorkspaceGenerationForDirectRequest(db, {
|
|
90
|
+
accountId: ids.accountId,
|
|
91
|
+
workspaceId: ids.workspaceId,
|
|
92
|
+
sessionId: ids.sessionId,
|
|
93
|
+
requestId: ids.directRequest.requestId,
|
|
94
|
+
holderId: ids.directRequest.holderId,
|
|
95
|
+
sandboxGroupId: ids.homeLease.sandboxGroupId,
|
|
96
|
+
expectedEpoch: backend.leaseEpoch,
|
|
97
|
+
expectedInstanceId: backend.providerInstanceId,
|
|
98
|
+
routeTargetId: backend.sandboxId,
|
|
99
|
+
routeEpoch: backend.activeEpoch,
|
|
100
|
+
operation: op
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
const afterMutation = async ({
|
|
104
|
+
op,
|
|
105
|
+
backend,
|
|
106
|
+
admission,
|
|
107
|
+
outcome,
|
|
108
|
+
retainedProcess
|
|
109
|
+
}) => {
|
|
110
|
+
if (admission === null) return;
|
|
111
|
+
if (!admission || typeof admission !== "object" || typeof admission.id !== "string" || typeof admission.workspaceGeneration !== "number" || backend.leaseEpoch === void 0 || backend.providerInstanceId === void 0 || backend.activeEpoch === void 0) {
|
|
112
|
+
throw new Error("API-direct workspace mutation settlement lacked its exact admission");
|
|
113
|
+
}
|
|
114
|
+
const exactAdmission = admission;
|
|
115
|
+
if (outcome === "resolved" && retainedProcess) {
|
|
116
|
+
await retainWorkspaceMutationProcess(db, {
|
|
117
|
+
accountId: ids.accountId,
|
|
118
|
+
workspaceId: ids.workspaceId,
|
|
119
|
+
sessionId: ids.sessionId,
|
|
120
|
+
processId: retainedProcess.id,
|
|
121
|
+
providerSessionId: retainedProcess.providerSessionId,
|
|
122
|
+
admissionId: exactAdmission.id,
|
|
123
|
+
admittedWorkspaceGeneration: exactAdmission.workspaceGeneration,
|
|
124
|
+
operation: op,
|
|
125
|
+
owner: {
|
|
126
|
+
kind: "direct",
|
|
127
|
+
requestId: ids.directRequest.requestId,
|
|
128
|
+
holderId: ids.directRequest.holderId,
|
|
129
|
+
sandboxGroupId: ids.homeLease.sandboxGroupId,
|
|
130
|
+
expectedEpoch: backend.leaseEpoch,
|
|
131
|
+
expectedInstanceId: backend.providerInstanceId,
|
|
132
|
+
routeTargetId: exactAdmission.routeTargetId,
|
|
133
|
+
routeEpoch: exactAdmission.routeEpoch
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
await verifyDirectWorkspaceMutationSettlement(db, {
|
|
139
|
+
accountId: ids.accountId,
|
|
140
|
+
workspaceId: ids.workspaceId,
|
|
141
|
+
sessionId: ids.sessionId,
|
|
142
|
+
requestId: ids.directRequest.requestId,
|
|
143
|
+
holderId: ids.directRequest.holderId,
|
|
144
|
+
sandboxGroupId: ids.homeLease.sandboxGroupId,
|
|
145
|
+
expectedEpoch: backend.leaseEpoch,
|
|
146
|
+
expectedInstanceId: backend.providerInstanceId,
|
|
147
|
+
routeTargetId: exactAdmission.routeTargetId,
|
|
148
|
+
routeEpoch: exactAdmission.routeEpoch,
|
|
149
|
+
admission: exactAdmission,
|
|
150
|
+
operation: op,
|
|
151
|
+
outcome
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
const beforeProcessMutation = async ({
|
|
155
|
+
op,
|
|
156
|
+
process
|
|
157
|
+
}) => await advanceWorkspaceGenerationForRetainedProcess(db, {
|
|
158
|
+
accountId: ids.accountId,
|
|
159
|
+
workspaceId: ids.workspaceId,
|
|
160
|
+
sessionId: ids.sessionId,
|
|
161
|
+
processId: process.id,
|
|
162
|
+
operation: op
|
|
163
|
+
});
|
|
164
|
+
const afterProcessMutation = async ({
|
|
165
|
+
op,
|
|
166
|
+
process,
|
|
167
|
+
admission,
|
|
168
|
+
outcome
|
|
169
|
+
}) => {
|
|
170
|
+
if (!admission || typeof admission !== "object" || typeof admission.id !== "string" || typeof admission.workspaceGeneration !== "number") {
|
|
171
|
+
throw new Error("API retained-process mutation settlement lacked its exact admission");
|
|
172
|
+
}
|
|
173
|
+
await verifyRetainedProcessMutationSettlement(db, {
|
|
174
|
+
accountId: ids.accountId,
|
|
175
|
+
workspaceId: ids.workspaceId,
|
|
176
|
+
sessionId: ids.sessionId,
|
|
177
|
+
processId: process.id,
|
|
178
|
+
admission,
|
|
179
|
+
operation: op,
|
|
180
|
+
outcome
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
const settleProcess = async ({
|
|
184
|
+
backend,
|
|
185
|
+
process,
|
|
186
|
+
proof
|
|
187
|
+
}) => {
|
|
188
|
+
if (backend.sandboxId !== null || backend.leaseEpoch === void 0 || backend.providerInstanceId === void 0) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
await settleRetainedProcess(db, {
|
|
192
|
+
accountId: ids.accountId,
|
|
193
|
+
workspaceId: ids.workspaceId,
|
|
194
|
+
sessionId: ids.sessionId,
|
|
195
|
+
processId: process.id,
|
|
196
|
+
outcome: proof.outcome,
|
|
197
|
+
exitCode: proof.exitCode,
|
|
198
|
+
reason: proof.reason,
|
|
199
|
+
idleGraceMs: settings.sandboxIdleGraceMs
|
|
200
|
+
});
|
|
201
|
+
};
|
|
66
202
|
const resolver = makeActiveBackendResolver({
|
|
67
203
|
workspaceId: ids.workspaceId,
|
|
68
204
|
defaultBackend: established.session,
|
|
@@ -77,14 +213,61 @@ function wrapChannelABoxWithRouting(services, ids, established) {
|
|
|
77
213
|
} : null;
|
|
78
214
|
},
|
|
79
215
|
controlRpcFactory: controlRpcFactory(bus),
|
|
80
|
-
relay: relayConfigFromSettings(settings)
|
|
216
|
+
relay: relayConfigFromSettings(settings),
|
|
217
|
+
resolveDefaultBackend: async () => ({
|
|
218
|
+
session: established.session,
|
|
219
|
+
sandboxId: null,
|
|
220
|
+
kind: established.backendId,
|
|
221
|
+
leaseEpoch: ids.homeLease.leaseEpoch,
|
|
222
|
+
providerInstanceId: ids.homeLease.instanceId
|
|
223
|
+
})
|
|
81
224
|
});
|
|
82
225
|
const proxy = new RoutingSandboxSession({
|
|
226
|
+
defaultResolved: {
|
|
227
|
+
session: established.session,
|
|
228
|
+
sandboxId: null,
|
|
229
|
+
kind: established.backendId,
|
|
230
|
+
leaseEpoch: ids.homeLease.leaseEpoch,
|
|
231
|
+
providerInstanceId: ids.homeLease.instanceId
|
|
232
|
+
},
|
|
83
233
|
readPointer: async () => {
|
|
234
|
+
if (!routingEnabled(settings)) {
|
|
235
|
+
return { activeSandboxId: null, activeEpoch: 0 };
|
|
236
|
+
}
|
|
84
237
|
const pointer = await readActiveSandbox(db, ids.workspaceId, ids.sessionId);
|
|
85
238
|
return pointer ?? { activeSandboxId: null, activeEpoch: 0 };
|
|
86
239
|
},
|
|
87
|
-
resolveActiveBackend: resolver
|
|
240
|
+
resolveActiveBackend: resolver,
|
|
241
|
+
beforeMutation,
|
|
242
|
+
afterMutation,
|
|
243
|
+
beforeProcessMutation,
|
|
244
|
+
afterProcessMutation,
|
|
245
|
+
settleProcess,
|
|
246
|
+
onDefaultBackendError: async ({ error }) => {
|
|
247
|
+
if (!isProviderSandboxGoneDuringRoutedOperation(ids.homeLease.backend, error)) return null;
|
|
248
|
+
const marked = await markWarmLeaseInstanceLost(db, {
|
|
249
|
+
accountId: ids.accountId,
|
|
250
|
+
workspaceId: ids.workspaceId,
|
|
251
|
+
sandboxGroupId: ids.homeLease.sandboxGroupId,
|
|
252
|
+
expectedEpoch: ids.homeLease.leaseEpoch,
|
|
253
|
+
expectedInstanceId: ids.homeLease.instanceId,
|
|
254
|
+
diagnostic: "provider_not_found_during_routed_operation"
|
|
255
|
+
});
|
|
256
|
+
if (marked.status === "marked" && bus) {
|
|
257
|
+
await appendAndPublishEvents(db, bus, ids.workspaceId, ids.sessionId, [
|
|
258
|
+
{
|
|
259
|
+
type: "sandbox.box.lost",
|
|
260
|
+
payload: { sandboxId: ids.homeLease.instanceId }
|
|
261
|
+
}
|
|
262
|
+
]).catch(() => void 0);
|
|
263
|
+
}
|
|
264
|
+
const lease = marked.lease;
|
|
265
|
+
const restore = lease?.recovery.restore.status;
|
|
266
|
+
return {
|
|
267
|
+
leaseEpoch: lease?.leaseEpoch ?? ids.homeLease.leaseEpoch,
|
|
268
|
+
recovery: marked.status === "stale" ? "superseded" : restore === "pending" ? "pending" : restore === "degraded" ? "degraded" : "unrecoverable"
|
|
269
|
+
};
|
|
270
|
+
}
|
|
88
271
|
});
|
|
89
272
|
return { ...established, session: proxy };
|
|
90
273
|
}
|
|
@@ -153,15 +336,33 @@ async function listFleet(services, ctx) {
|
|
|
153
336
|
};
|
|
154
337
|
const entries = [];
|
|
155
338
|
const groupActive = pointer.activeSandboxId === null;
|
|
339
|
+
const groupLease = await readLease(db, ctx.workspaceId, ctx.sessionGroupId);
|
|
340
|
+
const groupOnline = Boolean(
|
|
341
|
+
groupLease?.liveness === "warm" && groupLease.recovery.provider.status === "exists" && groupLease.recovery.workspace.status === "ready"
|
|
342
|
+
);
|
|
343
|
+
const groupRecovering = Boolean(
|
|
344
|
+
groupLease && (groupLease.liveness === "warming" || groupLease.recovery.restore.status === "pending" || groupLease.recovery.restore.status === "restoring" || groupLease.recovery.restore.status === "verifying")
|
|
345
|
+
);
|
|
156
346
|
entries.push({
|
|
157
347
|
id: ctx.sessionGroupId,
|
|
158
348
|
kind: ctx.sessionBackend === "selfhosted" ? "selfhosted" : "modal",
|
|
159
349
|
name: "session sandbox",
|
|
160
|
-
liveness: "online",
|
|
350
|
+
liveness: groupOnline ? "online" : groupRecovering ? "reconnecting" : "offline",
|
|
161
351
|
active: groupActive,
|
|
162
352
|
isSessionGroup: true,
|
|
163
353
|
enrollmentId: null,
|
|
164
|
-
attachable:
|
|
354
|
+
attachable: groupOnline,
|
|
355
|
+
providerStatus: groupLease?.recovery.provider.status ?? "not_created",
|
|
356
|
+
leaseLiveness: groupLease?.liveness ?? null,
|
|
357
|
+
routeStatus: groupActive ? "attached" : "detached",
|
|
358
|
+
archiveStatus: groupLease?.recovery.archive.status ?? "none",
|
|
359
|
+
restoreStatus: groupLease?.recovery.restore.status ?? "not_required",
|
|
360
|
+
workspaceStatus: groupLease?.recovery.workspace.status ?? "unknown",
|
|
361
|
+
leaseEpoch: groupLease?.leaseEpoch ?? null,
|
|
362
|
+
routeEpoch: pointer.activeEpoch,
|
|
363
|
+
workspaceGeneration: groupLease?.workspaceGeneration ?? null,
|
|
364
|
+
archiveGeneration: groupLease?.archiveGeneration ?? null,
|
|
365
|
+
archiveComplete: groupLease?.archiveComplete ?? false
|
|
165
366
|
});
|
|
166
367
|
const sandboxes = await listSandboxes(db, ctx.workspaceId);
|
|
167
368
|
for (const sandbox of sandboxes) {
|
|
@@ -181,7 +382,18 @@ async function listFleet(services, ctx) {
|
|
|
181
382
|
attachable: probe.liveness === "online",
|
|
182
383
|
consented: probe.consented,
|
|
183
384
|
hasDisplay: probe.hasDisplay,
|
|
184
|
-
lastSeenAt: enrollment?.lastSeenAt ?? null
|
|
385
|
+
lastSeenAt: enrollment?.lastSeenAt ?? null,
|
|
386
|
+
providerStatus: probe.liveness === "online" ? "exists" : probe.liveness === "reconnecting" ? "unknown" : "missing",
|
|
387
|
+
leaseLiveness: null,
|
|
388
|
+
routeStatus: pointer.activeSandboxId === sandbox.id ? "attached" : "detached",
|
|
389
|
+
archiveStatus: "none",
|
|
390
|
+
restoreStatus: "not_required",
|
|
391
|
+
workspaceStatus: probe.liveness === "online" ? "ready" : "not_ready",
|
|
392
|
+
leaseEpoch: null,
|
|
393
|
+
routeEpoch: pointer.activeEpoch,
|
|
394
|
+
workspaceGeneration: null,
|
|
395
|
+
archiveGeneration: null,
|
|
396
|
+
archiveComplete: false
|
|
185
397
|
});
|
|
186
398
|
}
|
|
187
399
|
return {
|
|
@@ -239,57 +451,75 @@ async function resolveTarget(services, ctx, target) {
|
|
|
239
451
|
async function swapActiveSandbox(services, ctx, target, workingDir) {
|
|
240
452
|
const resolved = await resolveTarget(services, ctx, target);
|
|
241
453
|
if (!resolved.ok) {
|
|
242
|
-
const
|
|
454
|
+
const pointer = await readActiveSandbox2(services.db, ctx.workspaceId, ctx.sessionId) ?? {
|
|
243
455
|
activeSandboxId: null,
|
|
244
456
|
activeEpoch: 0
|
|
245
457
|
};
|
|
246
458
|
return {
|
|
247
459
|
swapped: false,
|
|
248
|
-
activeSandboxId:
|
|
249
|
-
activeEpoch:
|
|
460
|
+
activeSandboxId: pointer.activeSandboxId,
|
|
461
|
+
activeEpoch: pointer.activeEpoch,
|
|
250
462
|
reason: resolved.reason,
|
|
251
463
|
code: resolved.code
|
|
252
464
|
};
|
|
253
465
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
466
|
+
let readinessHold;
|
|
467
|
+
if (resolved.targetSandboxId === null && services.ensureSessionGroupReady) {
|
|
468
|
+
try {
|
|
469
|
+
readinessHold = await services.ensureSessionGroupReady(ctx);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
const lease = await readLease(services.db, ctx.workspaceId, ctx.sessionGroupId);
|
|
472
|
+
const restore = lease?.recovery.restore.status;
|
|
473
|
+
const code = restore === "degraded" ? "recovery_degraded" : restore === "unrecoverable" ? "recovery_unrecoverable" : "recovery_in_progress";
|
|
474
|
+
const pointer = await readActiveSandbox2(services.db, ctx.workspaceId, ctx.sessionId) ?? {
|
|
475
|
+
activeSandboxId: null,
|
|
476
|
+
activeEpoch: 0
|
|
477
|
+
};
|
|
260
478
|
return {
|
|
261
|
-
swapped:
|
|
262
|
-
activeSandboxId:
|
|
263
|
-
activeEpoch:
|
|
479
|
+
swapped: false,
|
|
480
|
+
activeSandboxId: pointer.activeSandboxId,
|
|
481
|
+
activeEpoch: pointer.activeEpoch,
|
|
482
|
+
reason: error instanceof Error ? error.message : "session sandbox did not reach verified readiness",
|
|
483
|
+
code
|
|
264
484
|
};
|
|
265
485
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
...workingDir !== void 0 ? { workingDir } : {}
|
|
273
|
-
});
|
|
274
|
-
if (result.swapped && result.pointer) {
|
|
275
|
-
return {
|
|
276
|
-
swapped: true,
|
|
277
|
-
activeSandboxId: result.pointer.activeSandboxId,
|
|
278
|
-
activeEpoch: result.pointer.activeEpoch
|
|
486
|
+
}
|
|
487
|
+
try {
|
|
488
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
489
|
+
const pointer2 = await readActiveSandbox2(services.db, ctx.workspaceId, ctx.sessionId) ?? {
|
|
490
|
+
activeSandboxId: null,
|
|
491
|
+
activeEpoch: 0
|
|
279
492
|
};
|
|
493
|
+
const result = await setActiveSandbox(services.db, {
|
|
494
|
+
accountId: ctx.accountId,
|
|
495
|
+
workspaceId: ctx.workspaceId,
|
|
496
|
+
sessionId: ctx.sessionId,
|
|
497
|
+
targetSandboxId: resolved.targetSandboxId,
|
|
498
|
+
expectedEpoch: pointer2.activeEpoch,
|
|
499
|
+
...workingDir !== void 0 ? { workingDir } : {}
|
|
500
|
+
});
|
|
501
|
+
if (result.swapped && result.pointer) {
|
|
502
|
+
return {
|
|
503
|
+
swapped: true,
|
|
504
|
+
activeSandboxId: result.pointer.activeSandboxId,
|
|
505
|
+
activeEpoch: result.pointer.activeEpoch
|
|
506
|
+
};
|
|
507
|
+
}
|
|
280
508
|
}
|
|
509
|
+
const pointer = await readActiveSandbox2(services.db, ctx.workspaceId, ctx.sessionId) ?? {
|
|
510
|
+
activeSandboxId: null,
|
|
511
|
+
activeEpoch: 0
|
|
512
|
+
};
|
|
513
|
+
return {
|
|
514
|
+
swapped: false,
|
|
515
|
+
activeSandboxId: pointer.activeSandboxId,
|
|
516
|
+
activeEpoch: pointer.activeEpoch,
|
|
517
|
+
reason: "a concurrent swap won the epoch fence; re-read and retry",
|
|
518
|
+
code: "concurrent_swap"
|
|
519
|
+
};
|
|
520
|
+
} finally {
|
|
521
|
+
await readinessHold?.release().catch(() => void 0);
|
|
281
522
|
}
|
|
282
|
-
const pointer = await readActiveSandbox2(services.db, ctx.workspaceId, ctx.sessionId) ?? {
|
|
283
|
-
activeSandboxId: null,
|
|
284
|
-
activeEpoch: 0
|
|
285
|
-
};
|
|
286
|
-
return {
|
|
287
|
-
swapped: false,
|
|
288
|
-
activeSandboxId: pointer.activeSandboxId,
|
|
289
|
-
activeEpoch: pointer.activeEpoch,
|
|
290
|
-
reason: "a concurrent swap won the epoch fence; re-read and retry",
|
|
291
|
-
code: "concurrent_swap"
|
|
292
|
-
};
|
|
293
523
|
}
|
|
294
524
|
async function runOnSandbox(services, ctx, target, op) {
|
|
295
525
|
const sandbox = await getSandbox2(services.db, ctx.workspaceId, target);
|
|
@@ -3044,8 +3274,10 @@ function sessionWithEffectiveToolPolicy(session, workspaceServerIds, workspaceDe
|
|
|
3044
3274
|
import {
|
|
3045
3275
|
createScheduledTask,
|
|
3046
3276
|
deleteScheduledTask,
|
|
3277
|
+
getNestedAgentDepthDeploymentPolicy,
|
|
3047
3278
|
getRig as getRig3,
|
|
3048
3279
|
getScheduledTask,
|
|
3280
|
+
requireWorkspace as requireWorkspace2,
|
|
3049
3281
|
updateScheduledTask
|
|
3050
3282
|
} from "@opengeni/db";
|
|
3051
3283
|
import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
@@ -3061,6 +3293,7 @@ import {
|
|
|
3061
3293
|
import {
|
|
3062
3294
|
CreateSessionRequest,
|
|
3063
3295
|
DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
|
|
3296
|
+
SessionSpawnDenial,
|
|
3064
3297
|
ServiceTurnInitiator,
|
|
3065
3298
|
ServiceTurnInitiatorContext,
|
|
3066
3299
|
evaluateWorkspaceModelPolicy,
|
|
@@ -3069,7 +3302,7 @@ import {
|
|
|
3069
3302
|
} from "@opengeni/contracts";
|
|
3070
3303
|
import {
|
|
3071
3304
|
createSession,
|
|
3072
|
-
|
|
3305
|
+
createSessionWithIdempotencyKeyResult,
|
|
3073
3306
|
encryptVariableSetValue as encryptVariableSetValue2,
|
|
3074
3307
|
getAnySessionInGroup,
|
|
3075
3308
|
getEnrollment as getEnrollment2,
|
|
@@ -3080,7 +3313,7 @@ import {
|
|
|
3080
3313
|
getSandbox as getSandbox3,
|
|
3081
3314
|
getSession as getSession2,
|
|
3082
3315
|
SessionIdConflictError,
|
|
3083
|
-
|
|
3316
|
+
getSessionSpawnDenialByIdempotencyKey,
|
|
3084
3317
|
getSessionEvent,
|
|
3085
3318
|
getWorkspaceControlEvent,
|
|
3086
3319
|
getSessionLineage,
|
|
@@ -3096,10 +3329,11 @@ import {
|
|
|
3096
3329
|
withWorkspaceSubjectRls,
|
|
3097
3330
|
QueueCommandConflictError,
|
|
3098
3331
|
AgentCommandAuthorityError,
|
|
3332
|
+
SessionSpawnDeniedDbError,
|
|
3099
3333
|
SessionControlConflictError
|
|
3100
3334
|
} from "@opengeni/db";
|
|
3101
3335
|
import {
|
|
3102
|
-
appendAndPublishEvents,
|
|
3336
|
+
appendAndPublishEvents as appendAndPublishEvents2,
|
|
3103
3337
|
publishDurableSessionEvents,
|
|
3104
3338
|
publishDurableWorkspaceControlEvent
|
|
3105
3339
|
} from "@opengeni/events";
|
|
@@ -3108,6 +3342,29 @@ var reservedSessionMcpServerIds = /* @__PURE__ */ new Set(["opengeni", "files",
|
|
|
3108
3342
|
var maxSessionMcpCredentialHeaders = 16;
|
|
3109
3343
|
var maxSessionMcpCredentialHeaderValueLength = 4096;
|
|
3110
3344
|
var sessionMcpCredentialHeaderName = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
|
|
3345
|
+
var SessionSpawnDeniedError = class extends Error {
|
|
3346
|
+
denial;
|
|
3347
|
+
constructor(denial) {
|
|
3348
|
+
super(sessionSpawnDeniedMessage(denial));
|
|
3349
|
+
this.name = "SessionSpawnDeniedError";
|
|
3350
|
+
this.denial = denial;
|
|
3351
|
+
}
|
|
3352
|
+
};
|
|
3353
|
+
function sessionSpawnDeniedMessage(denial) {
|
|
3354
|
+
if (denial.code === "nested_agent_depth_override_forbidden") {
|
|
3355
|
+
return `requested nested-agent depth limit ${denial.requestedMaxNestedAgentDepthOverride ?? "unknown"} exceeds inherited limit ${denial.effectiveMaxNestedAgentDepth}; workspace:admin is required to increase it`;
|
|
3356
|
+
}
|
|
3357
|
+
return `nested-agent depth ${denial.attemptedDepth} exceeds effective limit ${denial.effectiveMaxNestedAgentDepth} (current parent depth ${denial.currentDepth})`;
|
|
3358
|
+
}
|
|
3359
|
+
function sessionSpawnDenialEnvelope(error) {
|
|
3360
|
+
return {
|
|
3361
|
+
error: {
|
|
3362
|
+
code: error.denial.code,
|
|
3363
|
+
message: error.message,
|
|
3364
|
+
details: { denial: error.denial }
|
|
3365
|
+
}
|
|
3366
|
+
};
|
|
3367
|
+
}
|
|
3111
3368
|
function serviceInitiatorForGrant(grant) {
|
|
3112
3369
|
if (!grant.serviceInitiator) {
|
|
3113
3370
|
if (grant.serviceInitiatorContext) {
|
|
@@ -3377,21 +3634,7 @@ async function createAndStartSession(input) {
|
|
|
3377
3634
|
reasoningEffort: input.reasoningEffort
|
|
3378
3635
|
};
|
|
3379
3636
|
if (input.createIdempotencyKey) {
|
|
3380
|
-
const
|
|
3381
|
-
input.db,
|
|
3382
|
-
input.workspaceId,
|
|
3383
|
-
input.createIdempotencyKey
|
|
3384
|
-
);
|
|
3385
|
-
if (existing) {
|
|
3386
|
-
if (input.requestedSessionId && existing.id !== input.requestedSessionId) {
|
|
3387
|
-
throw new SessionIdConflictError(input.requestedSessionId);
|
|
3388
|
-
}
|
|
3389
|
-
return await finishStartSession(
|
|
3390
|
-
existing.temporalWorkflowId ? { ...input, seedTargetSandbox: null } : input,
|
|
3391
|
-
existing
|
|
3392
|
-
);
|
|
3393
|
-
}
|
|
3394
|
-
const { session: keyed, created } = await createSessionWithIdempotencyKey(input.db, {
|
|
3637
|
+
const keyedResult = await createSessionWithIdempotencyKeyResult(input.db, {
|
|
3395
3638
|
...input.requestedSessionId ? { requestedSessionId: input.requestedSessionId } : {},
|
|
3396
3639
|
accountId: input.accountId,
|
|
3397
3640
|
workspaceId: input.workspaceId,
|
|
@@ -3415,8 +3658,15 @@ async function createAndStartSession(input) {
|
|
|
3415
3658
|
createIdempotencyKey: input.createIdempotencyKey,
|
|
3416
3659
|
sandboxGroupId: input.sandboxGroupId ?? null,
|
|
3417
3660
|
...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {},
|
|
3418
|
-
mcpServers: input.mcpServers ?? []
|
|
3661
|
+
mcpServers: input.mcpServers ?? [],
|
|
3662
|
+
maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
|
|
3663
|
+
allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
|
|
3664
|
+
subjectId: input.subjectId ?? null
|
|
3419
3665
|
});
|
|
3666
|
+
if (keyedResult.denied) {
|
|
3667
|
+
throw new SessionSpawnDeniedError(SessionSpawnDenial.parse(keyedResult.denial));
|
|
3668
|
+
}
|
|
3669
|
+
const { session: keyed, created } = keyedResult;
|
|
3420
3670
|
if (!created) {
|
|
3421
3671
|
return await finishStartSession(
|
|
3422
3672
|
keyed.temporalWorkflowId ? { ...input, seedTargetSandbox: null } : input,
|
|
@@ -3425,31 +3675,42 @@ async function createAndStartSession(input) {
|
|
|
3425
3675
|
}
|
|
3426
3676
|
return await finishStartSession(input, keyed);
|
|
3427
3677
|
}
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3678
|
+
let session;
|
|
3679
|
+
try {
|
|
3680
|
+
session = await createSession(input.db, {
|
|
3681
|
+
...input.requestedSessionId ? { requestedSessionId: input.requestedSessionId } : {},
|
|
3682
|
+
accountId: input.accountId,
|
|
3683
|
+
workspaceId: input.workspaceId,
|
|
3684
|
+
initialMessage: input.initialMessage,
|
|
3685
|
+
initialTurnInstructions: input.turnInstructions ?? null,
|
|
3686
|
+
resources: input.resources,
|
|
3687
|
+
tools: input.tools,
|
|
3688
|
+
...input.toolPolicy ? { toolPolicy: input.toolPolicy } : {},
|
|
3689
|
+
metadata: sessionMetadata,
|
|
3690
|
+
...input.createdBy ? { createdBy: input.createdBy } : {},
|
|
3691
|
+
...input.createdByContext ? { createdByContext: input.createdByContext } : {},
|
|
3692
|
+
createdByActor: input.createdByActor ?? null,
|
|
3693
|
+
model: input.model,
|
|
3694
|
+
sandboxBackend: input.sandboxBackend,
|
|
3695
|
+
variableSetId: input.variableSet?.id ?? null,
|
|
3696
|
+
rigId: input.rigId ?? null,
|
|
3697
|
+
rigVersionId: input.rigVersionId ?? null,
|
|
3698
|
+
firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
|
|
3699
|
+
instructions: input.instructions ?? null,
|
|
3700
|
+
parentSessionId: input.parentSessionId ?? null,
|
|
3701
|
+
sandboxGroupId: input.sandboxGroupId ?? null,
|
|
3702
|
+
...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {},
|
|
3703
|
+
mcpServers: input.mcpServers ?? [],
|
|
3704
|
+
maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
|
|
3705
|
+
allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
|
|
3706
|
+
subjectId: input.subjectId ?? null
|
|
3707
|
+
});
|
|
3708
|
+
} catch (error) {
|
|
3709
|
+
if (error instanceof SessionSpawnDeniedDbError) {
|
|
3710
|
+
throw new SessionSpawnDeniedError(SessionSpawnDenial.parse(error.denial));
|
|
3711
|
+
}
|
|
3712
|
+
throw error;
|
|
3713
|
+
}
|
|
3453
3714
|
return await finishStartSession(input, session);
|
|
3454
3715
|
}
|
|
3455
3716
|
async function finishStartSession(input, session) {
|
|
@@ -3496,7 +3757,8 @@ async function finishStartSession(input, session) {
|
|
|
3496
3757
|
text: input.goal.text,
|
|
3497
3758
|
...input.goal.successCriteria !== void 0 ? { successCriteria: input.goal.successCriteria } : {},
|
|
3498
3759
|
...input.goal.maxAutoContinuations !== void 0 ? { maxAutoContinuations: input.goal.maxAutoContinuations } : {}
|
|
3499
|
-
} : null
|
|
3760
|
+
} : null,
|
|
3761
|
+
consumeNewSessionDraft: input.consumeNewSessionDraft ?? null
|
|
3500
3762
|
});
|
|
3501
3763
|
await publishDurableSessionEvents(input.bus, session.workspaceId, session.id, started.events);
|
|
3502
3764
|
if (started.workflowWakeRevision !== null) {
|
|
@@ -3672,6 +3934,16 @@ async function postUserMessageTurn(input) {
|
|
|
3672
3934
|
async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
3673
3935
|
const { settings, db, bus, workflowClient, objectStorage } = deps;
|
|
3674
3936
|
const payload = CreateSessionRequest.parse(rawPayload);
|
|
3937
|
+
if (payload.idempotencyKey) {
|
|
3938
|
+
const denial = await getSessionSpawnDenialByIdempotencyKey(
|
|
3939
|
+
db,
|
|
3940
|
+
workspaceId,
|
|
3941
|
+
payload.idempotencyKey
|
|
3942
|
+
);
|
|
3943
|
+
if (denial) {
|
|
3944
|
+
throw new SessionSpawnDeniedError(SessionSpawnDenial.parse(denial));
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3675
3947
|
const parentSessionId = typeof grant.metadata?.["sessionId"] === "string" ? grant.metadata["sessionId"] : null;
|
|
3676
3948
|
if (parentSessionId) {
|
|
3677
3949
|
await requireSessionAuthorization(deps, grant, {
|
|
@@ -3962,11 +4234,18 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
|
3962
4234
|
sessionMcpServers: sessionMcpServers.metadata,
|
|
3963
4235
|
parentSessionId,
|
|
3964
4236
|
createIdempotencyKey: payload.idempotencyKey ?? null,
|
|
4237
|
+
maxNestedAgentDepthOverride: payload.maxNestedAgentDepth ?? null,
|
|
4238
|
+
allowNestedAgentDepthIncrease: hasPermission(grant.permissions, "workspace:admin"),
|
|
4239
|
+
subjectId: grant.subjectId,
|
|
3965
4240
|
// Create-time machine targeting (A-2a): when a target sandbox is named, the
|
|
3966
4241
|
// active-sandbox pointer is seeded race-free inside createAndStartSession
|
|
3967
4242
|
// (after the row exists, before the first turn dispatches). Validation
|
|
3968
4243
|
// (ownership/liveness) lives in swapActiveSandbox; an invalid target 422s.
|
|
3969
|
-
seedTargetSandbox: payload.targetSandboxId ? { sandboxId: payload.targetSandboxId, settings, workingDir: payload.workingDir ?? null } : null
|
|
4244
|
+
seedTargetSandbox: payload.targetSandboxId ? { sandboxId: payload.targetSandboxId, settings, workingDir: payload.workingDir ?? null } : null,
|
|
4245
|
+
consumeNewSessionDraft: payload.expectedNewSessionDraftRevision !== void 0 ? {
|
|
4246
|
+
subjectId: grant.subjectId,
|
|
4247
|
+
expectedRevision: payload.expectedNewSessionDraftRevision
|
|
4248
|
+
} : null
|
|
3970
4249
|
});
|
|
3971
4250
|
} catch (error) {
|
|
3972
4251
|
if (error instanceof AgentCommandAuthorityError) {
|
|
@@ -4136,7 +4415,7 @@ async function updateSessionTitle(deps, grant, sessionId, title, source) {
|
|
|
4136
4415
|
const workspaceId = grant.workspaceId;
|
|
4137
4416
|
const result = await updateSessionTitleRow(db, { workspaceId, sessionId, title, source });
|
|
4138
4417
|
if (result.updated) {
|
|
4139
|
-
await
|
|
4418
|
+
await appendAndPublishEvents2(db, bus, workspaceId, sessionId, [
|
|
4140
4419
|
{
|
|
4141
4420
|
type: "session.title_set",
|
|
4142
4421
|
payload: {
|
|
@@ -4333,6 +4612,7 @@ async function validatedScheduledTaskUpdate(input) {
|
|
|
4333
4612
|
settings: input.settings,
|
|
4334
4613
|
db: input.db,
|
|
4335
4614
|
objectStorage: input.objectStorage,
|
|
4615
|
+
grant: input.grant,
|
|
4336
4616
|
workspaceId: input.existing.workspaceId,
|
|
4337
4617
|
payload: { agentConfig: input.payload.agentConfig },
|
|
4338
4618
|
...input.toolsProvided !== void 0 ? { toolsProvided: input.toolsProvided } : {}
|
|
@@ -4415,6 +4695,18 @@ async function validateScheduledTaskAgentConfig(input) {
|
|
|
4415
4695
|
throw new HTTPException10(503, { message: "object storage is not configured" });
|
|
4416
4696
|
}
|
|
4417
4697
|
await validateFileResources(input.db, input.workspaceId, resources);
|
|
4698
|
+
const requestedMaxDepth = input.payload.agentConfig.maxNestedAgentDepth;
|
|
4699
|
+
if (requestedMaxDepth !== void 0) {
|
|
4700
|
+
const workspace = await requireWorkspace2(input.db, input.workspaceId);
|
|
4701
|
+
const workspaceMaxDepth = workspace.settings.maxNestedAgentDepth;
|
|
4702
|
+
const deploymentPolicy = await getNestedAgentDepthDeploymentPolicy(input.db);
|
|
4703
|
+
const inheritedMaxDepth = typeof workspaceMaxDepth === "number" ? workspaceMaxDepth : deploymentPolicy.maxNestedAgentDepth;
|
|
4704
|
+
if (requestedMaxDepth > inheritedMaxDepth && !hasPermission(input.grant.permissions, "workspace:admin")) {
|
|
4705
|
+
throw new HTTPException10(403, {
|
|
4706
|
+
message: `scheduled task maxNestedAgentDepth ${requestedMaxDepth} exceeds inherited limit ${inheritedMaxDepth}; workspace:admin is required to increase it`
|
|
4707
|
+
});
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4418
4710
|
return {
|
|
4419
4711
|
...input.payload.agentConfig,
|
|
4420
4712
|
...model === void 0 || model === null ? {} : { model },
|
|
@@ -4485,6 +4777,102 @@ function assertWorkspaceDeletable(input) {
|
|
|
4485
4777
|
}
|
|
4486
4778
|
}
|
|
4487
4779
|
|
|
4780
|
+
// src/application/new-session-drafts.ts
|
|
4781
|
+
import {
|
|
4782
|
+
NewSessionDraft,
|
|
4783
|
+
SaveNewSessionDraftRequest
|
|
4784
|
+
} from "@opengeni/contracts";
|
|
4785
|
+
import {
|
|
4786
|
+
getNewSessionDraftInTransaction,
|
|
4787
|
+
NewSessionDraftAccessError,
|
|
4788
|
+
saveNewSessionDraftInTransaction,
|
|
4789
|
+
withWorkspaceSubjectRls as withWorkspaceSubjectRls2
|
|
4790
|
+
} from "@opengeni/db";
|
|
4791
|
+
import { HTTPException as HTTPException12 } from "hono/http-exception";
|
|
4792
|
+
function mapNewSessionDraft(row) {
|
|
4793
|
+
if (!row) return null;
|
|
4794
|
+
return NewSessionDraft.parse({
|
|
4795
|
+
revision: row.revision,
|
|
4796
|
+
text: row.text,
|
|
4797
|
+
resources: row.resources,
|
|
4798
|
+
tools: row.tools,
|
|
4799
|
+
model: row.model,
|
|
4800
|
+
reasoningEffort: row.reasoningEffort,
|
|
4801
|
+
options: row.sessionOptions,
|
|
4802
|
+
updatedAt: row.updatedAt.toISOString()
|
|
4803
|
+
});
|
|
4804
|
+
}
|
|
4805
|
+
async function getActorNewSessionDraft(deps, grant, workspaceId) {
|
|
4806
|
+
const row = await withWorkspaceSubjectRls2(
|
|
4807
|
+
deps.db,
|
|
4808
|
+
workspaceId,
|
|
4809
|
+
grant.subjectId,
|
|
4810
|
+
(scoped) => getNewSessionDraftInTransaction(scoped, {
|
|
4811
|
+
workspaceId,
|
|
4812
|
+
subjectId: grant.subjectId
|
|
4813
|
+
})
|
|
4814
|
+
);
|
|
4815
|
+
return mapNewSessionDraft(row) ?? {
|
|
4816
|
+
revision: 0,
|
|
4817
|
+
text: "",
|
|
4818
|
+
resources: [],
|
|
4819
|
+
tools: [],
|
|
4820
|
+
model: deps.settings.openaiModel,
|
|
4821
|
+
reasoningEffort: deps.settings.openaiReasoningEffort,
|
|
4822
|
+
options: {},
|
|
4823
|
+
updatedAt: null
|
|
4824
|
+
};
|
|
4825
|
+
}
|
|
4826
|
+
async function saveActorNewSessionDraft(deps, grant, workspaceId, rawInput) {
|
|
4827
|
+
const input = SaveNewSessionDraftRequest.parse(rawInput);
|
|
4828
|
+
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
|
|
4829
|
+
deps.db,
|
|
4830
|
+
workspaceId,
|
|
4831
|
+
deps.settings
|
|
4832
|
+
);
|
|
4833
|
+
const resources = normalizeResources(input.resources);
|
|
4834
|
+
const tools = validateToolRefs(input.tools, runtimeSettings);
|
|
4835
|
+
await validateGitHubRepositorySelection(deps.db, workspaceId, resources);
|
|
4836
|
+
if (resources.some((resource) => resource.kind === "file") && !deps.objectStorage) {
|
|
4837
|
+
throw new HTTPException12(503, { message: "object storage is not configured" });
|
|
4838
|
+
}
|
|
4839
|
+
await validateFileResources(deps.db, workspaceId, resources);
|
|
4840
|
+
assertConfiguredModel(deps.settings, input.model);
|
|
4841
|
+
await assertWorkspaceModelPolicyAllows(deps.db, deps.settings, workspaceId, input.model);
|
|
4842
|
+
try {
|
|
4843
|
+
const saved = await withWorkspaceSubjectRls2(
|
|
4844
|
+
deps.db,
|
|
4845
|
+
workspaceId,
|
|
4846
|
+
grant.subjectId,
|
|
4847
|
+
(scoped) => scoped.transaction(
|
|
4848
|
+
(tx) => saveNewSessionDraftInTransaction(tx, {
|
|
4849
|
+
accountId: grant.accountId,
|
|
4850
|
+
workspaceId,
|
|
4851
|
+
subjectId: grant.subjectId,
|
|
4852
|
+
expectedRevision: input.expectedRevision,
|
|
4853
|
+
text: input.text,
|
|
4854
|
+
resources,
|
|
4855
|
+
tools,
|
|
4856
|
+
model: input.model,
|
|
4857
|
+
reasoningEffort: input.reasoningEffort,
|
|
4858
|
+
options: input.options,
|
|
4859
|
+
// Only managed people are removed through removeWorkspaceMember().
|
|
4860
|
+
// API keys and delegated service actors (for example the first-party
|
|
4861
|
+
// worker MCP principal) legitimately have no workspace_memberships
|
|
4862
|
+
// row, so they must not be rejected by the human-removal fence.
|
|
4863
|
+
requireWorkspaceMembership: grant.subjectId.startsWith("user:")
|
|
4864
|
+
})
|
|
4865
|
+
)
|
|
4866
|
+
);
|
|
4867
|
+
return mapNewSessionDraft(saved);
|
|
4868
|
+
} catch (error) {
|
|
4869
|
+
if (error instanceof NewSessionDraftAccessError) {
|
|
4870
|
+
throw new HTTPException12(403, { message: error.message });
|
|
4871
|
+
}
|
|
4872
|
+
throw error;
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4875
|
+
|
|
4488
4876
|
// src/application/session-commands.ts
|
|
4489
4877
|
import { reasoningEffortForMetadata as reasoningEffortForMetadata2 } from "@opengeni/contracts";
|
|
4490
4878
|
import {
|
|
@@ -4506,7 +4894,7 @@ import {
|
|
|
4506
4894
|
steerAgentSessionInTransaction,
|
|
4507
4895
|
steerQueuedTurnInTransaction,
|
|
4508
4896
|
withWorkspaceRls,
|
|
4509
|
-
withWorkspaceSubjectRls as
|
|
4897
|
+
withWorkspaceSubjectRls as withWorkspaceSubjectRls3
|
|
4510
4898
|
} from "@opengeni/db";
|
|
4511
4899
|
import {
|
|
4512
4900
|
publishDurableSessionEvents as publishDurableSessionEvents2,
|
|
@@ -4798,7 +5186,7 @@ async function deleteHumanQueuePrompt(deps, context, turnId, input) {
|
|
|
4798
5186
|
}
|
|
4799
5187
|
async function editHumanQueuePrompt(deps, context, turnId, input) {
|
|
4800
5188
|
const authorization = await authorizeHumanSessionCommand(deps, context, "session.queue.control");
|
|
4801
|
-
const result = await
|
|
5189
|
+
const result = await withWorkspaceSubjectRls3(
|
|
4802
5190
|
deps.db,
|
|
4803
5191
|
context.workspaceId,
|
|
4804
5192
|
context.subjectId,
|
|
@@ -4920,7 +5308,7 @@ async function controlHumanWorkspace(deps, context, input) {
|
|
|
4920
5308
|
}
|
|
4921
5309
|
async function getHumanComposerDraft(deps, context) {
|
|
4922
5310
|
await authorizeHumanSessionCommand(deps, context, "session.composer.read");
|
|
4923
|
-
const row = await
|
|
5311
|
+
const row = await withWorkspaceSubjectRls3(
|
|
4924
5312
|
deps.db,
|
|
4925
5313
|
context.workspaceId,
|
|
4926
5314
|
context.subjectId,
|
|
@@ -4949,7 +5337,7 @@ async function getHumanComposerDraft(deps, context) {
|
|
|
4949
5337
|
}
|
|
4950
5338
|
async function saveHumanComposerDraft(deps, context, input) {
|
|
4951
5339
|
await authorizeHumanSessionCommand(deps, context, "session.composer.write");
|
|
4952
|
-
const row = await
|
|
5340
|
+
const row = await withWorkspaceSubjectRls3(
|
|
4953
5341
|
deps.db,
|
|
4954
5342
|
context.workspaceId,
|
|
4955
5343
|
context.subjectId,
|
|
@@ -4977,6 +5365,7 @@ export {
|
|
|
4977
5365
|
SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE,
|
|
4978
5366
|
SessionAuthorizationDeniedError,
|
|
4979
5367
|
SessionAuthorizationUnavailableError,
|
|
5368
|
+
SessionSpawnDeniedError,
|
|
4980
5369
|
acceptSessionUserMessage,
|
|
4981
5370
|
activateRigVersionForApi,
|
|
4982
5371
|
appendRigSetupCommand,
|
|
@@ -5012,6 +5401,7 @@ export {
|
|
|
5012
5401
|
editHumanQueuePrompt,
|
|
5013
5402
|
enableCapability,
|
|
5014
5403
|
enabledCapabilityMcpToolRefs,
|
|
5404
|
+
getActorNewSessionDraft,
|
|
5015
5405
|
getCapabilityPack,
|
|
5016
5406
|
getHumanComposerDraft,
|
|
5017
5407
|
hasPermission,
|
|
@@ -5062,11 +5452,13 @@ export {
|
|
|
5062
5452
|
rigActorForGrant,
|
|
5063
5453
|
routingEnabled,
|
|
5064
5454
|
runOnSandbox,
|
|
5455
|
+
saveActorNewSessionDraft,
|
|
5065
5456
|
saveHumanComposerDraft,
|
|
5066
5457
|
scheduledTaskTemporalScheduleId,
|
|
5067
5458
|
scheduledTaskToolsProvided,
|
|
5068
5459
|
scheduledTaskTriggerToken,
|
|
5069
5460
|
sendAgentSessionMessage,
|
|
5461
|
+
sessionSpawnDenialEnvelope,
|
|
5070
5462
|
sessionWithEffectiveToolPolicy,
|
|
5071
5463
|
settingsWithEnabledCapabilityMcpServers,
|
|
5072
5464
|
settingsWithMcpCapabilityServers,
|