@opengeni/core 2.6.3-canary.0 → 2.6.4-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/index.js CHANGED
@@ -208,6 +208,367 @@ function filenameForMimeType(mimeType) {
208
208
  }
209
209
  }
210
210
 
211
+ // src/model-catalog.ts
212
+ import {
213
+ applyModelCatalogDocument,
214
+ configuredGatewayWorkspaceProductModelIds,
215
+ configuredModels,
216
+ configuredModelNotes,
217
+ configuredOpenRouterWorkspaceProductModelIds,
218
+ configuredProviders,
219
+ validateModelCatalogSettings,
220
+ withCodexCatalogProvider,
221
+ withWorkspaceGatewayCatalogProvider,
222
+ withWorkspaceOpenRouterCatalogProvider,
223
+ withXaiSubscriptionCatalogProvider,
224
+ WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
225
+ WORKSPACE_OPENROUTER_MODEL_ID_PREFIX,
226
+ WORKSPACE_OPENROUTER_PROVIDER_ID
227
+ } from "@opengeni/config";
228
+ import {
229
+ evaluateWorkspaceModelPolicy
230
+ } from "@opengeni/contracts";
231
+ import {
232
+ getDeploymentModelCatalog,
233
+ getWorkspaceGatewayCustomModelForExecution,
234
+ getWorkspaceOpenRouterCustomModelForExecution,
235
+ listWorkspaceGatewayCustomModels,
236
+ listWorkspaceOpenRouterCustomModels
237
+ } from "@opengeni/db";
238
+ function isWorkspaceGatewayCustomModelId(settings, modelId) {
239
+ return modelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX) && !configuredGatewayWorkspaceProductModelIds(settings).includes(modelId);
240
+ }
241
+ function isWorkspaceOpenRouterCustomModelId(settings, modelId) {
242
+ return modelId.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX) && !configuredOpenRouterWorkspaceProductModelIds(settings).includes(modelId);
243
+ }
244
+ function workspaceCustomModelReference(settings, modelId) {
245
+ if (isWorkspaceGatewayCustomModelId(settings, modelId)) {
246
+ return {
247
+ providerKind: "vercel_gateway",
248
+ upstreamModelId: modelId.slice(WORKSPACE_GATEWAY_MODEL_ID_PREFIX.length)
249
+ };
250
+ }
251
+ if (isWorkspaceOpenRouterCustomModelId(settings, modelId)) {
252
+ return {
253
+ providerKind: "openrouter",
254
+ upstreamModelId: modelId.slice(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX.length)
255
+ };
256
+ }
257
+ return null;
258
+ }
259
+ function isWorkspaceCustomModelId(settings, modelId) {
260
+ return workspaceCustomModelReference(settings, modelId) !== null;
261
+ }
262
+ async function resolveCatalogSettings(db, envSettings) {
263
+ if (envSettings.modelCatalogSource === "code") {
264
+ validateModelCatalogSettings(envSettings);
265
+ return {
266
+ settings: envSettings,
267
+ source: "code",
268
+ version: null,
269
+ modelNotes: configuredModelNotes(envSettings)
270
+ };
271
+ }
272
+ const row = await getDeploymentModelCatalog(db);
273
+ if (!row) {
274
+ throw new Error("database model catalog source is configured but the singleton row is missing");
275
+ }
276
+ const settings = applyModelCatalogDocument(envSettings, row.document);
277
+ validateModelCatalogSettings(settings);
278
+ return {
279
+ settings,
280
+ source: "database",
281
+ version: row.version,
282
+ modelNotes: configuredModelNotes(settings)
283
+ };
284
+ }
285
+ async function resolveWorkspaceCatalogSettings(db, envSettings, input) {
286
+ const retainedProductModelIds = [
287
+ ...input.retainedProductModelIds ?? [],
288
+ input.retainedProductModelId
289
+ ];
290
+ const retainedGatewayUpstreamModelIds = retainedProductModelIds.flatMap(
291
+ (productModelId) => productModelId?.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX) ? [productModelId.slice(WORKSPACE_GATEWAY_MODEL_ID_PREFIX.length)] : []
292
+ );
293
+ const retainedOpenRouterUpstreamModelIds = retainedProductModelIds.flatMap(
294
+ (productModelId) => productModelId?.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX) ? [productModelId.slice(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX.length)] : []
295
+ );
296
+ const [
297
+ resolved,
298
+ activeGatewayCustomModels,
299
+ activeOpenRouterCustomModels,
300
+ retainedGatewayCustomModels,
301
+ retainedOpenRouterCustomModels
302
+ ] = await Promise.all([
303
+ resolveCatalogSettings(db, envSettings),
304
+ listWorkspaceGatewayCustomModels(db, {
305
+ accountId: input.accountId,
306
+ workspaceId: input.workspaceId
307
+ }),
308
+ listWorkspaceOpenRouterCustomModels(db, {
309
+ accountId: input.accountId,
310
+ workspaceId: input.workspaceId
311
+ }),
312
+ Promise.all(
313
+ [...new Set(retainedGatewayUpstreamModelIds)].map(
314
+ async (upstreamModelId) => await getWorkspaceGatewayCustomModelForExecution(db, {
315
+ accountId: input.accountId,
316
+ workspaceId: input.workspaceId,
317
+ upstreamModelId
318
+ })
319
+ )
320
+ ),
321
+ Promise.all(
322
+ [...new Set(retainedOpenRouterUpstreamModelIds)].map(
323
+ async (upstreamModelId) => await getWorkspaceOpenRouterCustomModelForExecution(db, {
324
+ accountId: input.accountId,
325
+ workspaceId: input.workspaceId,
326
+ upstreamModelId
327
+ })
328
+ )
329
+ )
330
+ ]);
331
+ const includeRetainedModels = (activeModels, retainedModels) => {
332
+ const customModels = [...activeModels];
333
+ const includedUpstreamModelIds = new Set(activeModels.map((model) => model.upstreamModelId));
334
+ for (const retainedCustomModel of retainedModels) {
335
+ if (retainedCustomModel && !includedUpstreamModelIds.has(retainedCustomModel.upstreamModelId)) {
336
+ customModels.push(retainedCustomModel);
337
+ includedUpstreamModelIds.add(retainedCustomModel.upstreamModelId);
338
+ }
339
+ }
340
+ return customModels;
341
+ };
342
+ const gatewayCustomModels = includeRetainedModels(
343
+ activeGatewayCustomModels,
344
+ retainedGatewayCustomModels
345
+ );
346
+ const openRouterCustomModels = includeRetainedModels(
347
+ activeOpenRouterCustomModels,
348
+ retainedOpenRouterCustomModels
349
+ );
350
+ return {
351
+ ...resolved,
352
+ settings: withWorkspaceOpenRouterCatalogProvider(
353
+ withWorkspaceGatewayCatalogProvider(resolved.settings, gatewayCustomModels),
354
+ openRouterCustomModels
355
+ )
356
+ };
357
+ }
358
+ var MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS = 5 * 6e4;
359
+ function modelDefinitionRunnable(model) {
360
+ return model.capabilities.inputModalities.includes("text") && model.capabilities.outputModalities.includes("text") && model.capabilities.transports.sse.runnable;
361
+ }
362
+ function observedCredentialReadiness(input) {
363
+ if (!input.observation) {
364
+ return {
365
+ status: "not_ready",
366
+ reason: "prerequisites_missing",
367
+ basis: input.basis,
368
+ checkedAt: null
369
+ };
370
+ }
371
+ const checkedAtMs = Date.parse(input.observation.checkedAt);
372
+ if (!Number.isFinite(checkedAtMs)) {
373
+ return {
374
+ status: "error",
375
+ reason: "resolver_error",
376
+ basis: input.basis,
377
+ checkedAt: null
378
+ };
379
+ }
380
+ const checkedAt = new Date(checkedAtMs).toISOString();
381
+ if (Math.abs(input.nowMs - checkedAtMs) > input.maxAgeMs) {
382
+ return {
383
+ status: "not_ready",
384
+ reason: "observation_stale",
385
+ basis: input.basis,
386
+ checkedAt
387
+ };
388
+ }
389
+ if (input.observation.status === "ready") {
390
+ return { status: "ready", reason: null, basis: input.basis, checkedAt };
391
+ }
392
+ if (input.observation.status === "not_ready") {
393
+ return {
394
+ status: "not_ready",
395
+ reason: input.observation.reason === "needs_reauth" ? "needs_reauth" : "prerequisites_missing",
396
+ basis: input.basis,
397
+ checkedAt
398
+ };
399
+ }
400
+ return {
401
+ status: "error",
402
+ reason: "resolver_error",
403
+ basis: input.basis,
404
+ checkedAt
405
+ };
406
+ }
407
+ function credentialReadinessFor(input) {
408
+ const source = input.model.credentialSource;
409
+ if (source.kind === "connected_subscription") {
410
+ const active = source.provider === "xai" ? input.xaiSubscriptionActive : input.codexSubscriptionActive;
411
+ return active ? { status: "ready", reason: null, basis: "connection", checkedAt: null } : {
412
+ status: "not_ready",
413
+ reason: "needs_reauth",
414
+ basis: "connection",
415
+ checkedAt: null
416
+ };
417
+ }
418
+ if (source.kind === "workspace_connection") {
419
+ const connectionActive = input.model.providerId === WORKSPACE_OPENROUTER_PROVIDER_ID ? input.workspaceOpenRouterConnectionActive : input.workspaceGatewayConnectionActive;
420
+ return connectionActive ? { status: "ready", reason: null, basis: "connection", checkedAt: null } : {
421
+ status: "not_ready",
422
+ reason: "needs_reauth",
423
+ basis: "connection",
424
+ checkedAt: null
425
+ };
426
+ }
427
+ if (source.kind === "deployment" && source.mechanism === "none") {
428
+ return { status: "ready", reason: null, basis: "configuration", checkedAt: null };
429
+ }
430
+ if (source.kind === "deployment" && source.mechanism === "api_key") {
431
+ return input.provider?.apiKey ? { status: "ready", reason: null, basis: "configuration", checkedAt: null } : {
432
+ status: "not_ready",
433
+ reason: "missing_credential",
434
+ basis: "configuration",
435
+ checkedAt: null
436
+ };
437
+ }
438
+ return observedCredentialReadiness({
439
+ observation: input.observation,
440
+ basis: "resolver",
441
+ nowMs: input.nowMs,
442
+ maxAgeMs: input.maxAgeMs
443
+ });
444
+ }
445
+ function isXaiGrokModel(model) {
446
+ return model.providerId === "xai" && model.id.startsWith("xai/grok-");
447
+ }
448
+ function observationTimestamp(observation) {
449
+ if (!observation || typeof observation.checkedAt !== "string") {
450
+ return { checkedAt: null, checkedAtMs: null };
451
+ }
452
+ const checkedAtMs = Date.parse(observation.checkedAt);
453
+ if (!Number.isFinite(checkedAtMs)) {
454
+ return { checkedAt: null, checkedAtMs: null };
455
+ }
456
+ return { checkedAt: new Date(checkedAtMs).toISOString(), checkedAtMs };
457
+ }
458
+ function xaiGrokAvailabilityFor(input) {
459
+ const { checkedAt, checkedAtMs } = observationTimestamp(input.observation);
460
+ const freshSuccessfulObservation = input.observation?.status === "available" && input.observation.reason === null && checkedAtMs !== null && checkedAtMs <= input.nowMs && input.nowMs - checkedAtMs <= input.maxAgeMs;
461
+ if (freshSuccessfulObservation) {
462
+ return {
463
+ status: "available",
464
+ selectable: true,
465
+ reason: null,
466
+ checkedAt
467
+ };
468
+ }
469
+ return {
470
+ status: "unavailable",
471
+ selectable: false,
472
+ reason: input.observation?.status === "unavailable" ? input.observation.reason ?? "provider_unhealthy" : "provider_unhealthy",
473
+ checkedAt
474
+ };
475
+ }
476
+ function availabilityFor(input) {
477
+ if (!modelDefinitionRunnable(input.model)) {
478
+ return {
479
+ status: "unavailable",
480
+ selectable: false,
481
+ reason: "unsupported",
482
+ checkedAt: null
483
+ };
484
+ }
485
+ if (input.credentialReadiness.status !== "ready") {
486
+ return {
487
+ status: "unavailable",
488
+ selectable: false,
489
+ reason: input.credentialReadiness.reason === "missing_credential" ? "missing_credential" : input.credentialReadiness.reason === "needs_reauth" ? "needs_reauth" : "credential_not_ready",
490
+ checkedAt: input.credentialReadiness.checkedAt
491
+ };
492
+ }
493
+ if (!input.policyAllowed) {
494
+ return {
495
+ status: "unavailable",
496
+ selectable: false,
497
+ reason: "policy_blocked",
498
+ checkedAt: null
499
+ };
500
+ }
501
+ if (isXaiGrokModel(input.model)) {
502
+ return xaiGrokAvailabilityFor({
503
+ observation: input.observation,
504
+ nowMs: input.nowMs,
505
+ maxAgeMs: input.maxAgeMs
506
+ });
507
+ }
508
+ if (!input.observation) {
509
+ return { status: "unknown", selectable: true, reason: null, checkedAt: null };
510
+ }
511
+ if (input.observation.status === "unavailable") {
512
+ return {
513
+ status: "unavailable",
514
+ selectable: false,
515
+ reason: input.observation.reason ?? "provider_unhealthy",
516
+ checkedAt: input.observation.checkedAt
517
+ };
518
+ }
519
+ return {
520
+ status: input.observation.status,
521
+ selectable: true,
522
+ reason: null,
523
+ checkedAt: input.observation.checkedAt
524
+ };
525
+ }
526
+ function resolveWorkspaceModelSelection(input) {
527
+ const codexSettings = input.settings.codexSubscriptionEnabled ? withCodexCatalogProvider(input.settings) : input.settings;
528
+ const xaiSettings = input.settings.supergrokSubscriptionEnabled ? withXaiSubscriptionCatalogProvider(codexSettings) : codexSettings;
529
+ const catalogSettings = withWorkspaceOpenRouterCatalogProvider(
530
+ withWorkspaceGatewayCatalogProvider(xaiSettings, input.workspaceGatewayCustomModels ?? []),
531
+ input.workspaceOpenRouterCustomModels ?? []
532
+ );
533
+ const providers = new Map(
534
+ configuredProviders(catalogSettings).map((provider) => [provider.id, provider])
535
+ );
536
+ const requestedNowMs = input.now?.getTime();
537
+ const nowMs = typeof requestedNowMs === "number" && Number.isFinite(requestedNowMs) ? requestedNowMs : Date.now();
538
+ const maxAgeMs = typeof input.credentialReadinessMaxAgeMs === "number" && Number.isFinite(input.credentialReadinessMaxAgeMs) && input.credentialReadinessMaxAgeMs >= 0 ? input.credentialReadinessMaxAgeMs : MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS;
539
+ return configuredModels(catalogSettings).map((model) => {
540
+ const provider = providers.get(model.providerId);
541
+ const policyAllowed = evaluateWorkspaceModelPolicy(input.policy, {
542
+ providerId: model.providerId,
543
+ modelId: model.id
544
+ }).allowed;
545
+ const credentialReadiness = credentialReadinessFor({
546
+ model,
547
+ provider,
548
+ codexSubscriptionActive: input.codexSubscriptionActive,
549
+ xaiSubscriptionActive: input.xaiSubscriptionActive === true,
550
+ workspaceGatewayConnectionActive: input.workspaceGatewayConnectionActive === true,
551
+ workspaceOpenRouterConnectionActive: input.workspaceOpenRouterConnectionActive === true,
552
+ observation: input.credentialReadinessObservations?.[model.definitionVersion],
553
+ nowMs,
554
+ maxAgeMs
555
+ });
556
+ return {
557
+ model,
558
+ credentialReadiness,
559
+ policyAllowed,
560
+ availability: availabilityFor({
561
+ model,
562
+ credentialReadiness,
563
+ policyAllowed,
564
+ observation: input.observations?.[model.definitionVersion],
565
+ nowMs,
566
+ maxAgeMs
567
+ })
568
+ };
569
+ });
570
+ }
571
+
211
572
  // src/sandbox/fleet.ts
212
573
  import {
213
574
  authorizePersonalMachineForAttempt,
@@ -2074,7 +2435,10 @@ async function resolveSessionAuthorizationActor(db, grant) {
2074
2435
  }
2075
2436
 
2076
2437
  // src/billing/limits.ts
2077
- import { configuredStaticUsageLimits, resolveModelProvider } from "@opengeni/config";
2438
+ import {
2439
+ configuredStaticUsageLimits,
2440
+ resolveModelProviderForTurn
2441
+ } from "@opengeni/config";
2078
2442
  import {
2079
2443
  countActiveApiKeysForWorkspace,
2080
2444
  countActiveOrganizationApiKeysForAccount,
@@ -2086,6 +2450,18 @@ import {
2086
2450
  sumUsageQuantity
2087
2451
  } from "@opengeni/db";
2088
2452
  import { HTTPException as HTTPException2 } from "hono/http-exception";
2453
+ function modelFundingForAdmission(settings, model, codexBilled) {
2454
+ const resolvedModel = model ? resolveModelProviderForTurn(settings, model)?.model : null;
2455
+ const codexSubscriptionModel = resolvedModel?.credentialSource.kind === "connected_subscription" && resolvedModel.credentialSource.provider === "codex";
2456
+ return {
2457
+ // A Codex namespace/definition never bypasses credits by itself: the live
2458
+ // workspace credential predicate above remains authoritative. SuperGrok's
2459
+ // static overlay is likewise secret-free; its worker/provider admission
2460
+ // owns live account selection before any upstream request can occur.
2461
+ fundedWithoutCredits: codexBilled || resolvedModel != null && !codexSubscriptionModel && resolvedModel.cost !== "credits",
2462
+ countsTowardTokenCap: !codexBilled && resolvedModel != null && resolvedModel.billing.upstreamPayer === "deployment"
2463
+ };
2464
+ }
2089
2465
  async function requireLimit(deps, input) {
2090
2466
  const decision = await checkLimit(deps, input);
2091
2467
  if (decision.allowed) {
@@ -2102,19 +2478,22 @@ async function checkLimit(deps, input) {
2102
2478
  workspaceId: input.workspaceId,
2103
2479
  model: input.model
2104
2480
  }) : false;
2105
- const credentialFreeExternal = input.model ? (() => {
2106
- const resolved = resolveModelProvider(deps.settings, input.model);
2107
- return resolved?.model.billing.metering === "external" && resolved.model.credentialSource.kind === "deployment" && resolved.model.credentialSource.mechanism === "none";
2108
- })() : false;
2109
- const externallyBilled = codexBilled || credentialFreeExternal;
2110
- const creditDecision = await checkCreditBalance(deps, input, externallyBilled);
2481
+ const { fundedWithoutCredits, countsTowardTokenCap } = modelFundingForAdmission(
2482
+ deps.settings,
2483
+ input.model,
2484
+ codexBilled
2485
+ );
2486
+ const creditDecision = await checkCreditBalance(deps, input, fundedWithoutCredits);
2111
2487
  if (!creditDecision.allowed) {
2112
2488
  return creditDecision;
2113
2489
  }
2114
2490
  if (deps.settings.usageLimitsMode !== "static" && deps.settings.usageLimitsMode !== "managed") {
2115
2491
  return { allowed: true };
2116
2492
  }
2117
- return await checkStaticCaps(deps, input, externallyBilled);
2493
+ return await checkStaticCaps(deps, input, {
2494
+ fundedWithoutCredits,
2495
+ countsTowardTokenCap
2496
+ });
2118
2497
  }
2119
2498
  async function checkCreditBalance(deps, input, externallyBilled) {
2120
2499
  if (externallyBilled) {
@@ -2129,9 +2508,9 @@ async function checkCreditBalance(deps, input, externallyBilled) {
2129
2508
  }
2130
2509
  return { allowed: false, code: "insufficient_credits", message: "insufficient OpenGeni credits" };
2131
2510
  }
2132
- async function checkStaticCaps(deps, input, externallyBilled) {
2511
+ async function checkStaticCaps(deps, input, funding) {
2133
2512
  const limits = configuredStaticUsageLimits(deps.settings);
2134
- if (limits.maxMonthlyCostMicrosPerAccount && isCostlyAction(input.action) && !externallyBilled) {
2513
+ if (limits.maxMonthlyCostMicrosPerAccount && isCostlyAction(input.action) && !funding.fundedWithoutCredits) {
2135
2514
  const used = await sumUsageQuantity(deps.db, {
2136
2515
  accountId: input.accountId,
2137
2516
  eventType: "model.cost",
@@ -2200,7 +2579,7 @@ async function checkStaticCaps(deps, input, externallyBilled) {
2200
2579
  );
2201
2580
  }
2202
2581
  case "tokens:consume": {
2203
- if (externallyBilled || !limits.maxMonthlyTokensPerWorkspace || !input.workspaceId) {
2582
+ if (!funding.countsTowardTokenCap || !limits.maxMonthlyTokensPerWorkspace || !input.workspaceId) {
2204
2583
  return { allowed: true };
2205
2584
  }
2206
2585
  const used = await sumUsageQuantity(deps.db, {
@@ -7047,6 +7426,8 @@ import {
7047
7426
  getSandbox as getSandbox4,
7048
7427
  getSessionTurnXaiProviderAccountAuthoritySnapshot as getSessionTurnXaiProviderAccountAuthoritySnapshot2,
7049
7428
  getSession as getSession3,
7429
+ lockActiveWorkspaceGatewayCustomModelForAdmission as lockActiveWorkspaceGatewayCustomModelForAdmission2,
7430
+ lockActiveWorkspaceOpenRouterCustomModelForAdmission as lockActiveWorkspaceOpenRouterCustomModelForAdmission2,
7050
7431
  nestedPostgresSqlState as nestedPostgresSqlState2,
7051
7432
  requireWorkspace as requireWorkspace4,
7052
7433
  scopedKnowledgeScopeKey,
@@ -7062,10 +7443,11 @@ import { CODEX_MODEL_ID_PREFIX, isCodexBilledModel } from "@opengeni/codex";
7062
7443
  import {
7063
7444
  canonicalizeConfiguredModelId,
7064
7445
  configuredAllowedModels,
7446
+ WORKSPACE_GATEWAY_MODEL_ID_PREFIX as WORKSPACE_GATEWAY_MODEL_ID_PREFIX2,
7447
+ WORKSPACE_OPENROUTER_MODEL_ID_PREFIX as WORKSPACE_OPENROUTER_MODEL_ID_PREFIX2,
7065
7448
  resolveFirstPartyMcpToolPolicy,
7066
7449
  policyProviderIdForModel,
7067
7450
  resolveTurnExecutionPolicyV1,
7068
- WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
7069
7451
  XAI_SUBSCRIPTION_MODEL_ID_PREFIX
7070
7452
  } from "@opengeni/config";
7071
7453
  import {
@@ -7079,15 +7461,18 @@ import {
7079
7461
  SessionSpawnDenial,
7080
7462
  ServiceTurnInitiator,
7081
7463
  ServiceTurnInitiatorContext,
7082
- evaluateWorkspaceModelPolicy,
7464
+ evaluateWorkspaceModelPolicy as evaluateWorkspaceModelPolicy2,
7083
7465
  normalizeAutomaticSessionTitle,
7084
7466
  resolveWorkspaceSessionToolDefaults as resolveWorkspaceSessionToolDefaults2,
7467
+ metadataWithTurnExecutionPolicyV1,
7468
+ readTurnExecutionPolicyV1,
7085
7469
  stableJson as stableJson4,
7086
7470
  SessionMcpApprovalPolicy
7087
7471
  } from "@opengeni/contracts";
7088
7472
  import {
7089
7473
  createSession,
7090
7474
  createSessionWithIdempotencyKeyResult,
7475
+ canonicalSessionCommandHash,
7091
7476
  encryptVariableSetValue as encryptVariableSetValue2,
7092
7477
  getAnySessionInGroup,
7093
7478
  getEnrollment as getEnrollment2,
@@ -7098,10 +7483,10 @@ import {
7098
7483
  listDistinctRigVersionIdsInGroup,
7099
7484
  getSandbox as getSandbox3,
7100
7485
  getSession as getSession2,
7486
+ getInitializedSessionCreateReplay,
7101
7487
  getSessionAuthorityProjection as getSessionAuthorityProjection2,
7102
7488
  SessionIdConflictError,
7103
7489
  NewSessionDraftConflictError,
7104
- getSessionSpawnDenialByIdempotencyKey,
7105
7490
  getWorkspaceControlEvent,
7106
7491
  getSessionLineage,
7107
7492
  getSessionTurn,
@@ -7113,7 +7498,10 @@ import {
7113
7498
  initializeSessionStartAtomically,
7114
7499
  listSessionTurns,
7115
7500
  listSessionMcpServersForChildInheritance,
7501
+ lockActiveWorkspaceGatewayCustomModelForAdmission,
7502
+ lockActiveWorkspaceOpenRouterCustomModelForAdmission,
7116
7503
  requireSession as requireSession2,
7504
+ replaySubmittedHumanPromptFromBoundaryReceipt,
7117
7505
  submitHumanPromptInTransaction,
7118
7506
  appendSessionEventsWithLockedSessionUpdate,
7119
7507
  updateSessionTitleWithEvent,
@@ -7998,16 +8386,43 @@ function automaticTitleForAgentChildCreate(presentation, goal, initialMessage) {
7998
8386
  return null;
7999
8387
  }
8000
8388
  async function createAndStartSessionWithOutcome(input) {
8001
- const sessionMetadata = {
8002
- ...input.metadata,
8003
- model: input.model,
8004
- reasoningEffort: input.reasoningEffort,
8005
- ...input.latencyMode !== void 0 ? { latencyMode: input.latencyMode } : {}
8006
- };
8389
+ const sessionMetadata = metadataWithTurnExecutionPolicyV1(
8390
+ {
8391
+ ...input.metadata,
8392
+ model: input.model,
8393
+ reasoningEffort: input.reasoningEffort,
8394
+ ...input.latencyMode !== void 0 ? { latencyMode: input.latencyMode } : {}
8395
+ },
8396
+ input.turnExecutionPolicy
8397
+ );
8007
8398
  const frozenCreatedByContext = freezeAgentChildAutomaticTitleInCreatorContext(
8008
8399
  input.createdByContext,
8009
8400
  input.initialAutomaticTitle
8010
8401
  );
8402
+ const requiresActiveWorkspaceCustomModel = (input.workspaceCustomModel === true || input.workspaceGatewayCustomModel === true) && input.retainWorkspaceCustomModel !== true && input.retainWorkspaceGatewayModel !== true;
8403
+ const beforeCreateCommit = requiresActiveWorkspaceCustomModel || input.beforeCreateCommit ? async (tx, sessionId, context) => {
8404
+ if (requiresActiveWorkspaceCustomModel && context?.created !== false) {
8405
+ const openRouter = input.model.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX2);
8406
+ const upstreamModelId = input.model.slice(
8407
+ openRouter ? WORKSPACE_OPENROUTER_MODEL_ID_PREFIX2.length : WORKSPACE_GATEWAY_MODEL_ID_PREFIX2.length
8408
+ );
8409
+ const active = openRouter ? await lockActiveWorkspaceOpenRouterCustomModelForAdmission(tx, {
8410
+ accountId: input.accountId,
8411
+ workspaceId: input.workspaceId,
8412
+ upstreamModelId
8413
+ }) : await lockActiveWorkspaceGatewayCustomModelForAdmission(tx, {
8414
+ accountId: input.accountId,
8415
+ workspaceId: input.workspaceId,
8416
+ upstreamModelId
8417
+ });
8418
+ if (!active) {
8419
+ throw new HTTPException11(422, {
8420
+ message: `model is not available: ${input.model}`
8421
+ });
8422
+ }
8423
+ }
8424
+ await input.beforeCreateCommit?.(tx, sessionId);
8425
+ } : void 0;
8011
8426
  if (input.createIdempotencyKey) {
8012
8427
  const keyedResult = await createSessionWithIdempotencyKeyResult(input.db, {
8013
8428
  ...input.requestedSessionId ? { requestedSessionId: input.requestedSessionId } : {},
@@ -8050,15 +8465,23 @@ async function createAndStartSessionWithOutcome(input) {
8050
8465
  maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
8051
8466
  allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
8052
8467
  subjectId: input.subjectId ?? null,
8053
- ...input.beforeCreateCommit ? { beforeCreateCommit: input.beforeCreateCommit } : {}
8468
+ ...beforeCreateCommit ? { beforeCreateCommit } : {}
8054
8469
  });
8055
8470
  if (keyedResult.denied) {
8056
8471
  throw new SessionSpawnDeniedError(SessionSpawnDenial.parse(keyedResult.denial));
8057
8472
  }
8058
8473
  const { session: keyed, created } = keyedResult;
8059
8474
  if (!created) {
8475
+ const persistedPolicy = readTurnExecutionPolicyV1(keyed.metadata);
8060
8476
  const finished3 = await finishStartSession(
8061
- keyed.temporalWorkflowId ? { ...input, seedTargetSandbox: null } : input,
8477
+ keyed.temporalWorkflowId ? {
8478
+ ...input,
8479
+ seedTargetSandbox: null,
8480
+ ...persistedPolicy.kind === "valid" ? { turnExecutionPolicy: persistedPolicy.policy } : {}
8481
+ } : {
8482
+ ...input,
8483
+ ...persistedPolicy.kind === "valid" ? { turnExecutionPolicy: persistedPolicy.policy } : {}
8484
+ },
8062
8485
  keyed
8063
8486
  );
8064
8487
  return {
@@ -8118,7 +8541,7 @@ async function createAndStartSessionWithOutcome(input) {
8118
8541
  maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
8119
8542
  allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
8120
8543
  subjectId: input.subjectId ?? null,
8121
- ...input.beforeCreateCommit ? { beforeCreateCommit: input.beforeCreateCommit } : {}
8544
+ ...beforeCreateCommit ? { beforeCreateCommit } : {}
8122
8545
  });
8123
8546
  } catch (error) {
8124
8547
  if (error instanceof SessionSpawnDeniedDbError) {
@@ -8237,9 +8660,6 @@ function canonicalConfiguredModel(settings, model) {
8237
8660
  if (settings.supergrokSubscriptionEnabled && canonicalModel.startsWith(XAI_SUBSCRIPTION_MODEL_ID_PREFIX)) {
8238
8661
  return canonicalModel;
8239
8662
  }
8240
- if (canonicalModel.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
8241
- return canonicalModel;
8242
- }
8243
8663
  throw new HTTPException11(422, { message: `model is not available: ${model}` });
8244
8664
  }
8245
8665
  function assertConfiguredModel(settings, model) {
@@ -8276,7 +8696,7 @@ async function assertWorkspaceModelPolicyAllows(db, settings, workspaceId, model
8276
8696
  return;
8277
8697
  }
8278
8698
  const providerId = policyProviderIdForModel(settings, canonicalModel);
8279
- const verdict = evaluateWorkspaceModelPolicy(policy, {
8699
+ const verdict = evaluateWorkspaceModelPolicy2(policy, {
8280
8700
  providerId,
8281
8701
  modelId: canonicalModel
8282
8702
  });
@@ -8298,91 +8718,8 @@ async function requireQueuedTurnForApi(db, workspaceId, sessionId, turnId) {
8298
8718
  }
8299
8719
  return turn;
8300
8720
  }
8301
- async function postUserMessageTurn(input) {
8302
- const { db, bus, workflowClient, settings, accountId, workspaceId, sessionId } = input;
8303
- const requestedModel = canonicalConfiguredModel(settings, input.model ?? null) ?? null;
8304
- const requestedReasoningEffort = input.reasoningEffort ?? null;
8305
- assertConfiguredModel(settings, requestedModel);
8306
- await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, requestedModel);
8307
- const sessionForModelGate = await requireSession2(db, workspaceId, sessionId);
8308
- const effectiveModelForGate = requestedModel ?? sessionForModelGate.model;
8309
- try {
8310
- assertSessionAllowsProductModel(sessionForModelGate, effectiveModelForGate);
8311
- } catch (error) {
8312
- if (error instanceof CodexCompactionV2ProviderLockedError) {
8313
- throw new HTTPException11(422, { message: error.message, cause: error });
8314
- }
8315
- throw error;
8316
- }
8317
- const operationKey = input.clientEventId ?? crypto.randomUUID();
8318
- let result;
8319
- try {
8320
- result = await runIdempotentPersistenceTransaction(
8321
- {
8322
- stage: "session.prompt.submit",
8323
- eventTypes: ["user.message", "turn.queued", "session.status.changed"],
8324
- maxAttempts: 3
8325
- },
8326
- async () => await withWorkspaceSubjectSessionActivityRls(
8327
- db,
8328
- workspaceId,
8329
- input.actor ?? accountId,
8330
- (scoped) => submitHumanPromptInTransaction(scoped, {
8331
- accountId,
8332
- workspaceId,
8333
- sessionId,
8334
- subjectId: input.actor ?? accountId,
8335
- ...input.actorLabel ? { subjectLabel: input.actorLabel } : {},
8336
- actor: input.commandActor ?? {
8337
- type: "human",
8338
- subjectId: input.actor ?? accountId
8339
- },
8340
- operationKey,
8341
- delivery: input.delivery ?? "send",
8342
- controlEtag: input.controlEtag ?? null,
8343
- expectedDraftRevision: input.expectedDraftRevision ?? null,
8344
- text: input.text,
8345
- annotations: input.annotations ?? [],
8346
- modelContext: input.modelContext ?? null,
8347
- resources: input.resources,
8348
- ...input.composerDraftResources ? { composerDraftResources: input.composerDraftResources } : {},
8349
- model: requestedModel,
8350
- reasoningEffort: requestedReasoningEffort,
8351
- latencyMode: input.latencyMode ?? null,
8352
- reasoningEffortFallback: input.reasoningEffortFallback ?? settings.openaiReasoningEffort,
8353
- turnExecutionPolicy: input.turnExecutionPolicy,
8354
- source: input.origin === "operator" ? "api" : "user",
8355
- ...input.recordAgentRunUsage !== void 0 ? { recordAgentRunUsage: input.recordAgentRunUsage } : {},
8356
- personalConnectionDelegations: input.personalConnectionDelegations ?? [],
8357
- ...input.personalResourceAttachment ? {
8358
- personalResourceAttachment: input.personalResourceAttachment
8359
- } : {},
8360
- mcpCredentialUpdates: input.mcpCredentialUpdates ?? [],
8361
- controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs()
8362
- })
8363
- )
8364
- );
8365
- } catch (error) {
8366
- if (error instanceof WorkspaceControlBusyError) {
8367
- throw error;
8368
- }
8369
- if (error instanceof PersonalResourceAttachmentAcceptanceError) {
8370
- throw new HTTPException11(
8371
- error.kind === "invalid" ? 422 : error.kind === "forbidden" ? 403 : 409,
8372
- { message: error.message, cause: error }
8373
- );
8374
- }
8375
- if (error instanceof QueueCommandConflictError || error instanceof SessionControlConflictError) {
8376
- throw new HTTPException11(409, { message: error.message });
8377
- }
8378
- if (error instanceof Error && error.message.includes("cancelled")) {
8379
- throw new HTTPException11(409, { message: error.message });
8380
- }
8381
- if (error instanceof Error && error.message.startsWith("Unknown session MCP server")) {
8382
- throw new HTTPException11(422, { message: error.message });
8383
- }
8384
- throw error;
8385
- }
8721
+ function finalizePostUserMessageTurn(input, result) {
8722
+ const { db, bus, workflowClient, accountId, workspaceId, sessionId } = input;
8386
8723
  const postCommitTask = async () => {
8387
8724
  await Promise.all([
8388
8725
  (async () => {
@@ -8473,18 +8810,132 @@ async function postUserMessageTurn(input) {
8473
8810
  replay: result.replay
8474
8811
  };
8475
8812
  }
8476
- function resolveChildGoalFromAcceptedSnapshot(goal, parentGoalSnapshot) {
8477
- const inheritedRootConstraints = parentGoalSnapshot.state === "none" ? [] : parentGoalSnapshot.rootConstraints;
8478
- const requestedRootConstraints = goal.rootConstraints;
8479
- if (requestedRootConstraints?.some((constraint) => !inheritedRootConstraints.includes(constraint))) {
8480
- throw new Error(
8481
- "child goal rootConstraints must be an exact subset of the calling turn's frozen root constraints"
8482
- );
8813
+ async function postUserMessageTurn(input) {
8814
+ const { db, settings, accountId, workspaceId, sessionId } = input;
8815
+ const requestedModel = canonicalConfiguredModel(settings, input.model ?? null) ?? null;
8816
+ const requestedReasoningEffort = input.reasoningEffort ?? null;
8817
+ assertConfiguredModel(settings, requestedModel);
8818
+ const sessionForModelGate = await requireSession2(db, workspaceId, sessionId);
8819
+ const effectiveModelForGate = requestedModel ?? sessionForModelGate.model;
8820
+ const freshWorkspaceCustomModel = requestedModel !== null && isWorkspaceCustomModelId(settings, requestedModel) && requestedModel !== sessionForModelGate.model ? requestedModel : null;
8821
+ await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, effectiveModelForGate);
8822
+ try {
8823
+ assertSessionAllowsProductModel(sessionForModelGate, effectiveModelForGate);
8824
+ } catch (error) {
8825
+ if (error instanceof CodexCompactionV2ProviderLockedError) {
8826
+ throw new HTTPException11(422, { message: error.message, cause: error });
8827
+ }
8828
+ throw error;
8483
8829
  }
8484
- return {
8485
- ...goal,
8486
- rootConstraints: requestedRootConstraints ?? inheritedRootConstraints
8487
- };
8830
+ const operationKey = input.clientEventId ?? crypto.randomUUID();
8831
+ let result;
8832
+ try {
8833
+ result = await runIdempotentPersistenceTransaction(
8834
+ {
8835
+ stage: "session.prompt.submit",
8836
+ eventTypes: ["user.message", "turn.queued", "session.status.changed"],
8837
+ maxAttempts: 3
8838
+ },
8839
+ async () => await withWorkspaceSubjectSessionActivityRls(
8840
+ db,
8841
+ workspaceId,
8842
+ input.actor ?? accountId,
8843
+ (scoped) => submitHumanPromptInTransaction(scoped, {
8844
+ accountId,
8845
+ workspaceId,
8846
+ sessionId,
8847
+ subjectId: input.actor ?? accountId,
8848
+ ...input.actorLabel ? { subjectLabel: input.actorLabel } : {},
8849
+ actor: input.commandActor ?? {
8850
+ type: "human",
8851
+ subjectId: input.actor ?? accountId
8852
+ },
8853
+ operationKey,
8854
+ ...input.boundaryRequestHash ? { boundaryRequestHash: input.boundaryRequestHash } : {},
8855
+ delivery: input.delivery ?? "send",
8856
+ controlEtag: input.controlEtag ?? null,
8857
+ expectedDraftRevision: input.expectedDraftRevision ?? null,
8858
+ text: input.text,
8859
+ annotations: input.annotations ?? [],
8860
+ modelContext: input.modelContext ?? null,
8861
+ resources: input.resources,
8862
+ ...input.composerDraftResources ? { composerDraftResources: input.composerDraftResources } : {},
8863
+ model: requestedModel,
8864
+ reasoningEffort: requestedReasoningEffort,
8865
+ latencyMode: input.latencyMode ?? null,
8866
+ reasoningEffortFallback: input.reasoningEffortFallback ?? settings.openaiReasoningEffort,
8867
+ turnExecutionPolicy: input.turnExecutionPolicy,
8868
+ source: input.origin === "operator" ? "api" : "user",
8869
+ ...input.recordAgentRunUsage !== void 0 ? { recordAgentRunUsage: input.recordAgentRunUsage } : {},
8870
+ personalConnectionDelegations: input.personalConnectionDelegations ?? [],
8871
+ ...input.personalResourceAttachment ? {
8872
+ personalResourceAttachment: input.personalResourceAttachment
8873
+ } : {},
8874
+ mcpCredentialUpdates: input.mcpCredentialUpdates ?? [],
8875
+ ...freshWorkspaceCustomModel ? {
8876
+ beforeFreshPromptCommit: async (tx) => {
8877
+ const reference = workspaceCustomModelReference(
8878
+ settings,
8879
+ freshWorkspaceCustomModel
8880
+ );
8881
+ if (!reference) {
8882
+ throw new Error("workspace custom model reference disappeared");
8883
+ }
8884
+ const active = reference.providerKind === "openrouter" ? await lockActiveWorkspaceOpenRouterCustomModelForAdmission(tx, {
8885
+ accountId,
8886
+ workspaceId,
8887
+ upstreamModelId: reference.upstreamModelId
8888
+ }) : await lockActiveWorkspaceGatewayCustomModelForAdmission(tx, {
8889
+ accountId,
8890
+ workspaceId,
8891
+ upstreamModelId: reference.upstreamModelId
8892
+ });
8893
+ if (!active) {
8894
+ throw new HTTPException11(422, {
8895
+ message: `model is not available: ${freshWorkspaceCustomModel}`
8896
+ });
8897
+ }
8898
+ }
8899
+ } : {},
8900
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs()
8901
+ })
8902
+ )
8903
+ );
8904
+ } catch (error) {
8905
+ if (error instanceof WorkspaceControlBusyError) {
8906
+ throw error;
8907
+ }
8908
+ if (error instanceof PersonalResourceAttachmentAcceptanceError) {
8909
+ throw new HTTPException11(
8910
+ error.kind === "invalid" ? 422 : error.kind === "forbidden" ? 403 : 409,
8911
+ { message: error.message, cause: error }
8912
+ );
8913
+ }
8914
+ if (error instanceof QueueCommandConflictError || error instanceof SessionControlConflictError) {
8915
+ throw new HTTPException11(409, { message: error.message });
8916
+ }
8917
+ if (error instanceof Error && error.message.includes("cancelled")) {
8918
+ throw new HTTPException11(409, { message: error.message });
8919
+ }
8920
+ if (error instanceof Error && error.message.startsWith("Unknown session MCP server")) {
8921
+ throw new HTTPException11(422, { message: error.message });
8922
+ }
8923
+ throw error;
8924
+ }
8925
+ return finalizePostUserMessageTurn(input, result);
8926
+ }
8927
+ function resolveChildGoalFromAcceptedSnapshot(goal, parentGoalSnapshot) {
8928
+ const inheritedRootConstraints = parentGoalSnapshot.state === "none" ? [] : parentGoalSnapshot.rootConstraints;
8929
+ const requestedRootConstraints = goal.rootConstraints;
8930
+ if (requestedRootConstraints?.some((constraint) => !inheritedRootConstraints.includes(constraint))) {
8931
+ throw new Error(
8932
+ "child goal rootConstraints must be an exact subset of the calling turn's frozen root constraints"
8933
+ );
8934
+ }
8935
+ return {
8936
+ ...goal,
8937
+ rootConstraints: requestedRootConstraints ?? inheritedRootConstraints
8938
+ };
8488
8939
  }
8489
8940
  function resolveSessionCreateVisibility(input) {
8490
8941
  if (input.parentVisibility === "user_private") {
@@ -8501,9 +8952,56 @@ function resolveSessionCreateVisibility(input) {
8501
8952
  }
8502
8953
  return input.requestedVisibility === "private" ? "user_private" : "workspace_shared";
8503
8954
  }
8504
- async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawPayload, authorization, agentChildPresentation) {
8505
- const { settings, db, bus, workflowClient, objectStorage } = deps;
8955
+ async function resolveWorkspaceModelBoundarySettings(deps, grant, workspaceId, modelIds, retainedProductModelId) {
8956
+ const retainedWorkspaceModel = retainedProductModelId?.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX2) === true || retainedProductModelId?.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX2) === true;
8957
+ if (deps.catalogSourceSettings) {
8958
+ if (!retainedWorkspaceModel) return deps.settings;
8959
+ }
8960
+ const workspaceModelIds = modelIds.filter(
8961
+ (modelId) => typeof modelId === "string" && (modelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX2) || modelId.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX2))
8962
+ );
8963
+ const needsWorkspaceResolution = deps.settings.modelCatalogSource === "database" || workspaceModelIds.length > 0;
8964
+ if (!needsWorkspaceResolution) return deps.settings;
8965
+ return (await resolveWorkspaceCatalogSettings(deps.db, deps.catalogSourceSettings ?? deps.settings, {
8966
+ accountId: grant.accountId,
8967
+ workspaceId,
8968
+ ...retainedProductModelId !== void 0 ? { retainedProductModelId } : {}
8969
+ })).settings;
8970
+ }
8971
+ async function withSessionCreateUsageRecording(input) {
8972
+ let usageRecording = "recorded";
8973
+ if (input.startMode !== "realtime") {
8974
+ try {
8975
+ await recordWorkspaceUsage(input.deps, {
8976
+ accountId: input.grant.accountId,
8977
+ workspaceId: input.workspaceId,
8978
+ subjectId: input.grant.subjectId,
8979
+ eventType: "agent_run.created",
8980
+ quantity: 1,
8981
+ unit: "run",
8982
+ sourceResourceType: "session",
8983
+ sourceResourceId: input.createOutcome.session.id,
8984
+ sessionId: input.createOutcome.session.id,
8985
+ initiator: input.createOutcome.session.createdBy,
8986
+ initiatorContext: input.createOutcome.session.createdByContext,
8987
+ origin: input.origin,
8988
+ idempotencyKey: `agent_run.created:${input.workspaceId}:${input.createOutcome.session.id}`
8989
+ });
8990
+ } catch (error) {
8991
+ usageRecording = "failed";
8992
+ reportSessionUsageRecordingFailure(error);
8993
+ }
8994
+ }
8995
+ return { ...input.createOutcome, usageRecording };
8996
+ }
8997
+ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspaceId, rawPayload, authorization, agentChildPresentation) {
8506
8998
  const payload = CreateSessionRequest.parse(rawPayload);
8999
+ if (hasReservedOpenGeniSlackBotSessionMetadata(payload.metadata)) {
9000
+ throw new HTTPException11(422, {
9001
+ message: `${OPENGENI_SLACK_BOT_SESSION_METADATA_KEY2} is reserved for scheduler routing`
9002
+ });
9003
+ }
9004
+ const db = unresolvedDeps.db;
8507
9005
  const visibilityProvided = hasOwnProperty(rawPayload, "visibility");
8508
9006
  if (payload.visibility === "private" && !grant.metadata?.["sessionId"]) {
8509
9007
  if (!authorization) {
@@ -8511,39 +9009,17 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8511
9009
  message: "managed human session required"
8512
9010
  });
8513
9011
  }
8514
- await requireManagedHumanPrivateSessionCreate(deps, authorization, workspaceId);
9012
+ await requireManagedHumanPrivateSessionCreate(unresolvedDeps, authorization, workspaceId);
8515
9013
  if (payload.sandbox === "shared" || typeof payload.sandbox === "object") {
8516
9014
  throw new HTTPException11(422, {
8517
9015
  message: "Only-me sessions require their own sandbox"
8518
9016
  });
8519
9017
  }
8520
9018
  }
8521
- if (hasReservedOpenGeniSlackBotSessionMetadata(payload.metadata)) {
8522
- throw new HTTPException11(422, {
8523
- message: `${OPENGENI_SLACK_BOT_SESSION_METADATA_KEY2} is reserved for scheduler routing`
8524
- });
8525
- }
8526
- if (payload.idempotencyKey) {
8527
- const denial = await getSessionSpawnDenialByIdempotencyKey(
8528
- db,
8529
- workspaceId,
8530
- payload.idempotencyKey
8531
- );
8532
- if (denial) {
8533
- throw new SessionSpawnDeniedError(SessionSpawnDenial.parse(denial));
8534
- }
8535
- }
8536
- await requireAtomicPersonalResourceAttachment(
8537
- deps,
8538
- authorization,
8539
- workspaceId,
8540
- payload.personalResourceAttachment,
8541
- false
8542
- );
8543
9019
  const parentSessionId = typeof grant.metadata?.["sessionId"] === "string" ? grant.metadata["sessionId"] : null;
8544
9020
  if (parentSessionId) {
8545
9021
  try {
8546
- await requireSessionAuthorization(deps, grant, {
9022
+ await requireSessionAuthorization(unresolvedDeps, grant, {
8547
9023
  sessionId: parentSessionId,
8548
9024
  operation: "session.child.create",
8549
9025
  surface: "core"
@@ -8593,6 +9069,102 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8593
9069
  message: "caller attempt does not belong to the parent session"
8594
9070
  });
8595
9071
  }
9072
+ const replayManagedHumanSubjectId = creationInitiator.actor ? parentCallingTurn?.initiatingHumanSubjectId ?? (parentCallingTurn?.initiator.kind === "subject" ? parentCallingTurn.initiator.subjectId : null) : authorization?.canonicalManagedHumanSession || grant.principalKind === "human_session" ? grant.subjectId : null;
9073
+ let retainedKeyedShellModel = null;
9074
+ if (payload.idempotencyKey && (effectiveVisibility !== "user_private" || replayManagedHumanSubjectId !== null)) {
9075
+ try {
9076
+ const initializedReplay = await getInitializedSessionCreateReplay(db, {
9077
+ accountId: grant.accountId,
9078
+ workspaceId,
9079
+ subjectId: replayManagedHumanSubjectId ?? grant.subjectId,
9080
+ ...replayManagedHumanSubjectId ? { activeManagedHumanSubjectId: replayManagedHumanSubjectId } : {},
9081
+ createIdempotencyKey: payload.idempotencyKey,
9082
+ ...payload.requestedSessionId ? { requestedSessionId: payload.requestedSessionId } : {},
9083
+ visibility: effectiveVisibility,
9084
+ variableSetIds: payload.variableSetIds ?? [],
9085
+ initialPersonalResourceAttachmentIntent: payload.personalResourceAttachment ?? null,
9086
+ deferInitialTurn: payload.startMode === "realtime"
9087
+ });
9088
+ if (initializedReplay) {
9089
+ if (initializedReplay.outcome === "denied") {
9090
+ throw new SessionSpawnDeniedError(SessionSpawnDenial.parse(initializedReplay.denial));
9091
+ }
9092
+ if (initializedReplay.outcome === "pending") {
9093
+ retainedKeyedShellModel = initializedReplay.session.model;
9094
+ } else {
9095
+ if (initializedReplay.workflowWakeRevision !== null) {
9096
+ await unresolvedDeps.workflowClient.wakeSessionWorkflow({
9097
+ accountId: grant.accountId,
9098
+ workspaceId,
9099
+ sessionId: initializedReplay.session.id,
9100
+ workflowId: initializedReplay.temporalWorkflowId,
9101
+ wakeRevision: initializedReplay.workflowWakeRevision
9102
+ });
9103
+ }
9104
+ return await withSessionCreateUsageRecording({
9105
+ deps: unresolvedDeps,
9106
+ grant,
9107
+ workspaceId,
9108
+ startMode: payload.startMode,
9109
+ origin: creationInitiator.actor ? "system" : "user",
9110
+ createOutcome: {
9111
+ session: initializedReplay.session,
9112
+ outcome: initializedReplay.changed ? "repaired" : "replayed",
9113
+ replay: !initializedReplay.changed,
9114
+ changed: initializedReplay.changed
9115
+ }
9116
+ });
9117
+ }
9118
+ }
9119
+ } catch (error) {
9120
+ if (error instanceof SessionIdConflictError) {
9121
+ throw new HTTPException11(409, {
9122
+ message: "requested session id is already in use"
9123
+ });
9124
+ }
9125
+ if (error instanceof SessionCreateIdempotencyConflictError) {
9126
+ throw new HTTPException11(409, { message: error.message, cause: error });
9127
+ }
9128
+ throw error;
9129
+ }
9130
+ }
9131
+ let settings = await resolveWorkspaceModelBoundarySettings(
9132
+ unresolvedDeps,
9133
+ grant,
9134
+ workspaceId,
9135
+ [payload.model],
9136
+ retainedKeyedShellModel
9137
+ );
9138
+ let deps = settings === unresolvedDeps.settings ? unresolvedDeps : {
9139
+ ...unresolvedDeps,
9140
+ catalogSourceSettings: unresolvedDeps.catalogSourceSettings ?? unresolvedDeps.settings,
9141
+ settings
9142
+ };
9143
+ const { bus, workflowClient, objectStorage } = deps;
9144
+ await requireAtomicPersonalResourceAttachment(
9145
+ deps,
9146
+ authorization,
9147
+ workspaceId,
9148
+ payload.personalResourceAttachment,
9149
+ false
9150
+ );
9151
+ const inheritedModel = parentCallingTurn?.model ?? parentSession?.model ?? settings.openaiModel;
9152
+ const effectiveModelId = payload.model ?? inheritedModel;
9153
+ const effectiveCatalogSettings = await resolveWorkspaceModelBoundarySettings(
9154
+ deps,
9155
+ grant,
9156
+ workspaceId,
9157
+ [effectiveModelId],
9158
+ parentSession ? inheritedModel : null
9159
+ );
9160
+ if (effectiveCatalogSettings !== settings) {
9161
+ deps = {
9162
+ ...deps,
9163
+ catalogSourceSettings: deps.catalogSourceSettings ?? settings,
9164
+ settings: effectiveCatalogSettings
9165
+ };
9166
+ settings = effectiveCatalogSettings;
9167
+ }
8596
9168
  let effectiveGoal = payload.goal;
8597
9169
  if (parentSession && payload.goal) {
8598
9170
  try {
@@ -8743,8 +9315,7 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8743
9315
  }
8744
9316
  channelId = channel.id;
8745
9317
  }
8746
- const inheritedModel = parentCallingTurn?.model ?? parentSession?.model ?? settings.openaiModel;
8747
- const model = canonicalConfiguredModel(settings, payload.model ?? inheritedModel);
9318
+ const model = canonicalConfiguredModel(settings, effectiveModelId);
8748
9319
  if (model === null || model === void 0) {
8749
9320
  throw new Error("effective session model unexpectedly resolved to null");
8750
9321
  }
@@ -9057,6 +9628,8 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
9057
9628
  sessionMcpServers: sessionMcpServers.metadata,
9058
9629
  personalConnectionDelegations,
9059
9630
  initialPersonalResourceAttachmentIntent: payload.personalResourceAttachment ?? null,
9631
+ workspaceCustomModel: isWorkspaceCustomModelId(settings, model),
9632
+ retainWorkspaceCustomModel: parentSession !== null && model === inheritedModel,
9060
9633
  ...xaiProviderAccountAuthoritySnapshot ? { xaiProviderAccountAuthoritySnapshot } : {},
9061
9634
  parentSessionId,
9062
9635
  createIdempotencyKey: payload.idempotencyKey ?? null,
@@ -9118,30 +9691,14 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
9118
9691
  }
9119
9692
  throw error;
9120
9693
  }
9121
- let usageRecording = "recorded";
9122
- if (payload.startMode !== "realtime") {
9123
- try {
9124
- await recordWorkspaceUsage(deps, {
9125
- accountId: grant.accountId,
9126
- workspaceId,
9127
- subjectId: grant.subjectId,
9128
- eventType: "agent_run.created",
9129
- quantity: 1,
9130
- unit: "run",
9131
- sourceResourceType: "session",
9132
- sourceResourceId: createOutcome.session.id,
9133
- sessionId: createOutcome.session.id,
9134
- initiator: createOutcome.session.createdBy,
9135
- initiatorContext: createOutcome.session.createdByContext,
9136
- origin: creationInitiator.actor ? "system" : "user",
9137
- idempotencyKey: `agent_run.created:${workspaceId}:${createOutcome.session.id}`
9138
- });
9139
- } catch (error) {
9140
- usageRecording = "failed";
9141
- reportSessionUsageRecordingFailure(error);
9142
- }
9143
- }
9144
- return { ...createOutcome, usageRecording };
9694
+ return await withSessionCreateUsageRecording({
9695
+ deps,
9696
+ grant,
9697
+ workspaceId,
9698
+ startMode: payload.startMode,
9699
+ origin: creationInitiator.actor ? "system" : "user",
9700
+ createOutcome
9701
+ });
9145
9702
  }
9146
9703
  function reportSessionUsageRecordingFailure(_error) {
9147
9704
  console.warn(
@@ -9156,163 +9713,294 @@ function reportSessionUsageRecordingFailure(_error) {
9156
9713
  async function createSessionForRequest(deps, grant, workspaceId, rawPayload, authorization) {
9157
9714
  return (await createSessionForRequestWithOutcome(deps, grant, workspaceId, rawPayload, authorization)).session;
9158
9715
  }
9716
+ function sessionPromptBoundaryRequestHash(input) {
9717
+ return `prompt-boundary-v1:${canonicalSessionCommandHash({
9718
+ delivery: input.delivery,
9719
+ controlEtag: input.controlEtag,
9720
+ expectedDraftRevision: input.expectedDraftRevision,
9721
+ text: input.text,
9722
+ annotations: input.annotations,
9723
+ modelContext: input.modelContext,
9724
+ resources: input.resources,
9725
+ composerDraftResourcesProvided: input.composerDraftResources !== void 0,
9726
+ composerDraftResources: input.composerDraftResources ?? [],
9727
+ model: input.model,
9728
+ reasoningEffort: input.reasoningEffort,
9729
+ latencyMode: input.latencyMode,
9730
+ source: input.source,
9731
+ mcpCredentialUpdates: input.mcpCredentialUpdates,
9732
+ connectionAuthorities: input.connectionAuthorities ?? [],
9733
+ personalResourceAttachment: input.personalResourceAttachment ?? null,
9734
+ ...input.commandActor.type === "service" ? {
9735
+ serviceInitiator: {
9736
+ subjectId: input.commandActor.subjectId,
9737
+ subjectLabel: input.commandActor.subjectLabel ?? null,
9738
+ context: input.commandActor.context ?? {}
9739
+ }
9740
+ } : {}
9741
+ })}`;
9742
+ }
9159
9743
  async function acceptSessionUserMessageWithOutcome(deps, grant, workspaceId, sessionId, input) {
9160
- const { settings, db, bus, workflowClient, objectStorage } = deps;
9744
+ const { db, bus, workflowClient, objectStorage } = deps;
9161
9745
  const delegatedServiceInitiator = serviceInitiatorForGrant(grant);
9746
+ const delivery = input.delivery ?? "send";
9747
+ const source = delegatedServiceInitiator || input.origin === "operator" ? "api" : "user";
9748
+ const commandActor = delegatedServiceInitiator ? {
9749
+ type: "service",
9750
+ subjectId: delegatedServiceInitiator.initiator.subjectId,
9751
+ ...delegatedServiceInitiator.initiator.label ? { subjectLabel: delegatedServiceInitiator.initiator.label } : {},
9752
+ context: delegatedServiceInitiator.context
9753
+ } : { type: "human", subjectId: grant.subjectId };
9162
9754
  await requireSessionAuthorization(deps, grant, {
9163
9755
  sessionId,
9164
- operation: input.delivery === "steer" ? "session.steer" : "session.append",
9756
+ operation: delivery === "steer" ? "session.steer" : "session.append",
9165
9757
  surface: "core"
9166
9758
  });
9167
- await requireAtomicPersonalResourceAttachment(
9168
- deps,
9169
- input.authorization,
9170
- workspaceId,
9171
- input.personalResourceAttachment,
9172
- true
9173
- );
9174
- const existingSession = await requireSession2(db, workspaceId, sessionId);
9175
- const requestedModel = canonicalConfiguredModel(settings, input.model ?? null) ?? null;
9176
- const effectiveModel = canonicalConfiguredModel(settings, requestedModel ?? existingSession.model) ?? null;
9177
- if (effectiveModel === null) {
9178
- throw new Error("effective follow-up model unexpectedly resolved to null");
9179
- }
9180
- try {
9181
- assertSessionAllowsProductModel(existingSession, effectiveModel);
9182
- } catch (error) {
9183
- if (error instanceof CodexCompactionV2ProviderLockedError) {
9184
- throw new HTTPException11(422, { message: error.message, cause: error });
9185
- }
9186
- throw error;
9187
- }
9188
- const sessionReasoningEffort = existingSession.reasoningEffort;
9189
- const effectiveReasoningEffort = input.reasoningEffort ?? sessionReasoningEffort;
9190
- const sessionLatencyMode = existingSession.latencyMode;
9191
- const effectiveLatencyMode = input.latencyMode ?? sessionLatencyMode;
9192
- const turnExecutionPolicy = resolveTurnExecutionPolicyV1(settings, {
9193
- modelId: effectiveModel,
9194
- requestedModelId: input.model ?? null,
9195
- modelSource: input.model == null ? "session" : "explicit",
9196
- reasoningEffort: effectiveReasoningEffort,
9197
- reasoningSource: input.reasoningEffort == null ? "session" : "explicit",
9198
- latencyMode: effectiveLatencyMode,
9199
- latencyModeSource: input.latencyMode == null ? "session" : "explicit"
9200
- });
9201
9759
  const requestedResources = normalizeResources(input.resources ?? []);
9202
9760
  const composerDraftResources = input.composerDraftResources ? normalizeResources(input.composerDraftResources) : void 0;
9203
- if (composerDraftResources) {
9204
- const acceptedResources = new Set(requestedResources.map((resource) => stableJson4(resource)));
9205
- const unacceptedDraftResource = composerDraftResources.find(
9206
- (resource) => !acceptedResources.has(stableJson4(resource))
9207
- );
9208
- if (unacceptedDraftResource) {
9209
- throw new HTTPException11(422, {
9210
- message: "composer draft resources must be included in the accepted resource set"
9211
- });
9212
- }
9213
- }
9214
- const annotations = await validateSubmittedTimelineAnnotations(
9215
- db,
9216
- workspaceId,
9217
- sessionId,
9218
- input.annotations ?? []
9219
- );
9220
- await requireLimit(deps, {
9221
- accountId: grant.accountId,
9222
- workspaceId,
9223
- action: "agent_run:create",
9224
- quantity: 1,
9225
- model: effectiveModel
9226
- });
9227
- if (requestedResources.some((resource) => resource.kind === "file") && !objectStorage) {
9228
- throw new HTTPException11(503, {
9229
- message: "object storage is not configured"
9230
- });
9231
- }
9232
- await validateFileResources(
9233
- db,
9234
- grant.accountId,
9235
- workspaceId,
9236
- grant.subjectId,
9237
- requestedResources
9238
- );
9239
- await validateGitHubRepositorySelection(db, workspaceId, [
9240
- ...existingSession.resources,
9241
- ...requestedResources
9242
- ]);
9243
- const mcpCredentialUpdates = validateSessionMcpCredentialUpdates({
9244
- settings,
9245
- grant,
9246
- session: existingSession,
9247
- updates: input.mcpCredentialUpdates ?? []
9248
- });
9249
- const connectionDelegationSource = personalConnectionDelegationSourceForGrant(grant);
9250
- const inheritedPersonalConnectionDelegations = connectionDelegationSource.kind === "turn" ? await getSessionTurnPersonalConnectionDelegations2(
9251
- db,
9252
- workspaceId,
9253
- connectionDelegationSource.sessionId,
9254
- connectionDelegationSource.turnId
9255
- ) : null;
9256
- const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
9257
- db,
9258
- workspaceId,
9259
- settings,
9260
- inheritedPersonalConnectionDelegations ? {
9261
- personalConnectionDelegations: inheritedPersonalConnectionDelegations
9262
- } : { subjectId: grant.subjectId }
9263
- );
9264
- const personalConnectionDelegations = await freezePersonalConnectionDelegations({
9265
- db,
9266
- workspaceId,
9267
- settings: runtimeSettings,
9268
- tools: existingSession.tools,
9269
- resources: [...existingSession.resources, ...requestedResources],
9270
- source: connectionDelegationSource,
9271
- targetSessionId: sessionId,
9272
- googleDrivePublicationEnabled: existingSession.firstPartyMcpTools.includes("editable_artifact_export") && existingSession.firstPartyMcpTools.includes("editable_artifact_export_status") && (!existingSession.firstPartyMcpPermissions?.length || existingSession.firstPartyMcpPermissions.includes("artifacts:read") && existingSession.firstPartyMcpPermissions.includes("artifacts:publish")),
9273
- atlassianEnabled: existingSession.firstPartyMcpTools.some((tool) => tool.startsWith("atlassian_")) && (!existingSession.firstPartyMcpPermissions?.length || existingSession.firstPartyMcpPermissions.includes("connections:read")),
9274
- ...input.connectionAuthorities ? { authoritySelections: input.connectionAuthorities } : {}
9275
- });
9276
- const { accepted, turn, draft, receipt: receipt2, routing, interruptionCount, replay } = await postUserMessageTurn({
9277
- db,
9278
- bus,
9279
- workflowClient,
9280
- settings,
9281
- accountId: grant.accountId,
9282
- workspaceId,
9283
- sessionId,
9761
+ const boundaryRequestHash = input.clientEventId ? sessionPromptBoundaryRequestHash({
9762
+ delivery,
9763
+ controlEtag: input.controlEtag ?? null,
9764
+ expectedDraftRevision: input.expectedDraftRevision ?? null,
9284
9765
  text: input.text,
9285
- annotations,
9766
+ annotations: input.annotations ?? [],
9286
9767
  modelContext: input.modelContext ?? null,
9287
9768
  resources: requestedResources,
9288
9769
  ...composerDraftResources ? { composerDraftResources } : {},
9289
9770
  model: input.model ?? null,
9290
9771
  reasoningEffort: input.reasoningEffort ?? null,
9291
9772
  latencyMode: input.latencyMode ?? null,
9292
- reasoningEffortFallback: sessionReasoningEffort,
9293
- turnExecutionPolicy,
9294
- mcpCredentialUpdates,
9295
- personalConnectionDelegations,
9773
+ source,
9774
+ mcpCredentialUpdates: input.mcpCredentialUpdates ?? [],
9775
+ ...input.connectionAuthorities ? { connectionAuthorities: input.connectionAuthorities } : {},
9296
9776
  ...input.personalResourceAttachment ? { personalResourceAttachment: input.personalResourceAttachment } : {},
9297
- delivery: input.delivery ?? "send",
9298
- origin: delegatedServiceInitiator ? "operator" : input.origin ?? "human",
9299
- actor: grant.subjectId,
9300
- ...grant.subjectLabel ? { actorLabel: grant.subjectLabel } : {},
9301
- ...delegatedServiceInitiator ? {
9302
- commandActor: {
9303
- type: "service",
9304
- subjectId: delegatedServiceInitiator.initiator.subjectId,
9305
- ...delegatedServiceInitiator.initiator.label ? { subjectLabel: delegatedServiceInitiator.initiator.label } : {},
9306
- context: delegatedServiceInitiator.context
9777
+ commandActor
9778
+ }) : null;
9779
+ if (input.clientEventId && boundaryRequestHash) {
9780
+ const replay = await withWorkspaceSubjectSessionActivityRls(
9781
+ db,
9782
+ workspaceId,
9783
+ grant.subjectId,
9784
+ async (scopedDb) => await replaySubmittedHumanPromptFromBoundaryReceipt(scopedDb, {
9785
+ workspaceId,
9786
+ sessionId,
9787
+ subjectId: grant.subjectId,
9788
+ actor: commandActor,
9789
+ operationKey: input.clientEventId,
9790
+ delivery,
9791
+ boundaryRequestHash,
9792
+ expectedDraftRevision: input.expectedDraftRevision ?? null
9793
+ })
9794
+ );
9795
+ if (replay) {
9796
+ return finalizePostUserMessageTurn(
9797
+ {
9798
+ db,
9799
+ bus,
9800
+ workflowClient,
9801
+ accountId: grant.accountId,
9802
+ workspaceId,
9803
+ sessionId,
9804
+ delivery,
9805
+ ...deps.schedulePromptPostCommit ? { schedulePostCommit: deps.schedulePromptPostCommit } : {}
9806
+ },
9807
+ replay
9808
+ );
9809
+ }
9810
+ }
9811
+ try {
9812
+ await requireAtomicPersonalResourceAttachment(
9813
+ deps,
9814
+ input.authorization,
9815
+ workspaceId,
9816
+ input.personalResourceAttachment,
9817
+ true
9818
+ );
9819
+ const existingSession = await requireSession2(db, workspaceId, sessionId);
9820
+ const settings = await resolveWorkspaceModelBoundarySettings(
9821
+ deps,
9822
+ grant,
9823
+ workspaceId,
9824
+ [input.model ?? existingSession.model],
9825
+ existingSession.model
9826
+ );
9827
+ if (settings !== deps.settings) {
9828
+ deps = {
9829
+ ...deps,
9830
+ catalogSourceSettings: deps.catalogSourceSettings ?? deps.settings,
9831
+ settings
9832
+ };
9833
+ }
9834
+ const requestedModel = canonicalConfiguredModel(settings, input.model ?? null) ?? null;
9835
+ const effectiveModel = canonicalConfiguredModel(settings, requestedModel ?? existingSession.model) ?? null;
9836
+ if (effectiveModel === null) {
9837
+ throw new Error("effective follow-up model unexpectedly resolved to null");
9838
+ }
9839
+ await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, requestedModel);
9840
+ try {
9841
+ assertSessionAllowsProductModel(existingSession, effectiveModel);
9842
+ } catch (error) {
9843
+ if (error instanceof CodexCompactionV2ProviderLockedError) {
9844
+ throw new HTTPException11(422, { message: error.message, cause: error });
9307
9845
  }
9308
- } : {},
9309
- ...input.controlEtag !== void 0 ? { controlEtag: input.controlEtag } : {},
9310
- ...input.expectedDraftRevision !== void 0 ? { expectedDraftRevision: input.expectedDraftRevision } : {},
9311
- ...input.clientEventId ? { clientEventId: input.clientEventId } : {},
9312
- recordAgentRunUsage: true,
9313
- ...deps.schedulePromptPostCommit ? { schedulePostCommit: deps.schedulePromptPostCommit } : {}
9314
- });
9315
- return { accepted, turn, draft, receipt: receipt2, routing, interruptionCount, replay };
9846
+ throw error;
9847
+ }
9848
+ const sessionReasoningEffort = existingSession.reasoningEffort;
9849
+ const effectiveReasoningEffort = input.reasoningEffort ?? sessionReasoningEffort;
9850
+ const sessionLatencyMode = existingSession.latencyMode;
9851
+ const effectiveLatencyMode = input.latencyMode ?? sessionLatencyMode;
9852
+ const turnExecutionPolicy = resolveTurnExecutionPolicyV1(settings, {
9853
+ modelId: effectiveModel,
9854
+ requestedModelId: input.model ?? null,
9855
+ modelSource: input.model == null ? "session" : "explicit",
9856
+ reasoningEffort: effectiveReasoningEffort,
9857
+ reasoningSource: input.reasoningEffort == null ? "session" : "explicit",
9858
+ latencyMode: effectiveLatencyMode,
9859
+ latencyModeSource: input.latencyMode == null ? "session" : "explicit"
9860
+ });
9861
+ if (composerDraftResources) {
9862
+ const acceptedResources = new Set(requestedResources.map((resource) => stableJson4(resource)));
9863
+ const unacceptedDraftResource = composerDraftResources.find(
9864
+ (resource) => !acceptedResources.has(stableJson4(resource))
9865
+ );
9866
+ if (unacceptedDraftResource) {
9867
+ throw new HTTPException11(422, {
9868
+ message: "composer draft resources must be included in the accepted resource set"
9869
+ });
9870
+ }
9871
+ }
9872
+ const annotations = await validateSubmittedTimelineAnnotations(
9873
+ db,
9874
+ workspaceId,
9875
+ sessionId,
9876
+ input.annotations ?? []
9877
+ );
9878
+ await requireLimit(deps, {
9879
+ accountId: grant.accountId,
9880
+ workspaceId,
9881
+ action: "agent_run:create",
9882
+ quantity: 1,
9883
+ model: effectiveModel
9884
+ });
9885
+ if (requestedResources.some((resource) => resource.kind === "file") && !objectStorage) {
9886
+ throw new HTTPException11(503, {
9887
+ message: "object storage is not configured"
9888
+ });
9889
+ }
9890
+ await validateFileResources(
9891
+ db,
9892
+ grant.accountId,
9893
+ workspaceId,
9894
+ grant.subjectId,
9895
+ requestedResources
9896
+ );
9897
+ await validateGitHubRepositorySelection(db, workspaceId, [
9898
+ ...existingSession.resources,
9899
+ ...requestedResources
9900
+ ]);
9901
+ const mcpCredentialUpdates = validateSessionMcpCredentialUpdates({
9902
+ settings,
9903
+ grant,
9904
+ session: existingSession,
9905
+ updates: input.mcpCredentialUpdates ?? []
9906
+ });
9907
+ const connectionDelegationSource = personalConnectionDelegationSourceForGrant(grant);
9908
+ const inheritedPersonalConnectionDelegations = connectionDelegationSource.kind === "turn" ? await getSessionTurnPersonalConnectionDelegations2(
9909
+ db,
9910
+ workspaceId,
9911
+ connectionDelegationSource.sessionId,
9912
+ connectionDelegationSource.turnId
9913
+ ) : null;
9914
+ const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
9915
+ db,
9916
+ workspaceId,
9917
+ settings,
9918
+ inheritedPersonalConnectionDelegations ? {
9919
+ personalConnectionDelegations: inheritedPersonalConnectionDelegations
9920
+ } : { subjectId: grant.subjectId }
9921
+ );
9922
+ const personalConnectionDelegations = await freezePersonalConnectionDelegations({
9923
+ db,
9924
+ workspaceId,
9925
+ settings: runtimeSettings,
9926
+ tools: existingSession.tools,
9927
+ resources: [...existingSession.resources, ...requestedResources],
9928
+ source: connectionDelegationSource,
9929
+ targetSessionId: sessionId,
9930
+ googleDrivePublicationEnabled: existingSession.firstPartyMcpTools.includes("editable_artifact_export") && existingSession.firstPartyMcpTools.includes("editable_artifact_export_status") && (!existingSession.firstPartyMcpPermissions?.length || existingSession.firstPartyMcpPermissions.includes("artifacts:read") && existingSession.firstPartyMcpPermissions.includes("artifacts:publish")),
9931
+ atlassianEnabled: existingSession.firstPartyMcpTools.some((tool) => tool.startsWith("atlassian_")) && (!existingSession.firstPartyMcpPermissions?.length || existingSession.firstPartyMcpPermissions.includes("connections:read")),
9932
+ ...input.connectionAuthorities ? { authoritySelections: input.connectionAuthorities } : {}
9933
+ });
9934
+ const { accepted, turn, draft, receipt: receipt2, routing, interruptionCount, replay } = await postUserMessageTurn({
9935
+ db,
9936
+ bus,
9937
+ workflowClient,
9938
+ settings,
9939
+ accountId: grant.accountId,
9940
+ workspaceId,
9941
+ sessionId,
9942
+ text: input.text,
9943
+ annotations,
9944
+ modelContext: input.modelContext ?? null,
9945
+ resources: requestedResources,
9946
+ ...composerDraftResources ? { composerDraftResources } : {},
9947
+ model: input.model ?? null,
9948
+ reasoningEffort: input.reasoningEffort ?? null,
9949
+ latencyMode: input.latencyMode ?? null,
9950
+ reasoningEffortFallback: sessionReasoningEffort,
9951
+ turnExecutionPolicy,
9952
+ mcpCredentialUpdates,
9953
+ personalConnectionDelegations,
9954
+ ...input.personalResourceAttachment ? { personalResourceAttachment: input.personalResourceAttachment } : {},
9955
+ delivery,
9956
+ origin: source === "api" ? "operator" : "human",
9957
+ actor: grant.subjectId,
9958
+ ...grant.subjectLabel ? { actorLabel: grant.subjectLabel } : {},
9959
+ commandActor,
9960
+ ...boundaryRequestHash ? { boundaryRequestHash } : {},
9961
+ ...input.controlEtag !== void 0 ? { controlEtag: input.controlEtag } : {},
9962
+ ...input.expectedDraftRevision !== void 0 ? { expectedDraftRevision: input.expectedDraftRevision } : {},
9963
+ ...input.clientEventId ? { clientEventId: input.clientEventId } : {},
9964
+ recordAgentRunUsage: true,
9965
+ ...deps.schedulePromptPostCommit ? { schedulePostCommit: deps.schedulePromptPostCommit } : {}
9966
+ });
9967
+ return { accepted, turn, draft, receipt: receipt2, routing, interruptionCount, replay };
9968
+ } catch (error) {
9969
+ if (input.clientEventId && boundaryRequestHash) {
9970
+ const replay = await withWorkspaceSubjectSessionActivityRls(
9971
+ db,
9972
+ workspaceId,
9973
+ grant.subjectId,
9974
+ async (scopedDb) => await replaySubmittedHumanPromptFromBoundaryReceipt(scopedDb, {
9975
+ workspaceId,
9976
+ sessionId,
9977
+ subjectId: grant.subjectId,
9978
+ actor: commandActor,
9979
+ operationKey: input.clientEventId,
9980
+ delivery,
9981
+ boundaryRequestHash,
9982
+ expectedDraftRevision: input.expectedDraftRevision ?? null,
9983
+ serializeOperation: true
9984
+ })
9985
+ );
9986
+ if (replay) {
9987
+ return finalizePostUserMessageTurn(
9988
+ {
9989
+ db,
9990
+ bus,
9991
+ workflowClient,
9992
+ accountId: grant.accountId,
9993
+ workspaceId,
9994
+ sessionId,
9995
+ delivery,
9996
+ ...deps.schedulePromptPostCommit ? { schedulePostCommit: deps.schedulePromptPostCommit } : {}
9997
+ },
9998
+ replay
9999
+ );
10000
+ }
10001
+ }
10002
+ throw error;
10003
+ }
9316
10004
  }
9317
10005
  async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, input) {
9318
10006
  const { accepted, turn, receipt: receipt2, routing, interruptionCount, replay } = await acceptSessionUserMessageWithOutcome(deps, grant, workspaceId, sessionId, input);
@@ -9637,6 +10325,26 @@ function scheduledTaskToolsProvided(rawPayload) {
9637
10325
  agentConfig && typeof agentConfig === "object" && Object.prototype.hasOwnProperty.call(agentConfig, "tools")
9638
10326
  );
9639
10327
  }
10328
+ function workspaceCustomModelCommitGuard(input) {
10329
+ const reference = workspaceCustomModelReference(input.settings, input.modelId);
10330
+ if (!reference) return void 0;
10331
+ return async (tx) => {
10332
+ const active = reference.providerKind === "openrouter" ? await lockActiveWorkspaceOpenRouterCustomModelForAdmission2(tx, {
10333
+ accountId: input.accountId,
10334
+ workspaceId: input.workspaceId,
10335
+ upstreamModelId: reference.upstreamModelId
10336
+ }) : await lockActiveWorkspaceGatewayCustomModelForAdmission2(tx, {
10337
+ accountId: input.accountId,
10338
+ workspaceId: input.workspaceId,
10339
+ upstreamModelId: reference.upstreamModelId
10340
+ });
10341
+ if (!active) {
10342
+ throw new HTTPException12(422, {
10343
+ message: `model is not available: ${input.modelId}`
10344
+ });
10345
+ }
10346
+ };
10347
+ }
9640
10348
  function scheduledConnectionSurfaceEligibility(settings, target) {
9641
10349
  const tools = target?.firstPartyMcpTools ?? resolveFirstPartyMcpToolPolicy2(settings).default;
9642
10350
  const permissions = target?.firstPartyMcpPermissions?.length ? target.firstPartyMcpPermissions : DEFAULT_FIRST_PARTY_MCP_PERMISSIONS2;
@@ -9728,6 +10436,12 @@ async function createValidatedScheduledTask(input) {
9728
10436
  workspaceId: input.grant.workspaceId,
9729
10437
  subjectId: input.grant.subjectId
9730
10438
  });
10439
+ const beforeCreateCommit = !knowledgeAction && input.payload.runMode !== "existing_session" ? workspaceCustomModelCommitGuard({
10440
+ settings: input.settings,
10441
+ accountId: input.grant.accountId,
10442
+ workspaceId: input.grant.workspaceId,
10443
+ modelId: agentConfig.model ?? input.settings.openaiModel
10444
+ }) : void 0;
9731
10445
  return await withScheduledTaskAuthorityWriteErrors(
9732
10446
  () => createScheduledTask(input.db, {
9733
10447
  id,
@@ -9749,7 +10463,8 @@ async function createValidatedScheduledTask(input) {
9749
10463
  targetSessionId: target?.id ?? null,
9750
10464
  variableSetId: input.payload.variableSetId ?? null,
9751
10465
  rigId: input.payload.rigId ?? null,
9752
- metadata: input.payload.metadata
10466
+ metadata: input.payload.metadata,
10467
+ ...beforeCreateCommit ? { beforeCreateCommit } : {}
9753
10468
  })
9754
10469
  );
9755
10470
  }
@@ -10075,6 +10790,15 @@ async function validatedScheduledTaskUpdate(input) {
10075
10790
  const nextAgentConfig = update.agentConfig ?? input.existing.agentConfig;
10076
10791
  const authorityTargetChanged = nextRunMode !== input.existing.runMode || nextTargetSessionId !== input.existing.targetSessionId || input.payload.variableSetId !== void 0 && input.payload.variableSetId !== input.existing.variableSetId || input.payload.rigId !== void 0 && input.payload.rigId !== input.existing.rigId;
10077
10792
  const materialExecutionChange = authorityTargetChanged || input.payload.connectionAuthorities !== void 0 || !isDeepStrictEqual(nextAgentConfig, input.existing.agentConfig) || input.payload.action !== void 0 && !isDeepStrictEqual(input.payload.action, input.existing.action) || input.payload.schedule !== void 0 && !isDeepStrictEqual(input.payload.schedule, input.existing.schedule) || input.payload.overlapPolicy !== void 0 && input.payload.overlapPolicy !== input.existing.overlapPolicy || input.payload.metadata !== void 0 && !isDeepStrictEqual(input.payload.metadata, input.existing.metadata) || input.existing.status === "paused" && input.payload.status === "active";
10793
+ if (materialExecutionChange && nextRunMode !== "existing_session") {
10794
+ const beforeUpdateCommit = workspaceCustomModelCommitGuard({
10795
+ settings: input.settings,
10796
+ accountId: input.existing.accountId,
10797
+ workspaceId: input.existing.workspaceId,
10798
+ modelId: nextAgentConfig.model ?? input.settings.openaiModel
10799
+ });
10800
+ if (beforeUpdateCommit) update.beforeUpdateCommit = beforeUpdateCommit;
10801
+ }
10078
10802
  const existingXaiAuthority = await getScheduledTaskXaiProviderAccountAuthoritySnapshot(
10079
10803
  input.db,
10080
10804
  input.existing.workspaceId,
@@ -14575,6 +15299,7 @@ export {
14575
15299
  MEMORY_SLACK_PROJECTION_MAX_UTF8_BYTES,
14576
15300
  MEMORY_SLACK_PROJECTION_VERSION,
14577
15301
  MEMORY_SLACK_SUMMARY_MAX_UTF8_BYTES,
15302
+ MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS,
14578
15303
  ManagedAuthActorLeaseOutcomeUnknownError,
14579
15304
  MathEditableArtifactOutboxRandom,
14580
15305
  OPENGENI_PR_REVIEW_AGENT_INSTRUCTIONS,
@@ -14777,6 +15502,9 @@ export {
14777
15502
  isTerminalVideoGenerationState,
14778
15503
  isTrustedScheduledSlackBotSession,
14779
15504
  isUserMember,
15505
+ isWorkspaceCustomModelId,
15506
+ isWorkspaceGatewayCustomModelId,
15507
+ isWorkspaceOpenRouterCustomModelId,
14780
15508
  issueManagedHumanUserResourceGrant,
14781
15509
  legacySandboxRuntimeFromPacks,
14782
15510
  listCapabilityPacks,
@@ -14797,6 +15525,7 @@ export {
14797
15525
  mergeResourceRefs,
14798
15526
  mergeRigDefaultVariableSetEnvironment,
14799
15527
  mergeToolRefs,
15528
+ modelFundingForAdmission,
14800
15529
  moveHumanQueuePrompt,
14801
15530
  nativeConnectionCapabilityRecommendations,
14802
15531
  normalizeConversationActor,
@@ -14879,6 +15608,7 @@ export {
14879
15608
  requireVariableSetEncryption,
14880
15609
  requireVariableSetForApi,
14881
15610
  resolveCapabilityPack,
15611
+ resolveCatalogSettings,
14882
15612
  resolveChildGoalFromAcceptedSnapshot,
14883
15613
  resolveCodexAppsCredentialIdForRun,
14884
15614
  resolveFikenDefaultCompanySlug,
@@ -14891,7 +15621,9 @@ export {
14891
15621
  resolveSessionSandboxRuntime,
14892
15622
  resolveSessionToolPolicy,
14893
15623
  resolveSkillImport,
15624
+ resolveWorkspaceCatalogSettings,
14894
15625
  resolveWorkspaceLegacyRuntimePacks,
15626
+ resolveWorkspaceModelSelection,
14895
15627
  restoreScheduledTask,
14896
15628
  retainedProcessBackgroundSettlement,
14897
15629
  revokeManagedHumanUserResourceGrant,
@@ -14978,6 +15710,7 @@ export {
14978
15710
  withScheduledTaskAuthorityWriteErrors,
14979
15711
  withWorkspaceDefaultMcpTools,
14980
15712
  workflowIdForSession,
15713
+ workspaceCustomModelReference,
14981
15714
  workspaceSessionToolPolicyDefaultServerIds,
14982
15715
  workspaceSessionToolPolicyServerIds,
14983
15716
  wrapChannelABoxWithRouting