@opengeni/api-router 4.0.0 → 4.1.0-canary.35916963870001

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.
Files changed (37) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/{chunk-E7TZFJ65.js → chunk-PHQ7C36N.js} +983 -597
  3. package/dist/{chunk-E7TZFJ65.js.map → chunk-PHQ7C36N.js.map} +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/integrations/github-lens-connect.d.ts +1 -1
  6. package/dist/integrations/oauth-client.d.ts +1 -1
  7. package/dist/integrations/slack-bot.d.ts +8 -0
  8. package/dist/integrations/slack-interactions.d.ts +1 -1
  9. package/dist/mcp/child-read-evidence.d.ts +9 -0
  10. package/dist/mcp/documents.d.ts +2 -0
  11. package/dist/mcp/first-party-tool-permissions.d.ts +3 -0
  12. package/dist/mcp/session-wait.d.ts +2 -0
  13. package/dist/routes/codex.d.ts +2 -1
  14. package/package.json +19 -19
  15. package/src/integrations/oauth-client.ts +68 -5
  16. package/src/integrations/slack-bot.ts +32 -3
  17. package/src/integrations/slack-interactions.ts +79 -8
  18. package/src/mcp/child-read-evidence.ts +19 -0
  19. package/src/mcp/documents.ts +9 -2
  20. package/src/mcp/first-party-tool-permissions.ts +1 -0
  21. package/src/mcp/knowledge-entries.ts +15 -2
  22. package/src/mcp/server.ts +226 -103
  23. package/src/mcp/session-wait.ts +17 -0
  24. package/src/routes/browser-sessions.ts +42 -0
  25. package/src/routes/codex.ts +19 -12
  26. package/src/routes/computer-sessions.ts +18 -1
  27. package/src/routes/documents.ts +1 -0
  28. package/src/routes/knowledge.ts +19 -0
  29. package/src/routes/managed-onboarding.ts +3 -0
  30. package/src/routes/organization-memberships.ts +3 -0
  31. package/src/routes/skill-content.ts +21 -5
  32. package/src/sandbox/channel-a.ts +8 -0
  33. package/src/sandbox/machines.ts +9 -8
  34. package/src/sandbox/viewer.ts +16 -3
  35. package/src/sandbox-file-artifacts.ts +15 -1
  36. package/src/site-uploads.ts +19 -4
  37. package/src/workspace-tool-gateway.ts +1 -0
@@ -267,6 +267,7 @@ var FIRST_PARTY_TOOL_AUTHORIZATION = {
267
267
  run_on: { sessionRequired: true, allOf: ["sessions:control"] },
268
268
  sandbox_provision: { sessionRequired: true, allOf: ["sessions:control"] },
269
269
  connected_machine_remove: { allOf: ["enrollments:manage"] },
270
+ connected_machine_enroll_token: { allOf: ["enrollments:manage"] },
270
271
  rig_list: { allOf: ["rigs:use"] },
271
272
  rig_get: { allOf: ["rigs:use"] },
272
273
  rig_propose_change: { allOf: ["rigs:use"] },
@@ -649,8 +650,8 @@ import { SiteSessionPathError, OrganizationIntegrationDeniedError as Organizatio
649
650
 
650
651
  // src/routes/model-connection-access.ts
651
652
  import {
652
- configuredModels as configuredModels2,
653
- withCodexCatalogProvider,
653
+ configuredModels as configuredModels3,
654
+ withCodexCatalogProvider as withCodexCatalogProvider2,
654
655
  withXaiSubscriptionCatalogProvider as withXaiSubscriptionCatalogProvider2,
655
656
  withOrganizationGatewayCatalogProvider,
656
657
  withOrganizationOpenRouterCatalogProvider
@@ -673,14 +674,17 @@ import { HTTPException as HTTPException4 } from "hono/http-exception";
673
674
  import { z as z5 } from "zod";
674
675
 
675
676
  // src/routes/codex.ts
676
- import { environmentsEncryptionKeyBytes, productLabelForModelId } from "@opengeni/config";
677
+ import {
678
+ environmentsEncryptionKeyBytes,
679
+ configuredModels,
680
+ getSettings,
681
+ withCodexCatalogProvider
682
+ } from "@opengeni/config";
677
683
  import {
678
684
  accessTokenExpiry,
679
685
  buildCodexUsageWindowFromCache,
680
686
  CODEX_CLIENT_VERSION,
681
- CODEX_FALLBACK_MODEL_SLUGS,
682
687
  CODEX_FIVE_HOUR_WINDOW_SECONDS,
683
- CODEX_MODEL_ID_PREFIX,
684
688
  CODEX_PROVIDER_ID,
685
689
  CODEX_WEEKLY_WINDOW_SECONDS,
686
690
  CodexDeviceError,
@@ -741,7 +745,8 @@ import {
741
745
  getManagedSession,
742
746
  hasPermission as hasPermission2,
743
747
  requireAccessGrant as requireAccessGrant2,
744
- requireCanonicalLocalAccountAdministrator
748
+ requireCanonicalLocalAccountAdministrator,
749
+ resolveCatalogSettings
745
750
  } from "@opengeni/core";
746
751
  import { HTTPException as HTTPException2 } from "hono/http-exception";
747
752
  import * as z3 from "zod/v4";
@@ -863,10 +868,10 @@ function codexWorkerReadiness(input) {
863
868
  function codexUsageJson(payload) {
864
869
  return { status: payload.status, usage: payload };
865
870
  }
866
- function codexModelsForPicker() {
867
- return CODEX_FALLBACK_MODEL_SLUGS.map((slug) => ({
868
- id: `${CODEX_MODEL_ID_PREFIX}${slug}`,
869
- label: productLabelForModelId(slug),
871
+ function codexModelsForPicker(settings = getSettings()) {
872
+ return configuredModels(withCodexCatalogProvider(settings)).filter((model) => model.providerId === CODEX_PROVIDER_ID).map((model) => ({
873
+ id: model.id,
874
+ label: model.label,
870
875
  provider: CODEX_PROVIDER_ID,
871
876
  providerLabel: CODEX_PROVIDER_LABEL,
872
877
  api: "responses"
@@ -1687,7 +1692,7 @@ function registerCodexRoutes(app, deps) {
1687
1692
  now: /* @__PURE__ */ new Date()
1688
1693
  });
1689
1694
  let valid = false;
1690
- const models = codexModelsForPicker();
1695
+ const models = codexModelsForPicker((await resolveCatalogSettings(db, settings)).settings);
1691
1696
  let catalogError = null;
1692
1697
  try {
1693
1698
  const cred = status?.credentialId ? await loadCodexCredentialForRun(db, settings, workspaceId, status.credentialId) : null;
@@ -2471,7 +2476,7 @@ import {
2471
2476
  updateOrganizationXaiRotation
2472
2477
  } from "@opengeni/db";
2473
2478
  import {
2474
- configuredModels,
2479
+ configuredModels as configuredModels2,
2475
2480
  environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes2,
2476
2481
  withXaiSubscriptionCatalogProvider
2477
2482
  } from "@opengeni/config";
@@ -3184,7 +3189,7 @@ function registerSuperGrokRoutes(app, deps) {
3184
3189
  valid = false;
3185
3190
  }
3186
3191
  const catalogSettings = valid ? (await deps.resolveCatalogSettings()).settings : null;
3187
- const catalog = catalogSettings ? configuredModels(withXaiSubscriptionCatalogProvider(catalogSettings)).filter(
3192
+ const catalog = catalogSettings ? configuredModels2(withXaiSubscriptionCatalogProvider(catalogSettings)).filter(
3188
3193
  (model) => model.credentialSource.kind === "connected_subscription" && model.credentialSource.provider === "xai"
3189
3194
  ).map(projectClientModel) : [];
3190
3195
  return c.json({
@@ -3426,7 +3431,7 @@ function registerModelConnectionAccessRoutes(app, deps) {
3426
3431
  accountId: connection.accountId,
3427
3432
  workspaceId: connection.workspaceId
3428
3433
  })).settings;
3429
- settings = withCodexCatalogProvider(withXaiSubscriptionCatalogProvider2(settings));
3434
+ settings = withCodexCatalogProvider2(withXaiSubscriptionCatalogProvider2(settings));
3430
3435
  let workspaces = [];
3431
3436
  if (connection.workspaceId === null) {
3432
3437
  const actor = {
@@ -3448,7 +3453,7 @@ function registerModelConnectionAccessRoutes(app, deps) {
3448
3453
  ModelConnectionAccessResponse.parse({
3449
3454
  policy,
3450
3455
  workspaces,
3451
- models: configuredModels2(settings).filter((model) => model.id.startsWith(modelPrefix(connection))).map(({ id, label }) => ({ id, label })),
3456
+ models: configuredModels3(settings).filter((model) => model.id.startsWith(modelPrefix(connection))).map(({ id, label }) => ({ id, label })),
3452
3457
  personalWorkspacesSupported: connection.workspaceId === null && (connection.kind === "codex" || connection.kind === "supergrok")
3453
3458
  })
3454
3459
  );
@@ -3482,10 +3487,10 @@ import {
3482
3487
  canonicalizeConfiguredModelId as canonicalizeConfiguredModelId2,
3483
3488
  configuredAllowedModels,
3484
3489
  configuredAllowedReasoningEfforts,
3485
- configuredModels as configuredModels3,
3490
+ configuredModels as configuredModels4,
3486
3491
  resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy2,
3487
3492
  resolveVoiceInputProviderRegistry as resolveVoiceInputProviderRegistry2,
3488
- withCodexCatalogProvider as withCodexCatalogProvider2,
3493
+ withCodexCatalogProvider as withCodexCatalogProvider3,
3489
3494
  withXaiSubscriptionCatalogProvider as withXaiSubscriptionCatalogProvider3
3490
3495
  } from "@opengeni/config";
3491
3496
  import {
@@ -3600,7 +3605,7 @@ import {
3600
3605
  requireAccessGrantAuthorization as requireAccessGrantAuthorization36,
3601
3606
  requirePermission as requirePermission9,
3602
3607
  releaseManagedAuthRequestActorLease,
3603
- resolveCatalogSettings,
3608
+ resolveCatalogSettings as resolveCatalogSettings2,
3604
3609
  resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings10,
3605
3610
  validateManagedAuthRequestActorLease,
3606
3611
  requireSessionAuthorization as requireSessionAuthorization16,
@@ -5889,7 +5894,296 @@ import {
5889
5894
  deleteChannel,
5890
5895
  setSessionChannel
5891
5896
  } from "@opengeni/db";
5892
- import { createHash as createHash11, randomUUID as randomUUID2 } from "crypto";
5897
+ import { createHash as createHash12, randomUUID as randomUUID3 } from "crypto";
5898
+
5899
+ // src/sandbox/enrollment.ts
5900
+ import { randomBytes } from "crypto";
5901
+ import {
5902
+ resolveEnrollmentSigningSecret,
5903
+ resolveRelayTokenSecret
5904
+ } from "@opengeni/config";
5905
+ import {
5906
+ DeviceEnrollmentState,
5907
+ signEnrollmentBearer,
5908
+ signEnrollToken,
5909
+ signRelayToken,
5910
+ verifyEnrollToken
5911
+ } from "@opengeni/contracts";
5912
+ import {
5913
+ approveDeviceEnrollmentRequest,
5914
+ consumeDeviceEnrollmentRequest,
5915
+ createDeviceEnrollmentRequest,
5916
+ denyDeviceEnrollmentRequest,
5917
+ finalizeEnrollmentByToken,
5918
+ getDeviceEnrollmentRequestByDeviceCode,
5919
+ getEnrollment,
5920
+ getPendingDeviceEnrollmentRequestByUserCode,
5921
+ getPendingDeviceEnrollmentRequestByUserCodeGlobal
5922
+ } from "@opengeni/db";
5923
+ import { relayDialBaseFromSettings } from "@opengeni/core";
5924
+ var DEVICE_CODE_TTL_SECONDS = 600;
5925
+ var DEVICE_POLL_INTERVAL_SECONDS = 5;
5926
+ var ENROLLMENT_BEARER_TTL_SECONDS = 30 * 24 * 3600;
5927
+ var RELAY_TOKEN_TTL_SECONDS = 30 * 24 * 3600;
5928
+ var ENROLL_TOKEN_TTL_SECONDS = 3600;
5929
+ function mintDeviceCode() {
5930
+ return randomBytes(32).toString("base64url");
5931
+ }
5932
+ var USER_CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
5933
+ function mintUserCode() {
5934
+ const bytes = randomBytes(8);
5935
+ let out = "";
5936
+ for (let i = 0; i < 8; i += 1) {
5937
+ out += USER_CODE_ALPHABET[bytes[i] % USER_CODE_ALPHABET.length];
5938
+ }
5939
+ return `${out.slice(0, 4)}-${out.slice(4, 8)}`;
5940
+ }
5941
+ async function startDeviceEnrollment(services, input) {
5942
+ const { db } = services;
5943
+ const expiresAt = new Date(Date.now() + DEVICE_CODE_TTL_SECONDS * 1e3);
5944
+ let request;
5945
+ let lastError;
5946
+ for (let attempt = 0; attempt < 5 && !request; attempt += 1) {
5947
+ const deviceCode = mintDeviceCode();
5948
+ const userCode = mintUserCode();
5949
+ try {
5950
+ request = await createDeviceEnrollmentRequest(db, {
5951
+ accountId: input.accountId,
5952
+ workspaceId: input.workspaceId,
5953
+ deviceCode,
5954
+ userCode,
5955
+ pubkey: input.publicKey,
5956
+ os: input.os,
5957
+ arch: input.arch,
5958
+ machineName: input.machineName ?? null,
5959
+ requestedExposure: "whole-machine",
5960
+ canOfferDisplay: input.canOfferDisplay,
5961
+ requestsScreenControl: input.requestsScreenControl,
5962
+ expiresAt
5963
+ });
5964
+ } catch (error) {
5965
+ lastError = error;
5966
+ }
5967
+ }
5968
+ if (!request) {
5969
+ throw lastError instanceof Error ? lastError : new Error("failed to start device enrollment");
5970
+ }
5971
+ const base = input.verificationOrigin.replace(/\/+$/, "");
5972
+ const verificationUri = `${base}/device`;
5973
+ const verificationUriComplete = `${verificationUri}?user_code=${encodeURIComponent(request.userCode)}`;
5974
+ return {
5975
+ deviceCode: request.deviceCode,
5976
+ userCode: request.userCode,
5977
+ verificationUri,
5978
+ verificationUriComplete,
5979
+ intervalSeconds: DEVICE_POLL_INTERVAL_SECONDS,
5980
+ expiresInSeconds: DEVICE_CODE_TTL_SECONDS
5981
+ };
5982
+ }
5983
+ async function approveDeviceEnrollment(services, input) {
5984
+ const { db } = services;
5985
+ const pending = await getPendingDeviceEnrollmentRequestByUserCode(
5986
+ db,
5987
+ input.workspaceId,
5988
+ input.userCode
5989
+ );
5990
+ if (!pending) {
5991
+ return null;
5992
+ }
5993
+ const sandboxName = (pending.machineName?.trim() || `${pending.os} machine`).slice(0, 256);
5994
+ const result = await approveDeviceEnrollmentRequest(db, {
5995
+ accountId: input.accountId,
5996
+ workspaceId: input.workspaceId,
5997
+ ...input.scope ? { scope: input.scope } : {},
5998
+ allowOrganization: input.allowOrganization === true,
5999
+ requestId: pending.id,
6000
+ allowScreenControl: input.allowScreenControl,
6001
+ approvedBySubjectId: input.approvedBySubjectId,
6002
+ approvedBySubjectLabel: input.approvedBySubjectLabel ?? null,
6003
+ sandboxName
6004
+ });
6005
+ if (!result.approved || !result.enrollment || !result.sandbox) {
6006
+ return null;
6007
+ }
6008
+ return {
6009
+ enrollmentId: result.enrollment.id,
6010
+ sandboxId: result.sandbox.id,
6011
+ allowScreenControl: result.enrollment.allowScreenControl
6012
+ };
6013
+ }
6014
+ async function lookupDeviceEnrollment(services, input) {
6015
+ const { db } = services;
6016
+ return await getPendingDeviceEnrollmentRequestByUserCodeGlobal(db, input.userCode);
6017
+ }
6018
+ function toLookupResponse(record6) {
6019
+ return {
6020
+ workspaceId: record6.workspaceId,
6021
+ userCode: record6.userCode,
6022
+ machine: {
6023
+ machineName: record6.machineName,
6024
+ os: record6.os,
6025
+ arch: record6.arch,
6026
+ canOfferDisplay: record6.canOfferDisplay,
6027
+ requestsScreenControl: record6.requestsScreenControl
6028
+ },
6029
+ expiresAt: record6.expiresAt
6030
+ };
6031
+ }
6032
+ async function denyDeviceEnrollment(services, input) {
6033
+ const { db } = services;
6034
+ const pending = await getPendingDeviceEnrollmentRequestByUserCode(
6035
+ db,
6036
+ input.workspaceId,
6037
+ input.userCode
6038
+ );
6039
+ if (!pending) {
6040
+ return { denied: false };
6041
+ }
6042
+ return await denyDeviceEnrollmentRequest(db, {
6043
+ accountId: input.accountId,
6044
+ workspaceId: input.workspaceId,
6045
+ requestId: pending.id
6046
+ });
6047
+ }
6048
+ async function mintEnrollToken(services, input) {
6049
+ const { settings } = services;
6050
+ const secret = resolveEnrollmentSigningSecret(settings);
6051
+ if (!secret) {
6052
+ return null;
6053
+ }
6054
+ const nowSeconds = Math.floor(Date.now() / 1e3);
6055
+ const exp = nowSeconds + ENROLL_TOKEN_TTL_SECONDS;
6056
+ const token = await signEnrollToken(secret, {
6057
+ typ: "enroll",
6058
+ workspaceId: input.workspaceId,
6059
+ accountId: input.accountId,
6060
+ allowScreenControl: input.allowScreenControl,
6061
+ iat: nowSeconds,
6062
+ exp
6063
+ });
6064
+ return {
6065
+ token,
6066
+ expiresAt: new Date(exp * 1e3).toISOString(),
6067
+ expiresInSeconds: ENROLL_TOKEN_TTL_SECONDS
6068
+ };
6069
+ }
6070
+ async function exchangeEnrollToken(services, input) {
6071
+ const { db, settings } = services;
6072
+ const secret = resolveEnrollmentSigningSecret(settings);
6073
+ if (!secret) {
6074
+ return { ok: false, reason: "disabled" };
6075
+ }
6076
+ const claims = await verifyEnrollToken(secret, input.token);
6077
+ if (!claims) {
6078
+ return { ok: false, reason: "invalid" };
6079
+ }
6080
+ const sandboxName = (input.machineName?.trim() || `${input.os} machine`).slice(0, 256);
6081
+ const { enrollment } = await finalizeEnrollmentByToken(db, {
6082
+ accountId: claims.accountId,
6083
+ workspaceId: claims.workspaceId,
6084
+ pubkey: input.publicKey,
6085
+ hasDisplay: input.canOfferDisplay,
6086
+ // The token's allowScreenControl is the AUTHORITATIVE consent (NOT the agent's
6087
+ // requestsScreenControl) — it was baked in at mint by the authorizing user.
6088
+ allowScreenControl: claims.allowScreenControl,
6089
+ os: input.os,
6090
+ arch: input.arch,
6091
+ sandboxName
6092
+ });
6093
+ const credentials = await buildEnrollmentCredentials(services, {
6094
+ secret,
6095
+ workspaceId: claims.workspaceId,
6096
+ agentId: enrollment.id,
6097
+ credentialGeneration: enrollment.credentialGeneration,
6098
+ consentedScreenControl: enrollment.allowScreenControl
6099
+ });
6100
+ return { ok: true, credentials };
6101
+ }
6102
+ async function pollDeviceEnrollment(services, input) {
6103
+ const { db, settings } = services;
6104
+ const request = await getDeviceEnrollmentRequestByDeviceCode(db, input.deviceCode);
6105
+ if (!request) {
6106
+ return { state: "expired" };
6107
+ }
6108
+ if (request.status === "denied") {
6109
+ return { state: "denied" };
6110
+ }
6111
+ if (request.status === "pending") {
6112
+ if (new Date(request.expiresAt).getTime() <= Date.now()) {
6113
+ return { state: "expired" };
6114
+ }
6115
+ return { state: "pending" };
6116
+ }
6117
+ if (!request.enrollmentId) {
6118
+ return { state: "expired" };
6119
+ }
6120
+ const secret = resolveEnrollmentSigningSecret(settings);
6121
+ if (!secret) {
6122
+ return { state: "disabled" };
6123
+ }
6124
+ const enrollment = await getEnrollment(db, request.workspaceId, request.enrollmentId);
6125
+ if (!enrollment || enrollment.status !== "active") {
6126
+ return { state: "denied" };
6127
+ }
6128
+ const credentials = await buildEnrollmentCredentials(services, {
6129
+ secret,
6130
+ workspaceId: request.workspaceId,
6131
+ agentId: enrollment.id,
6132
+ credentialGeneration: enrollment.credentialGeneration,
6133
+ consentedScreenControl: enrollment.allowScreenControl
6134
+ });
6135
+ if (request.status === "approved") {
6136
+ await consumeDeviceEnrollmentRequest(db, {
6137
+ accountId: request.accountId,
6138
+ workspaceId: request.workspaceId,
6139
+ requestId: request.id
6140
+ });
6141
+ }
6142
+ return { state: DeviceEnrollmentState.enum.authorized, credentials };
6143
+ }
6144
+ async function buildEnrollmentCredentials(services, input) {
6145
+ const { settings } = services;
6146
+ const subjectPrefix = `agent.${input.workspaceId}.${input.agentId}`;
6147
+ const nowSeconds = Math.floor(Date.now() / 1e3);
6148
+ const exp = nowSeconds + ENROLLMENT_BEARER_TTL_SECONDS;
6149
+ const bearer = await signEnrollmentBearer(input.secret, {
6150
+ workspaceId: input.workspaceId,
6151
+ agentId: input.agentId,
6152
+ enrollmentId: input.agentId,
6153
+ credentialGeneration: input.credentialGeneration,
6154
+ subjectPrefix,
6155
+ exp
6156
+ });
6157
+ const natsUrls = settings.selfhostedNatsUrl ? [settings.selfhostedNatsUrl] : [];
6158
+ const relayTokenSecret = resolveRelayTokenSecret(settings);
6159
+ const relayToken = relayTokenSecret ? await signRelayToken(relayTokenSecret, {
6160
+ workspaceId: input.workspaceId,
6161
+ agentId: input.agentId,
6162
+ exp: nowSeconds + RELAY_TOKEN_TTL_SECONDS
6163
+ }) : "";
6164
+ return {
6165
+ agentId: input.agentId,
6166
+ workspaceId: input.workspaceId,
6167
+ bearer,
6168
+ subjectPrefix,
6169
+ natsUrls,
6170
+ // Hand the agent the canonical `/stream` dial base, NOT the raw configured URL.
6171
+ // The agent's relay producer appends only its routing query and assumes the base
6172
+ // already carries the relay's `/stream` route; a path-less base 400s the dial and
6173
+ // makes the terminal/desktop streams unreachable.
6174
+ relayUrl: relayDialBaseFromSettings(settings),
6175
+ relayToken,
6176
+ // M-AUTH closes the placeholder: there is NO per-machine NATS Account creds
6177
+ // file. The agent presents the BEARER as the NATS connect auth-token; the
6178
+ // server's auth-callout responder validates it and mints a workspace-scoped
6179
+ // user JWT. We echo the bearer here so a consumer reading this (vestigial) field
6180
+ // as the connect credential still works — the value IS the bearer.
6181
+ natsAccountCreds: bearer,
6182
+ updatePublicKey: settings.agentUpdatePublicKey ?? "",
6183
+ consentedWholeMachine: true,
6184
+ consentedScreenControl: input.consentedScreenControl
6185
+ };
6186
+ }
5893
6187
 
5894
6188
  // src/mcp/capability-account-readiness.ts
5895
6189
  function capabilityAccountReadiness(availableRoutes, bindings) {
@@ -5905,6 +6199,7 @@ function capabilityAccountReadiness(availableRoutes, bindings) {
5905
6199
 
5906
6200
  // src/site-uploads.ts
5907
6201
  import { WorkspaceArtifactSourceBundle as WorkspaceArtifactSourceBundle2 } from "@opengeni/contracts";
6202
+ import { retryWhileMissing as retryWhileMissing2 } from "@opengeni/storage";
5908
6203
  import {
5909
6204
  createWorkspaceArtifactUpload,
5910
6205
  getWorkspaceArtifactUpload,
@@ -6017,7 +6312,12 @@ async function validateSiteSource(storage, key, sizeBytes) {
6017
6312
  "Editable source JSON must be at most 64 MiB. Exclude dependencies and build output; HTML has a separate storage limit."
6018
6313
  );
6019
6314
  }
6020
- const content = await storage.getObjectBytes(key);
6315
+ const content = await retryWhileMissing2(() => storage.getObjectBytes(key));
6316
+ if (!content) {
6317
+ throw new WorkspaceArtifactOperationError(
6318
+ "Site source is not available yet. Retry publication with the same upload."
6319
+ );
6320
+ }
6021
6321
  try {
6022
6322
  WorkspaceArtifactSourceBundle2.parse(
6023
6323
  JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(content.bytes))
@@ -6110,13 +6410,20 @@ async function freeze(storage, from, to, contentType, optional = false) {
6110
6410
  }
6111
6411
  decoder2.decode();
6112
6412
  }
6113
- await storage.putObjectStreamIfAbsent({
6413
+ const created = await storage.putObjectStreamIfAbsent({
6114
6414
  key: to,
6115
6415
  contentType,
6116
6416
  byteSize: head.ContentLength,
6117
6417
  chunks: chunks()
6118
6418
  });
6119
- return (await storage.headObject(to)).ContentLength;
6419
+ if (created) return head.ContentLength;
6420
+ const stored = await retryWhileMissing2(() => storage.headObject(to));
6421
+ if (!stored || stored.ContentLength === void 0) {
6422
+ throw new WorkspaceArtifactOperationError(
6423
+ "Frozen Site upload is unavailable. Retry publication with the same upload."
6424
+ );
6425
+ }
6426
+ return stored.ContentLength;
6120
6427
  }
6121
6428
  async function prepareWorkspaceArtifactPublication(deps, grant, input) {
6122
6429
  const storage = deps.objectStorage;
@@ -7291,7 +7598,14 @@ function registerKnowledgeEntryTools(server, deps, grant, sessionId) {
7291
7598
  inputSchema: KnowledgeSavePreparationRequest.shape
7292
7599
  },
7293
7600
  (input) => run(
7294
- (context) => prepareKnowledgeSave(deps.db, context, input, () => deps.getDocumentServices().embedder)
7601
+ (context) => prepareKnowledgeSave(
7602
+ deps.db,
7603
+ context,
7604
+ input,
7605
+ () => deps.getDocumentServices().embedder,
7606
+ void 0,
7607
+ deps.settings
7608
+ )
7295
7609
  )
7296
7610
  );
7297
7611
  server.registerTool(
@@ -7306,7 +7620,13 @@ function registerKnowledgeEntryTools(server, deps, grant, sessionId) {
7306
7620
  }).extend({ view: z8.enum(["published", "needs_review"]).default("published") }).shape
7307
7621
  },
7308
7622
  (input) => run(
7309
- (context) => searchKnowledgeEntries(deps.db, context, input, () => deps.getDocumentServices().embedder)
7623
+ (context) => searchKnowledgeEntries(
7624
+ deps.db,
7625
+ context,
7626
+ input,
7627
+ () => deps.getDocumentServices().embedder,
7628
+ deps.settings
7629
+ )
7310
7630
  )
7311
7631
  );
7312
7632
  server.registerTool(
@@ -7700,7 +8020,7 @@ import {
7700
8020
  recordSandboxFilePublication,
7701
8021
  withSessionRlsActorContext as withSessionRlsActorContext5
7702
8022
  } from "@opengeni/db";
7703
- import { retryWhileMissing as retryWhileMissing3 } from "@opengeni/storage";
8023
+ import { retryWhileMissing as retryWhileMissing4 } from "@opengeni/storage";
7704
8024
  import { HTTPException as HTTPException12 } from "hono/http-exception";
7705
8025
 
7706
8026
  // src/sandbox/channel-a.ts
@@ -7709,6 +8029,7 @@ import {
7709
8029
  hasGitCredentialRepositorySelection,
7710
8030
  hasGitHubRepositorySelection,
7711
8031
  sandboxLifecycleTransitionWaitMs,
8032
+ sandboxWarmRateMicrosPerSecond,
7712
8033
  stableSandboxEnvironmentForRun
7713
8034
  } from "@opengeni/config";
7714
8035
  import { githubAppBotIdentity } from "@opengeni/github";
@@ -7727,6 +8048,7 @@ import {
7727
8048
  SandboxImageConflictError,
7728
8049
  SandboxProviderReadLockUnavailableError,
7729
8050
  SandboxRigConflictError,
8051
+ SandboxPaidComputeAdmissionError,
7730
8052
  withSandboxProviderReadLock
7731
8053
  } from "@opengeni/db";
7732
8054
  import { appendAndPublishEvents } from "@opengeni/events";
@@ -8546,6 +8868,10 @@ async function withChannelAOperation(services, ctx, readOnly, fn) {
8546
8868
  holderId,
8547
8869
  subjectId: session.id,
8548
8870
  backend: session.sandboxBackend,
8871
+ warmBilling: {
8872
+ mode: settings.sandboxWarmBillingMode,
8873
+ rateMicrosPerSecond: sandboxWarmRateMicrosPerSecond(settings, session.sandboxBackend)
8874
+ },
8549
8875
  os: session.sandboxOs,
8550
8876
  image: sandboxRuntime.image,
8551
8877
  rigVersionId: session.rigVersionId,
@@ -8846,6 +9172,8 @@ async function withChannelAOperation(services, ctx, readOnly, fn) {
8846
9172
  }
8847
9173
  function mapChannelAError(error, waitSignal) {
8848
9174
  if (error instanceof HTTPException6) return error;
9175
+ if (error instanceof SandboxPaidComputeAdmissionError)
9176
+ return new HTTPException6(402, { message: error.message, cause: error });
8849
9177
  if (isChannelARequestCancellation(error, waitSignal))
8850
9178
  return new HTTPException6(499, {
8851
9179
  message: "request cancelled",
@@ -9059,7 +9387,7 @@ import {
9059
9387
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError3
9060
9388
  } from "@opengeni/core";
9061
9389
  import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
9062
- import { retryWhileMissing as retryWhileMissing2 } from "@opengeni/storage";
9390
+ import { retryWhileMissing as retryWhileMissing3 } from "@opengeni/storage";
9063
9391
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
9064
9392
 
9065
9393
  // src/mcp/files.ts
@@ -9161,7 +9489,7 @@ function buildFilesMcpServer(deps, authority) {
9161
9489
  }
9162
9490
 
9163
9491
  // src/mcp-oauth.ts
9164
- import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
9492
+ import { createHash as createHash2, randomBytes as randomBytes3 } from "crypto";
9165
9493
  import {
9166
9494
  MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS,
9167
9495
  MCP_OAUTH_AUTHORIZATION_CODE_TTL_SECONDS,
@@ -9219,7 +9547,7 @@ function normalizedAddress(value) {
9219
9547
 
9220
9548
  // src/workspace-tool-gateway.ts
9221
9549
  import { knowledgeContextForGateway } from "@opengeni/core";
9222
- import { createHash, randomBytes } from "crypto";
9550
+ import { createHash, randomBytes as randomBytes2 } from "crypto";
9223
9551
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
9224
9552
  import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
9225
9553
  import { withSiteSessionOrigin } from "@opengeni/core";
@@ -9324,7 +9652,13 @@ function buildDocumentsMcpServer(db, accountId, workspaceId, documentServices, o
9324
9652
  {
9325
9653
  type: "text",
9326
9654
  text: JSON.stringify(
9327
- await searchKnowledgeEntries2(db, context, input, () => documentServices.embedder)
9655
+ await searchKnowledgeEntries2(
9656
+ db,
9657
+ context,
9658
+ input,
9659
+ () => documentServices.embedder,
9660
+ options.settings
9661
+ )
9328
9662
  )
9329
9663
  }
9330
9664
  ]
@@ -9610,7 +9944,8 @@ async function prepareWorkspaceToolGatewayForGrantInternal(routeDeps, grant, all
9610
9944
  grant.workspaceId,
9611
9945
  routeDeps.getDocumentServices(),
9612
9946
  {
9613
- knowledge: await knowledgeContextForGateway(routeDeps, grant)
9947
+ knowledge: await knowledgeContextForGateway(routeDeps, grant),
9948
+ settings: gatewaySettings
9614
9949
  }
9615
9950
  )
9616
9951
  )
@@ -9869,7 +10204,7 @@ async function approveWorkspaceToolGatewayCall(prepared, grant, db, input, issue
9869
10204
  operation: "approval",
9870
10205
  source: preparedCall.entry.source
9871
10206
  });
9872
- const approvalToken = `ogta_${randomBytes(32).toString("base64url")}`;
10207
+ const approvalToken = `ogta_${randomBytes2(32).toString("base64url")}`;
9873
10208
  const expiresAt = new Date(Date.now() + 5 * 6e4);
9874
10209
  try {
9875
10210
  await prepared.reauthorize?.();
@@ -10501,7 +10836,7 @@ function pkceChallenge(verifier) {
10501
10836
  return createHash2("sha256").update(verifier, "ascii").digest("base64url");
10502
10837
  }
10503
10838
  function opaque(prefix) {
10504
- return `${prefix}${randomBytes2(32).toString("base64url")}`;
10839
+ return `${prefix}${randomBytes3(32).toString("base64url")}`;
10505
10840
  }
10506
10841
  function tokenHash(value) {
10507
10842
  return createHash2("sha256").update(value, "utf8").digest("hex");
@@ -10954,7 +11289,7 @@ function registerFileRoutes(app, deps) {
10954
11289
  const file2 = await rejectAndCleanObject(409, "file upload has expired", "expired");
10955
11290
  return c.json(CompleteFileUploadResponse.parse({ file: file2 }));
10956
11291
  }
10957
- const head = await retryWhileMissing2(async () => {
11292
+ const head = await retryWhileMissing3(async () => {
10958
11293
  if (!await objectStorage.fileExists(upload.file)) return null;
10959
11294
  return await objectStorage.headFile(upload.file);
10960
11295
  });
@@ -11086,7 +11421,7 @@ function registerFileRoutes(app, deps) {
11086
11421
  throw new HTTPException11(409, { message: `generated video is ${retained.file.status}` });
11087
11422
  }
11088
11423
  const file = generatedVideoFileAsset(retained.file);
11089
- const videoPresent = await retryWhileMissing2(
11424
+ const videoPresent = await retryWhileMissing3(
11090
11425
  async () => await objectStorage.fileExists(file) ? true : null
11091
11426
  );
11092
11427
  if (!videoPresent) {
@@ -11196,7 +11531,7 @@ function registerFileRoutes(app, deps) {
11196
11531
  if (file.status !== "ready") {
11197
11532
  throw new HTTPException11(409, { message: `file is ${file.status}` });
11198
11533
  }
11199
- const present = await retryWhileMissing2(
11534
+ const present = await retryWhileMissing3(
11200
11535
  async () => await objectStorage.fileExists(file) ? true : null
11201
11536
  );
11202
11537
  if (!present) {
@@ -11270,7 +11605,7 @@ async function serveRetainedArtifactContent(c, objectStorage, artifactId2, file,
11270
11605
  ...range.contentRange ? { "Content-Range": range.contentRange } : {}
11271
11606
  };
11272
11607
  if (range.kind === "empty") {
11273
- const present = await retryWhileMissing2(
11608
+ const present = await retryWhileMissing3(
11274
11609
  async () => await objectStorage.fileExists(file) ? true : null
11275
11610
  );
11276
11611
  if (!present) {
@@ -11278,7 +11613,7 @@ async function serveRetainedArtifactContent(c, objectStorage, artifactId2, file,
11278
11613
  }
11279
11614
  return c.body(null, 200, headers);
11280
11615
  }
11281
- const bytes = await retryWhileMissing2(
11616
+ const bytes = await retryWhileMissing3(
11282
11617
  async () => objectStorage.getFileRange(file, {
11283
11618
  start: range.start,
11284
11619
  end: range.end
@@ -11674,6 +12009,14 @@ function sandboxFileContentType(filename) {
11674
12009
  ".jpg": "image/jpeg",
11675
12010
  ".json": "application/json",
11676
12011
  ".md": "text/markdown",
12012
+ ".mp4": "video/mp4",
12013
+ ".webm": "video/webm",
12014
+ ".ogv": "video/ogg",
12015
+ ".mp3": "audio/mpeg",
12016
+ ".m4a": "audio/mp4",
12017
+ ".ogg": "audio/ogg",
12018
+ ".wav": "audio/wav",
12019
+ ".flac": "audio/flac",
11677
12020
  ".pdf": "application/pdf",
11678
12021
  ".png": "image/png",
11679
12022
  ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
@@ -11685,7 +12028,9 @@ function sandboxFileContentType(filename) {
11685
12028
  }[extension] ?? "application/octet-stream";
11686
12029
  }
11687
12030
  function sandboxArtifactIdentity(input) {
11688
- const digest2 = createHash3("sha256").update("opengeni-sandbox-file-artifact-v1\0").update(input.workspaceId).update("\0").update(input.sessionId).update("\0").update(input.path).update("\0").update(input.sha256).digest("hex");
12031
+ const digest2 = createHash3("sha256").update(
12032
+ /^(audio|video)\//.test(sandboxFileContentType(input.path)) ? "opengeni-sandbox-media-artifact-v1\0" : "opengeni-sandbox-file-artifact-v1\0"
12033
+ ).update(input.workspaceId).update("\0").update(input.sessionId).update("\0").update(input.path).update("\0").update(input.sha256).digest("hex");
11689
12034
  return {
11690
12035
  fileId: uuidFromDigest(digest2, 0),
11691
12036
  uploadId: uuidFromDigest(digest2, 16)
@@ -11704,7 +12049,7 @@ function assertSandboxArtifactFile(file, expected) {
11704
12049
  }
11705
12050
  }
11706
12051
  async function assertVisibleSandboxArtifact(storage, file) {
11707
- const head = await retryWhileMissing3(async () => await storage.headObject(file.objectKey));
12052
+ const head = await retryWhileMissing4(async () => await storage.headObject(file.objectKey));
11708
12053
  assertStoredSandboxArtifact(head, file);
11709
12054
  }
11710
12055
  function assertStoredSandboxArtifact(head, file) {
@@ -11834,9 +12179,11 @@ import {
11834
12179
  getGlobalCatalogOAuthProfile,
11835
12180
  listConnectionsMetadata,
11836
12181
  loadIntegrationOAuthClient,
12182
+ loadIntegrationOAuthPendingState,
11837
12183
  normalizeBearerScheme,
11838
12184
  replaceIntegrationOAuthClientIfCurrent,
11839
12185
  storeIntegrationOAuthClient,
12186
+ storeIntegrationOAuthPendingState,
11840
12187
  updateConnection,
11841
12188
  withDatabaseStatementTimeout
11842
12189
  } from "@opengeni/db";
@@ -11854,7 +12201,7 @@ import {
11854
12201
  validateHttpUrl
11855
12202
  } from "@opengeni/network";
11856
12203
  import { Buffer as Buffer2 } from "buffer";
11857
- import { createHash as createHash4, randomBytes as randomBytes3 } from "crypto";
12204
+ import { createHash as createHash4, randomBytes as randomBytes4, randomUUID } from "crypto";
11858
12205
  import { HTTPException as HTTPException17 } from "hono/http-exception";
11859
12206
 
11860
12207
  // src/connection-ownership.ts
@@ -12406,7 +12753,7 @@ async function startMcpOAuthWithinDeadline(deps, context, deadline) {
12406
12753
  )
12407
12754
  );
12408
12755
  const key = requireEnvironmentEncryption(settings);
12409
- const state = createSignedState3(requireIntegrationsStateSecret(settings), {
12756
+ const fullState = createSignedState3(requireIntegrationsStateSecret(settings), {
12410
12757
  ...context.integrationKey ? { integrationKey: context.integrationKey } : {},
12411
12758
  ...context.connectAttemptId ? { connectAttemptId: context.connectAttemptId } : {},
12412
12759
  accountId: context.accountId,
@@ -12448,6 +12795,30 @@ async function startMcpOAuthWithinDeadline(deps, context, deadline) {
12448
12795
  returnPath,
12449
12796
  ...existing ? { connectionId: existing.id, connectionVersion: existing.version } : {}
12450
12797
  });
12798
+ const expiresAt = new Date(Date.now() + oauthStateTtlMs);
12799
+ let state = fullState;
12800
+ if (settings.integrationsOauthShortStateEnabled) {
12801
+ const stateId = randomUUID();
12802
+ state = createSignedState3(requireIntegrationsStateSecret(settings), {
12803
+ kind: "mcp_oauth_reference",
12804
+ id: stateId,
12805
+ accountId: context.accountId,
12806
+ workspaceId: context.workspaceId
12807
+ });
12808
+ if (state.length > 512) {
12809
+ throw new Error("MCP OAuth reference state exceeded its provider-safe length");
12810
+ }
12811
+ await deadline.run(
12812
+ "state_persist",
12813
+ async () => storeIntegrationOAuthPendingState(db, {
12814
+ id: stateId,
12815
+ accountId: context.accountId,
12816
+ workspaceId: context.workspaceId,
12817
+ stateEncrypted: encryptEnvironmentValue3(key, fullState),
12818
+ expiresAt
12819
+ })
12820
+ );
12821
+ }
12451
12822
  const authorizationUrl = buildAuthorizationUrl({
12452
12823
  endpoint: discovery.as.authorizationEndpoint,
12453
12824
  settings,
@@ -12463,7 +12834,7 @@ async function startMcpOAuthWithinDeadline(deps, context, deadline) {
12463
12834
  return OAuthStartResponse.parse({
12464
12835
  state,
12465
12836
  authorizationUrl,
12466
- expiresAt: new Date(Date.now() + oauthStateTtlMs).toISOString()
12837
+ expiresAt: expiresAt.toISOString()
12467
12838
  });
12468
12839
  }
12469
12840
  function assertOAuthStartProfile(settings, payload, mcpUrl, profile) {
@@ -12519,7 +12890,7 @@ async function completeMcpOAuthCallbackWithinDeadline(deps, input, deadline) {
12519
12890
  };
12520
12891
  }
12521
12892
  try {
12522
- state = readOAuthState(input.state, settings);
12893
+ state = await resolveMcpOAuthState(db, settings, input.state, deadline);
12523
12894
  integrationKey = state.integrationKey ?? "custom:mcp";
12524
12895
  if (state.connectAttemptId) {
12525
12896
  const stored = await getConnectAttempt2(db, state, state.connectAttemptId);
@@ -13297,6 +13668,33 @@ function buildAuthorizationUrl(input) {
13297
13668
  }
13298
13669
  return url.toString();
13299
13670
  }
13671
+ async function resolveMcpOAuthState(db, settings, state, deadline) {
13672
+ const reference = readSignedState4(state, requireIntegrationsStateSecret(settings));
13673
+ if (reference?.kind !== "mcp_oauth_reference") {
13674
+ return readOAuthState(state, settings);
13675
+ }
13676
+ const accountId = requiredString(reference.accountId, "state.accountId");
13677
+ const workspaceId = requiredString(reference.workspaceId, "state.workspaceId");
13678
+ const id = requiredString(reference.id, "state.id");
13679
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id)) {
13680
+ throw new HTTPException17(400, { message: "invalid OAuth state reference" });
13681
+ }
13682
+ const encrypted = await runCallbackDatabaseStage(
13683
+ deadline,
13684
+ "state_verify",
13685
+ db,
13686
+ (scopedDb) => loadIntegrationOAuthPendingState(scopedDb, { accountId, workspaceId, id })
13687
+ );
13688
+ if (!encrypted) {
13689
+ throw new HTTPException17(400, { message: "invalid or expired OAuth state" });
13690
+ }
13691
+ const fullState = decryptEnvironmentValue2(requireEnvironmentEncryption(settings), encrypted);
13692
+ const resolved = readOAuthState(fullState, settings);
13693
+ if (resolved.accountId !== accountId || resolved.workspaceId !== workspaceId) {
13694
+ throw new HTTPException17(400, { message: "OAuth state reference mismatch" });
13695
+ }
13696
+ return resolved;
13697
+ }
13300
13698
  function readOAuthState(state, settings) {
13301
13699
  const payload = readSignedState4(state, requireIntegrationsStateSecret(settings));
13302
13700
  if (!payload) {
@@ -13633,6 +14031,8 @@ function oauthStartStageLabel(stage) {
13633
14031
  return "authorization-server discovery";
13634
14032
  case "client_registration":
13635
14033
  return "OAuth client registration";
14034
+ case "state_persist":
14035
+ return "OAuth state persistence";
13636
14036
  }
13637
14037
  }
13638
14038
  function logOAuthVerificationWarning(observability, error, _state) {
@@ -13920,7 +14320,7 @@ function pkceChallenge2(verifier) {
13920
14320
  return createHash4("sha256").update(verifier).digest("base64url");
13921
14321
  }
13922
14322
  function randomPkceVerifier() {
13923
- return randomBytes3(32).toString("base64url");
14323
+ return randomBytes4(32).toString("base64url");
13924
14324
  }
13925
14325
  function uniqueStrings(values) {
13926
14326
  return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
@@ -13964,7 +14364,7 @@ import {
13964
14364
  import { createSignedState as createSignedState4, readSignedState as readSignedState5 } from "@opengeni/github";
13965
14365
  import { OAUTH_MAX_RESPONSE_BYTES as OAUTH_MAX_RESPONSE_BYTES3, pinnedFetch as pinnedFetch2, readResponseJsonBounded as readResponseJsonBounded2 } from "@opengeni/network";
13966
14366
  import { Buffer as Buffer3 } from "buffer";
13967
- import { createHash as createHash5, randomBytes as randomBytes4 } from "crypto";
14367
+ import { createHash as createHash5, randomBytes as randomBytes5 } from "crypto";
13968
14368
  import { HTTPException as HTTPException18 } from "hono/http-exception";
13969
14369
  var SOCIAL_USER_AGENT = "opengeni:social-connector:v0.1.0 (self-hosted)";
13970
14370
  var SOCIAL_TIMEOUT_MS = 1e4;
@@ -14033,7 +14433,7 @@ async function startSocialOAuth(deps, context) {
14033
14433
  const redirectUri = socialOAuthRedirectUri(settings, context.requestUrl);
14034
14434
  const returnPath = safeReturnPath2(context.payload.returnPath ?? "/integrations");
14035
14435
  const scopes = uniqueScopes(context.payload.scopes) ?? provider.defaultScopes;
14036
- const verifier = provider.pkce ? randomBytes4(32).toString("base64url") : null;
14436
+ const verifier = provider.pkce ? randomBytes5(32).toString("base64url") : null;
14037
14437
  const key = requireEnvironmentEncryption2(settings);
14038
14438
  const state = createSignedState4(requireIntegrationsStateSecret(settings), {
14039
14439
  kind: "social_oauth",
@@ -15772,6 +16172,21 @@ function boundRigDetailMcp(rig, versionsPage, changesPage, maxBytes = RIG_DETAIL
15772
16172
  return result;
15773
16173
  }
15774
16174
 
16175
+ // src/mcp/child-read-evidence.ts
16176
+ function completeChildReadSequences(page) {
16177
+ if (!page.sourceExact || !["conversation", "results"].includes(page.view)) return [];
16178
+ return page.events.filter((event) => {
16179
+ if (event.sourceOmitted) return false;
16180
+ if (event.fragment === void 0) return true;
16181
+ if (!event.fragment || typeof event.fragment !== "object") return false;
16182
+ const fragment = event.fragment;
16183
+ return fragment.offset === 0 && fragment.complete === true;
16184
+ }).map((event) => event.sequence);
16185
+ }
16186
+
16187
+ // src/mcp/server.ts
16188
+ import { acknowledgeConsumedChildEvents } from "@opengeni/db";
16189
+
15775
16190
  // src/mcp/session-wait.ts
15776
16191
  import {
15777
16192
  SESSION_EVENT_SEMANTIC_CLASS_TYPES,
@@ -16069,6 +16484,7 @@ function summarizeSessionWaitEvent(event, textChars = SUMMARY_TEXT_TIERS[0]) {
16069
16484
  } else if (typeof compact.result === "string" && compact.result !== compact.text) {
16070
16485
  summary.result = clampSummaryString(compact.result, textChars);
16071
16486
  }
16487
+ summary.contentComplete = ["turn.completed", "agent.message.completed"].includes(event.type) && !compact.truncation.truncated && text === compact.text && JSON.stringify(failure) === JSON.stringify(compact.failure) && (compact.output === null || compact.output === compact.text || JSON.stringify(compact.output) === JSON.stringify(summary.result)) && (compact.result === null || compact.result === void 0 || compact.result === compact.text || JSON.stringify(summary.result) === JSON.stringify(compact.result));
16072
16488
  return summary;
16073
16489
  }
16074
16490
  function prettyJsonBytes2(value) {
@@ -16100,6 +16516,7 @@ function boundSessionWaitResult(input, maxBytes = SESSION_EVENT_MCP_MAX_BYTES) {
16100
16516
  ...target,
16101
16517
  events: target.events.map((event) => ({
16102
16518
  ...event,
16519
+ contentComplete: false,
16103
16520
  text: event.text === null ? null : clampSummaryString(event.text, textChars),
16104
16521
  ...typeof event.result === "string" ? { result: clampSummaryString(event.result, textChars) } : {}
16105
16522
  }))
@@ -16705,6 +17122,7 @@ import {
16705
17122
  hasGitHubRepositorySelection as hasGitHubRepositorySelection2,
16706
17123
  resolveStreamTokenSecret,
16707
17124
  sandboxLifecycleTransitionWaitMs as sandboxLifecycleTransitionWaitMs2,
17125
+ sandboxWarmRateMicrosPerSecond as sandboxWarmRateMicrosPerSecond2,
16708
17126
  stableSandboxEnvironmentForRun as stableSandboxEnvironmentForRun2
16709
17127
  } from "@opengeni/config";
16710
17128
  import { githubAppBotIdentity as githubAppBotIdentity2 } from "@opengeni/github";
@@ -16714,7 +17132,7 @@ import {
16714
17132
  getSandbox as getSandbox2,
16715
17133
  getScheduledScopedRigVersionMetadata as getScheduledScopedRigVersionMetadata2,
16716
17134
  getSandboxSessionEnvelope as getSandboxSessionEnvelope2,
16717
- heartbeatLeaseHolder,
17135
+ heartbeatLeaseHolderStatus,
16718
17136
  loadWorkspaceEnvironmentForRun as loadWorkspaceEnvironmentForRun2,
16719
17137
  markSandboxProviderReady,
16720
17138
  markWarmLeaseInstanceLost as markWarmLeaseInstanceLost2,
@@ -16724,6 +17142,7 @@ import {
16724
17142
  releaseLeaseHolder as releaseLeaseHolder2,
16725
17143
  SandboxLeaseSupersededError as SandboxLeaseSupersededError2,
16726
17144
  SandboxViewerAdmissionBlockedError,
17145
+ SandboxPaidComputeAdmissionError as SandboxPaidComputeAdmissionError2,
16727
17146
  getSessionAuthorityEpoch,
16728
17147
  namedSubjectHasLiveWorkspaceAuthority
16729
17148
  } from "@opengeni/db";
@@ -16853,6 +17272,10 @@ async function attachViewer(services, input) {
16853
17272
  ...attachSubjectId ? { viewerSubjectId: attachSubjectId } : {},
16854
17273
  ...attachAuthorityEpoch !== null ? { viewerAuthorityEpoch: attachAuthorityEpoch } : {},
16855
17274
  backend: session.sandboxBackend,
17275
+ warmBilling: {
17276
+ mode: settings.sandboxWarmBillingMode,
17277
+ rateMicrosPerSecond: sandboxWarmRateMicrosPerSecond2(settings, session.sandboxBackend)
17278
+ },
16856
17279
  os: session.sandboxOs,
16857
17280
  image: sandboxRuntime.image,
16858
17281
  rigVersionId: session.rigVersionId,
@@ -16862,6 +17285,9 @@ async function attachViewer(services, input) {
16862
17285
  ...input.waitSignal ? { waitSignal: input.waitSignal } : {}
16863
17286
  });
16864
17287
  } catch (error) {
17288
+ if (error instanceof SandboxPaidComputeAdmissionError2) {
17289
+ throw new HTTPException19(402, { message: error.message, cause: error });
17290
+ }
16865
17291
  if (error instanceof SandboxViewerAdmissionBlockedError) {
16866
17292
  throw new HTTPException19(error.reason === "balance" ? 402 : 429, {
16867
17293
  message: error.reason === "balance" ? "insufficient OpenGeni credits for an idle sandbox viewer" : "workspace sandbox warm allowance exhausted",
@@ -17048,16 +17474,17 @@ async function ensureSessionGroupReady(services, input) {
17048
17474
  throw lastError ?? new Error("sandbox readiness could not be established");
17049
17475
  }
17050
17476
  async function heartbeatViewer(services, input) {
17051
- const alive = await heartbeatLeaseHolder(services.db, {
17477
+ const status = await heartbeatLeaseHolderStatus(services.db, {
17052
17478
  accountId: input.accountId,
17053
17479
  workspaceId: input.workspaceId,
17054
17480
  sandboxGroupId: input.sandboxGroupId,
17055
17481
  kind: "viewer",
17056
17482
  holderId: input.viewerId,
17057
17483
  leaseTtlMs: services.settings.sandboxLeaseTtlMs,
17058
- expectedEpoch: input.expectedEpoch
17484
+ expectedEpoch: input.expectedEpoch,
17485
+ billingMode: services.settings.sandboxWarmBillingMode
17059
17486
  });
17060
- if (!alive) return false;
17487
+ if (!status.leaseExtended) return status.holderAlive && status.fence === "funding";
17061
17488
  const lease = await readLease2(services.db, input.workspaceId, input.sandboxGroupId);
17062
17489
  if (lease?.liveness === "warm" && lease.leaseEpoch === input.expectedEpoch && lease.instanceId) {
17063
17490
  await renewSandboxProviderExpiration({
@@ -17543,7 +17970,7 @@ function viewerIdAsUuid(rawViewerId) {
17543
17970
  }
17544
17971
 
17545
17972
  // src/integrations/slack-bot.ts
17546
- import { createHmac as createHmac3, timingSafeEqual as timingSafeEqual2 } from "crypto";
17973
+ import { createHash as createHash7, createHmac as createHmac3, timingSafeEqual as timingSafeEqual2 } from "crypto";
17547
17974
  import { environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes3 } from "@opengeni/config";
17548
17975
  import {
17549
17976
  OPENGENI_SLACK_BOT_CREDENTIAL_LABEL,
@@ -18228,6 +18655,7 @@ var OpenGeniSlackBotClient = class {
18228
18655
  const targetKind = input.userId ? "user" : "channel";
18229
18656
  const targetId = input.userId ?? input.channelId;
18230
18657
  const blocks = validateSlackMessageBlocks(input.blocks);
18658
+ const wireBlocks = slackMessageWireBlocks(blocks);
18231
18659
  const requestDigest = this.postRequestDigest({
18232
18660
  operationId: input.operationId,
18233
18661
  targetKind,
@@ -18309,7 +18737,7 @@ var OpenGeniSlackBotClient = class {
18309
18737
  const posted = await this.call(headers, "chat.postMessage", {
18310
18738
  channel: channelId,
18311
18739
  text: input.text,
18312
- ...blocks ? { blocks: JSON.stringify(blocks) } : {},
18740
+ ...wireBlocks ? { blocks: JSON.stringify(wireBlocks) } : {},
18313
18741
  client_msg_id: input.operationId,
18314
18742
  unfurl_links: "false",
18315
18743
  unfurl_media: "false",
@@ -18367,6 +18795,7 @@ var OpenGeniSlackBotClient = class {
18367
18795
  let providerCallStarted = false;
18368
18796
  const blocks = validateSlackMessageBlocks(input.blocks);
18369
18797
  try {
18798
+ const wireBlocks = slackMessageWireBlocks(blocks);
18370
18799
  const headers = await this.headersFor(operation);
18371
18800
  await this.requireMemberChannel(headers, input.channelId);
18372
18801
  const requestDigest = this.updateRequestDigest({ ...input, ...blocks ? { blocks } : {} });
@@ -18405,7 +18834,7 @@ var OpenGeniSlackBotClient = class {
18405
18834
  channel: input.channelId,
18406
18835
  ts: input.timestamp,
18407
18836
  text: input.text,
18408
- ...blocks ? { blocks: JSON.stringify(blocks) } : {}
18837
+ ...wireBlocks ? { blocks: JSON.stringify(wireBlocks) } : {}
18409
18838
  });
18410
18839
  const slackChannelId = requiredSlackString(updated.channel, "channel");
18411
18840
  const slackMessageTimestamp = requiredSlackString(updated.ts, "ts");
@@ -19713,6 +20142,20 @@ function nextSlackFilesListPage(payload, requested, returnedFileCount) {
19713
20142
  }
19714
20143
  return nextPage;
19715
20144
  }
20145
+ function slackMessageWireBlocks(blocks) {
20146
+ if (!blocks) return void 0;
20147
+ const wire = blocks.flatMap((block, index) => {
20148
+ if (block.type !== "actions" || new Set(block.elements.map((e) => e.action_id)).size === block.elements.length)
20149
+ return [block];
20150
+ const id = createHash7("sha256").update(`${index}:${block.block_id}`).digest("hex").slice(0, 24);
20151
+ return block.elements.map((element, position2) => ({
20152
+ type: "actions",
20153
+ block_id: `opengeni_wire_${id}_${position2}`,
20154
+ elements: [element]
20155
+ }));
20156
+ });
20157
+ return validateSlackMessageBlocks(wire);
20158
+ }
19716
20159
  function validateSlackMessageBlocks(blocks) {
19717
20160
  if (blocks === void 0) return void 0;
19718
20161
  if (blocks.length < 1 || blocks.length > MAX_SLACK_BLOCKS) {
@@ -19778,7 +20221,7 @@ function slackMutationOutcomeMayBeAmbiguous(error) {
19778
20221
 
19779
20222
  // src/integrations/fiken.ts
19780
20223
  import { withOrganizationIntegrationAcquisition as withOrganizationIntegrationAcquisition3 } from "@opengeni/db/organization-integration-policy";
19781
- import { createHash as createHash7 } from "crypto";
20224
+ import { createHash as createHash8 } from "crypto";
19782
20225
  import { ExternalActorContinuation as ExternalActorContinuation3 } from "@opengeni/contracts/external-identities";
19783
20226
  import { requireConnectOwnerAuthority as requireConnectOwnerAuthority2 } from "@opengeni/core";
19784
20227
  import {
@@ -20599,7 +21042,7 @@ async function completeFikenOAuthCallback(deps, input) {
20599
21042
  operation = {
20600
21043
  attemptId: state.connectAttemptId,
20601
21044
  operationId: `oauth:${state.nonce}`,
20602
- inputDigest: createHash7("sha256").update(input.state).digest("hex")
21045
+ inputDigest: createHash8("sha256").update(input.state).digest("hex")
20603
21046
  };
20604
21047
  const claim = await claimOAuthAcquisition(
20605
21048
  deps.db,
@@ -20906,7 +21349,7 @@ import {
20906
21349
  knowledgeSourceAgentConfig,
20907
21350
  assertOrganizationIntegrationAllowed as assertOrganizationIntegrationAllowed2
20908
21351
  } from "@opengeni/contracts";
20909
- import { createHash as createHash8 } from "crypto";
21352
+ import { createHash as createHash9 } from "crypto";
20910
21353
  import { ExternalActorContinuation as ExternalActorContinuation4 } from "@opengeni/contracts/external-identities";
20911
21354
  import {
20912
21355
  ATLASSIAN_CREDENTIAL_LABEL,
@@ -21032,7 +21475,7 @@ async function completeAtlassianOAuthCallback(deps, input) {
21032
21475
  operation = {
21033
21476
  attemptId: state.connectAttemptId,
21034
21477
  operationId: `oauth:${state.nonce}`,
21035
- inputDigest: createHash8("sha256").update(input.state).digest("hex")
21478
+ inputDigest: createHash9("sha256").update(input.state).digest("hex")
21036
21479
  };
21037
21480
  const claim = await claimOAuthAcquisition(
21038
21481
  deps.db,
@@ -22466,7 +22909,7 @@ function adfText(value) {
22466
22909
  import { AtlassianConnectionMetadata as AtlassianConnectionMetadata2 } from "@opengeni/contracts/atlassian";
22467
22910
 
22468
22911
  // src/mcp/editable-artifacts.ts
22469
- import { createHash as createHash9 } from "crypto";
22912
+ import { createHash as createHash10 } from "crypto";
22470
22913
  import {
22471
22914
  editableArtifactClientTransactionId as editableArtifactClientTransactionId2,
22472
22915
  editableArtifactId as editableArtifactId2
@@ -22889,7 +23332,7 @@ function registerEditableArtifactAgentTools(input) {
22889
23332
  }
22890
23333
  function artifactAttemptContext(grant, sessionId, toolName, extra) {
22891
23334
  const operationId = trustedOperationId(extra);
22892
- const digest2 = createHash9("sha256").update("opengeni:editable-artifact-agent-call:v1\0", "utf8").update(grant.accountId, "utf8").update("\0", "utf8").update(grant.workspaceId, "utf8").update("\0", "utf8").update(sessionId, "utf8").update("\0", "utf8").update(String(grant.metadata?.["turnId"] ?? ""), "utf8").update("\0", "utf8").update(String(grant.metadata?.["attemptId"] ?? ""), "utf8").update("\0", "utf8").update(String(grant.metadata?.["executionGeneration"] ?? ""), "utf8").update("\0", "utf8").update(toolName, "utf8").update("\0", "utf8").update(operationId, "utf8").digest("hex");
23335
+ const digest2 = createHash10("sha256").update("opengeni:editable-artifact-agent-call:v1\0", "utf8").update(grant.accountId, "utf8").update("\0", "utf8").update(grant.workspaceId, "utf8").update("\0", "utf8").update(sessionId, "utf8").update("\0", "utf8").update(String(grant.metadata?.["turnId"] ?? ""), "utf8").update("\0", "utf8").update(String(grant.metadata?.["attemptId"] ?? ""), "utf8").update("\0", "utf8").update(String(grant.metadata?.["executionGeneration"] ?? ""), "utf8").update("\0", "utf8").update(toolName, "utf8").update("\0", "utf8").update(operationId, "utf8").digest("hex");
22893
23336
  const replicaId = digest2.slice(0, 16) === "0".repeat(16) ? `1${digest2.slice(1, 16)}` : digest2.slice(0, 16);
22894
23337
  const actor = editableArtifactActorForGrant(grant, replicaId);
22895
23338
  if (actor.kind !== "agent" || actor.sessionId !== sessionId) {
@@ -23095,7 +23538,7 @@ import {
23095
23538
  knowledgeSourceAgentConfig as knowledgeSourceAgentConfig2,
23096
23539
  assertOrganizationIntegrationAllowed as assertOrganizationIntegrationAllowed3
23097
23540
  } from "@opengeni/contracts";
23098
- import { createHash as createHash10, randomBytes as randomBytes5 } from "crypto";
23541
+ import { createHash as createHash11, randomBytes as randomBytes6 } from "crypto";
23099
23542
  import { ExternalActorContinuation as ExternalActorContinuation5 } from "@opengeni/contracts/external-identities";
23100
23543
  import {
23101
23544
  configuredGoogleDriveSyncLimits,
@@ -23216,7 +23659,7 @@ async function startGoogleDriveOAuth(deps, input) {
23216
23659
  requireGoogleDriveConnection(existing, input.subjectId);
23217
23660
  }
23218
23661
  const key = requireEnvironmentEncryption5(deps.settings);
23219
- const verifier = randomBytes5(48).toString("base64url");
23662
+ const verifier = randomBytes6(48).toString("base64url");
23220
23663
  const redirectUri = requireGoogleDriveOAuthCallbackUrl(deps.settings);
23221
23664
  const state = createSignedState7(requireIntegrationsStateSecret(deps.settings), {
23222
23665
  accountId: input.accountId,
@@ -23262,7 +23705,7 @@ async function startGoogleDriveOAuth(deps, input) {
23262
23705
  authorizationUrl.searchParams.set("code_challenge_method", "S256");
23263
23706
  authorizationUrl.searchParams.set(
23264
23707
  "code_challenge",
23265
- createHash10("sha256").update(verifier).digest("base64url")
23708
+ createHash11("sha256").update(verifier).digest("base64url")
23266
23709
  );
23267
23710
  return GoogleDriveOAuthStartResponse.parse({
23268
23711
  authorizationUrl: authorizationUrl.toString(),
@@ -23286,7 +23729,7 @@ async function completeGoogleDriveOAuthCallback(deps, input) {
23286
23729
  operation = {
23287
23730
  attemptId: state.connectAttemptId,
23288
23731
  operationId: `oauth:${state.nonce}`,
23289
- inputDigest: createHash10("sha256").update(input.state).digest("hex")
23732
+ inputDigest: createHash11("sha256").update(input.state).digest("hex")
23290
23733
  };
23291
23734
  const claim = await claimOAuthAcquisition(
23292
23735
  deps.db,
@@ -25206,7 +25649,7 @@ function uniqueStrings3(values) {
25206
25649
  }
25207
25650
 
25208
25651
  // src/temporal-schedule-cleanup.ts
25209
- import { randomUUID } from "crypto";
25652
+ import { randomUUID as randomUUID2 } from "crypto";
25210
25653
  import {
25211
25654
  claimTemporalScheduleCleanups,
25212
25655
  completeTemporalScheduleConnectorCleanup,
@@ -25278,7 +25721,7 @@ async function processTemporalScheduleCleanupClaims(deps, claims) {
25278
25721
  }
25279
25722
  async function drainTemporalScheduleCleanupOutbox(deps) {
25280
25723
  const claims = await claimTemporalScheduleCleanups(deps.db, {
25281
- claimId: randomUUID(),
25724
+ claimId: randomUUID2(),
25282
25725
  limit: CLEANUP_BATCH_SIZE,
25283
25726
  claimSeconds: CLEANUP_CLAIM_SECONDS
25284
25727
  });
@@ -26627,7 +27070,7 @@ function registerSandboxFileArtifactTool(server, deps, grant, sessionId, json) {
26627
27070
  server.registerTool(
26628
27071
  "sandbox_file_publish",
26629
27072
  {
26630
- description: "Publish one exact file from this session's /workspace into durable workspace storage. Use this before presenting a ZIP, CSV, JSON, Markdown, HTML, PDF, Office file, or other sandbox output as downloadable. Returns a permanent, authenticated artifact receipt; never invent or expose a sandbox: URL as the durable result.",
27073
+ description: "Publish one exact file from this session's /workspace into durable workspace storage. Use this before presenting a video, ZIP, CSV, JSON, Markdown, HTML, PDF, Office file, or other sandbox output as downloadable. Present the returned artifact.artifactId as [Open file](artifact:<artifactId>), or ![Preview](artifact:<artifactId>) for inline image/video/audio/PDF previews. Replace <artifactId> with the exact returned ID and use a descriptive label. Never expose a sandbox: URL as the durable result.",
26631
27074
  inputSchema: {
26632
27075
  path: z43.string().min(1).max(4096)
26633
27076
  }
@@ -27369,7 +27812,7 @@ function registerGoalTools(server, deps, grant, sessionId, json) {
27369
27812
  async ({ reason, timeoutSeconds, idempotencyKey }) => {
27370
27813
  await authorizeFirstPartySession(deps, grant, sessionId, "session.control");
27371
27814
  const context = exactAgentCommandContext(grant, sessionId);
27372
- const operationKey = idempotencyKey ?? `wait-for-input:${context.callerTurnId}:${createHash11("sha256").update(JSON.stringify({ reason, timeoutSeconds })).digest("hex").slice(0, 32)}`;
27815
+ const operationKey = idempotencyKey ?? `wait-for-input:${context.callerTurnId}:${createHash12("sha256").update(JSON.stringify({ reason, timeoutSeconds })).digest("hex").slice(0, 32)}`;
27373
27816
  const { events, operationId, replay, deadlineAt } = await waitForSessionInputWithEvent(
27374
27817
  deps.db,
27375
27818
  grant.workspaceId,
@@ -27610,7 +28053,7 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
27610
28053
  return {
27611
28054
  accountId: grant.accountId,
27612
28055
  workspaceId: grant.workspaceId,
27613
- operationKey: `attempt:${createHash11("sha256").update(`${claims.sessionId}:${claims.turnId}:${idempotencyKey}`).digest("hex")}`,
28056
+ operationKey: `attempt:${createHash12("sha256").update(`${claims.sessionId}:${claims.turnId}:${idempotencyKey}`).digest("hex")}`,
27614
28057
  actorSubjectId: grant.subjectId,
27615
28058
  sourceSessionId: claims.sessionId,
27616
28059
  sourceTurnId: claims.turnId,
@@ -28300,7 +28743,7 @@ function registerFleetTools(server, deps, grant, sessionId, json) {
28300
28743
  server.registerTool(
28301
28744
  "sandbox_provision",
28302
28745
  {
28303
- description: "Provision a new sandbox for the fleet. kind=selfhosted returns device-flow enrollment instructions to share with a HUMAN (install the agent + enroll their machine with loud whole-machine consent \u2014 the agent cannot self-consent). kind=modal creates a named Modal sandbox record, but it is NOT yet attachable as a swap target: routing a session onto a second Modal box is not supported yet, so sandbox_swap to its id is rejected. Use the session's own box (the default) or attach a Connected Machine instead.",
28746
+ description: "Provision a new sandbox for the fleet. kind=selfhosted returns interactive device-flow instructions for a human to approve. For authorized headless enrollment, use connected_machine_enroll_token when available instead. kind=modal creates a named Modal sandbox record, but it is NOT yet attachable as a swap target: routing a session onto a second Modal box is not supported yet, so sandbox_swap to its id is rejected. Use the session's own box (the default) or attach a Connected Machine instead.",
28304
28747
  inputSchema: {
28305
28748
  kind: z43.enum(["selfhosted", "modal"]),
28306
28749
  name: z43.string().min(1).max(120).optional()
@@ -28315,10 +28758,38 @@ function registerFleetTools(server, deps, grant, sessionId, json) {
28315
28758
  );
28316
28759
  }
28317
28760
  function registerConnectedMachineTools(server, deps, grant, sessionId, json) {
28761
+ server.registerTool(
28762
+ "connected_machine_enroll_token",
28763
+ {
28764
+ description: "Create a short-lived Connected Machine enrollment token for this workspace using existing enrollments:manage authority. Returns the token, expiry and Unix/PowerShell install commands. Run the appropriate command on the intended machine through an already-authorized execution path, then verify it with sandboxes_list. No separate device approval is required. Screen control is optional and defaults off. The token grants whole-machine access on enrollment; do not put it in public code or unrelated logs. Existing machine connections are preserved.",
28765
+ inputSchema: { allowScreenControl: z43.boolean().optional() }
28766
+ },
28767
+ async ({ allowScreenControl }) => {
28768
+ if (sessionId) {
28769
+ await authorizeFirstPartySession(deps, grant, sessionId, "session.first_party_mcp.call");
28770
+ }
28771
+ const minted = await mintEnrollToken(deps, {
28772
+ accountId: grant.accountId,
28773
+ workspaceId: grant.workspaceId,
28774
+ allowScreenControl: allowScreenControl ?? false
28775
+ });
28776
+ if (!minted) throw new Error("enrollment credential plane is not configured");
28777
+ const base = (deps.settings.publicBaseUrl ?? "https://app.opengeni.ai").replace(/\/+$/, "");
28778
+ const unixQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
28779
+ const windowsQuote = (value) => `'${value.replaceAll("'", "''")}'`;
28780
+ return json({
28781
+ ...minted,
28782
+ workspaceId: grant.workspaceId,
28783
+ allowScreenControl: allowScreenControl ?? false,
28784
+ installCommandUnix: `curl -fsSL ${unixQuote(`${base}/install.sh`)} | OPENGENI_API_URL=${unixQuote(base)} OPENGENI_ENROLL_TOKEN=${unixQuote(minted.token)} sh`,
28785
+ installCommandWindows: `$env:OPENGENI_API_URL=${windowsQuote(base)}; $env:OPENGENI_ENROLL_TOKEN=${windowsQuote(minted.token)}; irm ${windowsQuote(`${base}/install.ps1`)} | iex`
28786
+ });
28787
+ }
28788
+ );
28318
28789
  server.registerTool(
28319
28790
  "connected_machine_remove",
28320
28791
  {
28321
- description: "Remove one enrolled self-hosted machine while it is offline. Access is revoked, future heartbeat/reconnect credentials are rejected, and session, route, lease, archive, and audit history is retained. Idle dependent sessions are detached atomically; machine-home sessions become compute-less (backend none) until another sandbox is selected. Active turns, live leases, and recovery work remain fail-closed blockers whose typed outcome explains what must settle before retrying. Pass the enrollmentId from the Machines surface, never a Modal sandbox id. Reconnecting later requires a fresh human-approved device-flow enrollment.",
28792
+ description: "Remove one enrolled self-hosted machine while it is offline. Access is revoked, future heartbeat/reconnect credentials are rejected, and session, route, lease, archive, and audit history is retained. Idle dependent sessions are detached atomically; machine-home sessions become compute-less (backend none) until another sandbox is selected. Active turns, live leases, and recovery work remain fail-closed blockers whose typed outcome explains what must settle before retrying. Pass the enrollmentId from the Machines surface, never a Modal sandbox id. Reconnecting later requires fresh enrollment through device approval or an authorized enrollment token.",
28322
28793
  inputSchema: {
28323
28794
  enrollmentId: z43.string().uuid(),
28324
28795
  expectedUpdatedAt: z43.string().datetime({ offset: true }).optional(),
@@ -28343,7 +28814,7 @@ function registerConnectedMachineTools(server, deps, grant, sessionId, json) {
28343
28814
  accountId: grant.accountId,
28344
28815
  workspaceId: enrollment.workspaceId,
28345
28816
  enrollmentId,
28346
- operationKey: idempotencyKey?.trim() || randomUUID2(),
28817
+ operationKey: idempotencyKey?.trim() || randomUUID3(),
28347
28818
  ...expectedUpdatedAt ? { expectedUpdatedAt } : {},
28348
28819
  subjectId: resourceGrant.subjectId
28349
28820
  });
@@ -28671,6 +29142,18 @@ function exactAgentCommandContext(grant, callerSessionId, authorizationSurface)
28671
29142
  };
28672
29143
  }
28673
29144
  function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSessionId, sessionCreateVisible, json) {
29145
+ const acknowledgeReads = async (children) => {
29146
+ if (!callerSessionId || !exactAgentAttemptClaims(grant) || !children.some((child) => child.sequences.length > 0))
29147
+ return;
29148
+ const actor = await requireLiveAgentAttemptAuthorization4(deps.db, grant, callerSessionId);
29149
+ if (!actor.initiatingHumanSubjectId) return;
29150
+ await acknowledgeConsumedChildEvents(deps.db, {
29151
+ workspaceId: grant.workspaceId,
29152
+ sessionId: callerSessionId,
29153
+ subjectId: actor.initiatingHumanSubjectId,
29154
+ children
29155
+ });
29156
+ };
28674
29157
  if (can("sessions:read")) {
28675
29158
  server.registerTool(
28676
29159
  "sessions_list",
@@ -28965,29 +29448,29 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
28965
29448
  if (view !== "debug" && !auditRequested) {
28966
29449
  const { readSessionEventView } = await import("./session-event-view-MU3HMNV3.js");
28967
29450
  const { listSessionEventSlices } = await import("@opengeni/db/session-event-slices");
28968
- return json(
28969
- await readSessionEventView(
28970
- {
28971
- sessionId,
28972
- view,
28973
- cursor,
28974
- callId,
28975
- includeArguments,
28976
- includeOutput,
28977
- after,
28978
- before,
28979
- direction: requestedDirection,
28980
- limit
28981
- },
28982
- (options) => listSessionEventSlices(
28983
- deps.db,
28984
- grant.workspaceId,
28985
- sessionId,
28986
- options,
28987
- (legacyOptions) => listSessionEventPage(deps.db, grant.workspaceId, sessionId, legacyOptions)
28988
- )
29451
+ const page2 = await readSessionEventView(
29452
+ {
29453
+ sessionId,
29454
+ view,
29455
+ cursor,
29456
+ callId,
29457
+ includeArguments,
29458
+ includeOutput,
29459
+ after,
29460
+ before,
29461
+ direction: requestedDirection,
29462
+ limit
29463
+ },
29464
+ (options) => listSessionEventSlices(
29465
+ deps.db,
29466
+ grant.workspaceId,
29467
+ sessionId,
29468
+ options,
29469
+ (legacyOptions) => listSessionEventPage(deps.db, grant.workspaceId, sessionId, legacyOptions)
28989
29470
  )
28990
29471
  );
29472
+ await acknowledgeReads([{ sessionId, sequences: completeChildReadSequences(page2) }]);
29473
+ return json(page2);
28991
29474
  }
28992
29475
  if (cursor !== void 0 || callId !== void 0 || includeArguments !== void 0 || includeOutput !== void 0) {
28993
29476
  throw new Error("cursor/callId/includeArguments/includeOutput require a non-debug view");
@@ -29017,31 +29500,37 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
29017
29500
  });
29018
29501
  if (requestedResultMode === "compact") {
29019
29502
  const event = dbPage.events[0];
29020
- return json(
29021
- event ? boundSessionEventCompactResult(
29022
- compactSessionEventResult2(
29023
- event,
29024
- latestClass,
29025
- dbPage.coveredSequence ?? {
29026
- first: event.sequence,
29027
- last: event.sequence
29028
- }
29029
- )
29030
- ) : null
29031
- );
29503
+ const result = event ? boundSessionEventCompactResult(
29504
+ compactSessionEventResult2(
29505
+ event,
29506
+ latestClass,
29507
+ dbPage.coveredSequence ?? {
29508
+ first: event.sequence,
29509
+ last: event.sequence
29510
+ }
29511
+ )
29512
+ ) : null;
29513
+ if (result && ["turn.completed", "agent.message.completed"].includes(result.type) && !result.truncation.truncated && dbPage.fullPayloadsExact) {
29514
+ await acknowledgeReads([{ sessionId, sequences: [result.sequence] }]);
29515
+ }
29516
+ return json(result);
29032
29517
  }
29033
- return json(
29034
- boundSessionEventMcpPage({
29035
- events: dbPage.events,
29036
- mode,
29037
- payloadMode,
29038
- direction,
29039
- sourceHasMore: dbPage.hasMore,
29040
- sourceTruncatedBy: dbPage.truncatedBy,
29041
- after: after ?? 0,
29042
- before: before ?? null
29043
- })
29044
- );
29518
+ const page = boundSessionEventMcpPage({
29519
+ events: dbPage.events,
29520
+ mode,
29521
+ payloadMode,
29522
+ direction,
29523
+ sourceHasMore: dbPage.hasMore,
29524
+ sourceTruncatedBy: dbPage.truncatedBy,
29525
+ after: after ?? 0,
29526
+ before: before ?? null
29527
+ });
29528
+ if (payloadMode === "full" && dbPage.fullPayloadsExact && !page.truncation?.reasons.includes("model_payload")) {
29529
+ await acknowledgeReads([
29530
+ { sessionId, sequences: page.events.map((event) => event.sequence) }
29531
+ ]);
29532
+ }
29533
+ return json(page);
29045
29534
  }
29046
29535
  );
29047
29536
  server.registerTool(
@@ -29077,45 +29566,52 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
29077
29566
  const targetEventTypes = waitFor === "completion" ? SESSION_WAIT_COMPLETION_EVENT_TYPES : SESSION_WAIT_EVENT_TYPES;
29078
29567
  const signal = extra?.signal;
29079
29568
  const workspaceId = grant.workspaceId;
29080
- return json(
29081
- await waitForSessionChanges({
29082
- targets,
29083
- ownSessionId,
29084
- maxWaitMs: (maxWaitSeconds ?? SESSION_WAIT_DEFAULT_SECONDS) * 1e3,
29085
- targetEventTypes,
29086
- targetEventMatches: waitFor === "completion" ? sessionWaitCompletionEventMatches : void 0,
29087
- signal,
29088
- source: {
29089
- reauthorizeTargets: async (sessionIds) => {
29090
- for (const targetSessionId of sessionIds) {
29091
- await authorizeFirstPartySession(
29092
- deps,
29093
- grant,
29094
- targetSessionId,
29095
- "session.events.read"
29096
- );
29097
- }
29098
- },
29099
- readTargetEvents: async (target) => {
29100
- const page = await listSessionEventPage(deps.db, workspaceId, target.sessionId, {
29101
- after: target.afterSequence,
29102
- direction: "after",
29103
- limit: SESSION_WAIT_EVENTS_PER_TARGET,
29104
- payloadMode: "full",
29105
- includeTypes: targetEventTypes,
29106
- maxBytes: SESSION_EVENT_MCP_MAX_BYTES * 4
29107
- });
29108
- return { events: page.events, hasMore: page.hasMore };
29109
- },
29110
- readOwnPendingUpdateKinds: ownSessionId === null ? null : async () => (await listOutstandingSessionSystemUpdates(
29111
- deps.db,
29112
- workspaceId,
29113
- ownSessionId
29114
- )).map((update) => update.kind),
29115
- subscribe: (targetSessionId, onEvents) => deps.bus.subscribe(workspaceId, targetSessionId, onEvents)
29116
- }
29117
- })
29118
- );
29569
+ const incompleteWaitEvents = /* @__PURE__ */ new Set();
29570
+ const result = await waitForSessionChanges({
29571
+ targets,
29572
+ ownSessionId,
29573
+ maxWaitMs: (maxWaitSeconds ?? SESSION_WAIT_DEFAULT_SECONDS) * 1e3,
29574
+ targetEventTypes,
29575
+ targetEventMatches: waitFor === "completion" ? sessionWaitCompletionEventMatches : void 0,
29576
+ signal,
29577
+ source: {
29578
+ reauthorizeTargets: async (sessionIds) => {
29579
+ for (const targetSessionId of sessionIds) {
29580
+ await authorizeFirstPartySession(
29581
+ deps,
29582
+ grant,
29583
+ targetSessionId,
29584
+ "session.events.read"
29585
+ );
29586
+ }
29587
+ },
29588
+ readTargetEvents: async (target) => {
29589
+ const page = await listSessionEventPage(deps.db, workspaceId, target.sessionId, {
29590
+ after: target.afterSequence,
29591
+ direction: "after",
29592
+ limit: SESSION_WAIT_EVENTS_PER_TARGET,
29593
+ payloadMode: "full",
29594
+ includeTypes: targetEventTypes,
29595
+ maxBytes: SESSION_EVENT_MCP_MAX_BYTES * 4
29596
+ });
29597
+ if (!page.fullPayloadsExact) {
29598
+ for (const event of page.events) incompleteWaitEvents.add(event.id);
29599
+ }
29600
+ return { events: page.events, hasMore: page.hasMore };
29601
+ },
29602
+ readOwnPendingUpdateKinds: ownSessionId === null ? null : async () => (await listOutstandingSessionSystemUpdates(deps.db, workspaceId, ownSessionId)).map((update) => update.kind),
29603
+ subscribe: (targetSessionId, onEvents) => deps.bus.subscribe(workspaceId, targetSessionId, onEvents)
29604
+ }
29605
+ });
29606
+ if (!result.aborted && !result.truncated) {
29607
+ await acknowledgeReads(
29608
+ result.changed.map((target) => ({
29609
+ sessionId: target.sessionId,
29610
+ sequences: target.events.filter((event) => event.contentComplete && !incompleteWaitEvents.has(event.id)).map((event) => event.sequence)
29611
+ }))
29612
+ );
29613
+ }
29614
+ return json(result);
29119
29615
  }
29120
29616
  );
29121
29617
  if (callerSessionId !== null) {
@@ -29985,9 +30481,12 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json)
29985
30481
  };
29986
30482
  const setupProjections = async (items) => {
29987
30483
  const availableServerIds = /* @__PURE__ */ new Set();
30484
+ const availableFirstPartyTools = /* @__PURE__ */ new Set();
29988
30485
  const acceptedServerIds = /* @__PURE__ */ new Set();
29989
30486
  let accountSelectionKnown = false;
29990
- if (items.some((item) => item.enabled && item.connectionRef)) {
30487
+ if (items.some(
30488
+ (item) => item.enabled && (item.connectionRef || item.surfaceType === "first_party_fiken")
30489
+ )) {
29991
30490
  const claims = exactAgentCommandContext(grant, sessionId);
29992
30491
  const attemptCatalog = await getAttemptToolCatalog2(deps.db, {
29993
30492
  accountId: grant.accountId,
@@ -29995,7 +30494,11 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json)
29995
30494
  attemptId: claims.callerAttemptId
29996
30495
  });
29997
30496
  if (attemptCatalog?.sessionId === sessionId && attemptCatalog.turnId === claims.callerTurnId && attemptCatalog.executionGeneration === claims.callerExecutionGeneration) {
29998
- for (const entry2 of attemptCatalog.entries) availableServerIds.add(entry2.identity.serverId);
30497
+ for (const entry2 of attemptCatalog.entries) {
30498
+ availableServerIds.add(entry2.identity.serverId);
30499
+ if (entry2.identity.serverId === "opengeni")
30500
+ availableFirstPartyTools.add(entry2.identity.toolName);
30501
+ }
29999
30502
  const bindings = await getSessionTurnMcpAccountBindings(
30000
30503
  deps.db,
30001
30504
  grant.workspaceId,
@@ -30016,7 +30519,9 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json)
30016
30519
  item,
30017
30520
  availableServerIds,
30018
30521
  acceptedServerIds,
30019
- accountSelectionKnown
30522
+ accountSelectionKnown,
30523
+ availableFirstPartyTools,
30524
+ new Set(grant.metadata?.firstPartyMcpTools ?? [])
30020
30525
  )
30021
30526
  )
30022
30527
  );
@@ -30135,7 +30640,38 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json)
30135
30640
  }
30136
30641
  );
30137
30642
  }
30138
- async function capabilitySetupProjection(deps, workspaceId, item, availableServerIds, acceptedServerIds, accountSelectionKnown) {
30643
+ async function capabilitySetupProjection(deps, workspaceId, item, availableServerIds, acceptedServerIds, accountSelectionKnown, availableFirstPartyTools, selectedFirstPartyTools) {
30644
+ if (item.surfaceType === "first_party_fiken") {
30645
+ if (!item.enabled || item.metadata.connectionStatus !== "active") {
30646
+ return {
30647
+ status: "authorization_required",
30648
+ action: "connect",
30649
+ detail: item.metadata.connectionStatus === "needs_reauth" ? "The workspace Fiken connection needs reconnection." : "Connect Fiken through the protected setup form."
30650
+ };
30651
+ }
30652
+ const selected = FIRST_PARTY_MCP_TOOL_NAMES2.filter(
30653
+ (name) => name.startsWith("fiken_") && selectedFirstPartyTools.has(name)
30654
+ );
30655
+ if (selected.length === 0) {
30656
+ return {
30657
+ status: "authorization_required",
30658
+ action: "enable",
30659
+ detail: "Fiken is connected, but its tools are not selected for this conversation. Ask the user to add its tools through the setup card, then send a new message."
30660
+ };
30661
+ }
30662
+ if (selected.some((name) => availableFirstPartyTools.has(name))) {
30663
+ return {
30664
+ status: "ready",
30665
+ action: null,
30666
+ detail: "Fiken is connected and has tools available in this turn."
30667
+ };
30668
+ }
30669
+ return {
30670
+ status: "unavailable",
30671
+ action: null,
30672
+ detail: "Fiken is connected, but its selected tools are unavailable in this execution. Check tool permissions and setup before retrying; reconnection is not required by this status."
30673
+ };
30674
+ }
30139
30675
  if (item.id === "api:github-app" || item.surfaceType === "first_party_github") {
30140
30676
  const missing = githubAppMissingSettings(deps.settings);
30141
30677
  if (missing.length > 0) {
@@ -60552,7 +61088,7 @@ function registerOrganizationIntegrationPolicyRoutes(app, deps) {
60552
61088
  }
60553
61089
 
60554
61090
  // src/routes/connections.ts
60555
- import { createHash as createHash14, createHmac as createHmac4 } from "crypto";
61091
+ import { createHash as createHash15, createHmac as createHmac4 } from "crypto";
60556
61092
  import {
60557
61093
  createConnectionIdempotently,
60558
61094
  getConnectionCreationResult,
@@ -60721,7 +61257,7 @@ import { HTTPException as HTTPException35 } from "hono/http-exception";
60721
61257
  // src/integrations/provider-oauth.ts
60722
61258
  import { withOrganizationIntegrationAcquisition as withOrganizationIntegrationAcquisition6 } from "@opengeni/db/organization-integration-policy";
60723
61259
  import { Buffer as Buffer4 } from "buffer";
60724
- import { createHash as createHash12, randomBytes as randomBytes6 } from "crypto";
61260
+ import { createHash as createHash13, randomBytes as randomBytes7 } from "crypto";
60725
61261
  import {
60726
61262
  integrationDefinitionProviderDomain as integrationDefinitionProviderDomain2,
60727
61263
  integrationDefinitionById
@@ -60804,7 +61340,7 @@ async function startApiIntegrationProviderOAuth(deps, input) {
60804
61340
  ...definition.authentication.scopes
60805
61341
  ]);
60806
61342
  const client = providerClientForDefinition(deps.settings, definition);
60807
- const verifier = randomBytes6(48).toString("base64url");
61343
+ const verifier = randomBytes7(48).toString("base64url");
60808
61344
  const key = requireEnvironmentEncryption8(deps.settings);
60809
61345
  const baseUrl = integrationBaseUrl(deps.settings.publicBaseUrl, input.requestUrl);
60810
61346
  const redirectUri = `${baseUrl}${providerOAuthCallbackPath(definition)}`;
@@ -60849,7 +61385,7 @@ async function startApiIntegrationProviderOAuth(deps, input) {
60849
61385
  authorizationUrl.searchParams.set("code_challenge_method", "S256");
60850
61386
  authorizationUrl.searchParams.set(
60851
61387
  "code_challenge",
60852
- createHash12("sha256").update(verifier).digest("base64url")
61388
+ createHash13("sha256").update(verifier).digest("base64url")
60853
61389
  );
60854
61390
  authorizationUrl.searchParams.set("prompt", "select_account");
60855
61391
  if (definition.provider.id === "google") {
@@ -60879,7 +61415,7 @@ async function completeApiIntegrationProviderOAuth(deps, input) {
60879
61415
  connectOperation = {
60880
61416
  attemptId: state.connectAttemptId,
60881
61417
  operationId: `oauth:${state.nonce}`,
60882
- inputDigest: createHash12("sha256").update(input.state).digest("hex")
61418
+ inputDigest: createHash13("sha256").update(input.state).digest("hex")
60883
61419
  };
60884
61420
  const claim = await claimOAuthAcquisition(
60885
61421
  deps.db,
@@ -61172,7 +61708,7 @@ function providerClientKeys(definition) {
61172
61708
  ]);
61173
61709
  }
61174
61710
  function providerDefinitionFingerprint(definition) {
61175
- return createHash12("sha256").update(
61711
+ return createHash13("sha256").update(
61176
61712
  JSON.stringify({
61177
61713
  id: definition.id,
61178
61714
  provider: definition.provider,
@@ -61425,7 +61961,7 @@ function numberValue4(value) {
61425
61961
 
61426
61962
  // src/integrations/personal-github.ts
61427
61963
  import { withOrganizationIntegrationAcquisition as withOrganizationIntegrationAcquisition7 } from "@opengeni/db/organization-integration-policy";
61428
- import { createHash as createHash13, randomBytes as randomBytes7, randomUUID as randomUUID3 } from "crypto";
61964
+ import { createHash as createHash14, randomBytes as randomBytes8, randomUUID as randomUUID4 } from "crypto";
61429
61965
  import { ExternalActorContinuation as ExternalActorContinuation8 } from "@opengeni/contracts/external-identities";
61430
61966
  import { personalGitHubOAuthCallbackUrl } from "@opengeni/config";
61431
61967
  import {
@@ -61472,7 +62008,7 @@ var PersonalGitHubCallbackError = class extends Error {
61472
62008
  }
61473
62009
  };
61474
62010
  function personalGitHubClientMarker(clientId) {
61475
- return createHash13("sha256").update(clientId).digest("hex").slice(0, 32);
62011
+ return createHash14("sha256").update(clientId).digest("hex").slice(0, 32);
61476
62012
  }
61477
62013
  function requirePersonalGitHubOAuthSettings(settings) {
61478
62014
  if (!settings.githubPersonalOauthEnabled) {
@@ -61555,7 +62091,7 @@ async function startPersonalGitHubOAuth(deps, input) {
61555
62091
  });
61556
62092
  }
61557
62093
  const key = requireEnvironmentEncryption9(deps.settings);
61558
- const verifier = randomBytes7(48).toString("base64url");
62094
+ const verifier = randomBytes8(48).toString("base64url");
61559
62095
  const returnPath = personalGitHubReturnPath(input.workspaceId, input.returnPath);
61560
62096
  const state = createSignedState11(requireIntegrationsStateSecret(deps.settings), {
61561
62097
  accountId: grant.accountId,
@@ -61584,7 +62120,7 @@ async function startPersonalGitHubOAuth(deps, input) {
61584
62120
  authorizationUrl.searchParams.set("state", state);
61585
62121
  authorizationUrl.searchParams.set(
61586
62122
  "code_challenge",
61587
- createHash13("sha256").update(verifier).digest("base64url")
62123
+ createHash14("sha256").update(verifier).digest("base64url")
61588
62124
  );
61589
62125
  authorizationUrl.searchParams.set("code_challenge_method", "S256");
61590
62126
  return PersonalGitHubOAuthStartResponse.parse({
@@ -61608,7 +62144,7 @@ async function completePersonalGitHubOAuthCallback(deps, input) {
61608
62144
  operation = {
61609
62145
  attemptId: state.connectAttemptId,
61610
62146
  operationId: `oauth:${state.nonce}`,
61611
- inputDigest: createHash13("sha256").update(input.state).digest("hex")
62147
+ inputDigest: createHash14("sha256").update(input.state).digest("hex")
61612
62148
  };
61613
62149
  const claim = await claimOAuthAcquisition(
61614
62150
  deps.db,
@@ -61710,7 +62246,7 @@ async function completePersonalGitHubOAuthCallback(deps, input) {
61710
62246
  githubLogin: identity2.login,
61711
62247
  oauthEnvironment: oauth.oauthEnvironment,
61712
62248
  oauthClientMarker: oauth.oauthClientMarker,
61713
- credentialBindingId: requestedMetadata?.credentialBindingId ?? randomUUID3(),
62249
+ credentialBindingId: requestedMetadata?.credentialBindingId ?? randomUUID4(),
61714
62250
  connectedAt: requestedMetadata?.connectedAt ?? now,
61715
62251
  lastVerifiedAt: now,
61716
62252
  disconnectedAt: null,
@@ -62255,7 +62791,7 @@ function registerConnectionRoutes(app, deps) {
62255
62791
  operation = {
62256
62792
  attemptId: state.connectAttemptId,
62257
62793
  operationId: `oauth:${state.nonce}`,
62258
- inputDigest: createHash14("sha256").update(c.req.query("state")).digest("hex")
62794
+ inputDigest: createHash15("sha256").update(c.req.query("state")).digest("hex")
62259
62795
  };
62260
62796
  const callbackState = state;
62261
62797
  const callbackOperation = operation;
@@ -62414,7 +62950,7 @@ function registerConnectionRoutes(app, deps) {
62414
62950
  accountId: state.accountId,
62415
62951
  workspaceId: state.workspaceId,
62416
62952
  subjectId: state.subjectId,
62417
- callbackDigest: createHash14("sha256").update(state.nonce).digest("hex"),
62953
+ callbackDigest: createHash15("sha256").update(state.nonce).digest("hex"),
62418
62954
  installMode: state.connectionId ? "reinstall" : "connect",
62419
62955
  ...failure
62420
62956
  });
@@ -63445,7 +63981,7 @@ function slackBotCredentialBundle(token) {
63445
63981
  }
63446
63982
 
63447
63983
  // src/routes/connect.ts
63448
- import { createHash as createHash17, createHmac as createHmac5, randomUUID as randomUUID4 } from "crypto";
63984
+ import { createHash as createHash18, createHmac as createHmac5, randomUUID as randomUUID5 } from "crypto";
63449
63985
  import { HTTPException as HTTPException39 } from "hono/http-exception";
63450
63986
  import { getConnectBeginReplay } from "@opengeni/db/connect-attempts";
63451
63987
  import {
@@ -63496,7 +64032,7 @@ import {
63496
64032
  } from "@opengeni/core";
63497
64033
 
63498
64034
  // src/integrations/github-app-connect.ts
63499
- import { createHash as createHash15 } from "crypto";
64035
+ import { createHash as createHash16 } from "crypto";
63500
64036
  import { z as z19 } from "zod";
63501
64037
  import { HTTPException as HTTPException37 } from "hono/http-exception";
63502
64038
  import {
@@ -63705,7 +64241,7 @@ async function completeGitHubAppConnect(deps, input) {
63705
64241
  const operation = {
63706
64242
  attemptId: state.connectAttemptId,
63707
64243
  operationId: `github:${state.nonce}`,
63708
- inputDigest: createHash15("sha256").update(input.state).digest("hex"),
64244
+ inputDigest: createHash16("sha256").update(input.state).digest("hex"),
63709
64245
  authorize
63710
64246
  };
63711
64247
  const claim = await claimOAuthAcquisition(
@@ -63956,7 +64492,7 @@ import {
63956
64492
  import { HTTPException as HTTPException38 } from "hono/http-exception";
63957
64493
 
63958
64494
  // src/integrations/api-integrations.ts
63959
- import { createHash as createHash16 } from "crypto";
64495
+ import { createHash as createHash17 } from "crypto";
63960
64496
  import {
63961
64497
  compileGraphqlRevision,
63962
64498
  compileOpenApiRevision,
@@ -64175,7 +64711,7 @@ function resolvedPreview(input) {
64175
64711
  };
64176
64712
  }
64177
64713
  function integrationInstallationIdentity(protocol, label, locator2) {
64178
- const hash = createHash16("sha256").update(`${protocol}\0${locator2}`).digest("hex").slice(0, 12);
64714
+ const hash = createHash17("sha256").update(`${protocol}\0${locator2}`).digest("hex").slice(0, 12);
64179
64715
  const slug = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "integration";
64180
64716
  return {
64181
64717
  sourceDerivedDefinitionId: `${slug}-${hash}`,
@@ -65000,7 +65536,7 @@ function registerConnectRoutes(app, deps) {
65000
65536
  attemptId: before.id,
65001
65537
  expectedRevision: input.expectedRevision,
65002
65538
  operationId: input.idempotencyKey,
65003
- inputDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
65539
+ inputDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65004
65540
  authorize: (tx, _attempt, origin) => requireConnectOwnerAuthority(tx, scope, "capabilities:manage", origin),
65005
65541
  execute: async (attempt) => {
65006
65542
  if (attempt.state !== "credential_input")
@@ -65042,7 +65578,7 @@ function registerConnectRoutes(app, deps) {
65042
65578
  attemptId: before.id,
65043
65579
  expectedRevision: input.expectedRevision,
65044
65580
  operationId: input.idempotencyKey,
65045
- inputDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
65581
+ inputDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65046
65582
  authorize: async (tx, _attempt, origin) => {
65047
65583
  await requireConnectOwnerAuthority(tx, scope, installationPermission, origin);
65048
65584
  if (before.providerId === "github-lens") {
@@ -65068,7 +65604,7 @@ function registerConnectRoutes(app, deps) {
65068
65604
  attemptId: before.id,
65069
65605
  expectedRevision: input.expectedRevision,
65070
65606
  operationId: input.idempotencyKey,
65071
- inputDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
65607
+ inputDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65072
65608
  authorize: (tx, _attempt, origin) => requireConnectOwnerAuthority(tx, scope, "connections:write", origin),
65073
65609
  execute: async (attempt) => {
65074
65610
  if (attempt.state !== "credential_input")
@@ -65367,7 +65903,7 @@ function registerConnectRoutes(app, deps) {
65367
65903
  attemptId: c.req.param("attemptId"),
65368
65904
  expectedRevision: input.expectedRevision,
65369
65905
  operationId: input.idempotencyKey,
65370
- inputDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
65906
+ inputDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65371
65907
  authorize: (tx, _attempt, origin) => requireConnectOwnerAuthority(tx, scope, permission, origin),
65372
65908
  execute: async (attempt) => {
65373
65909
  if (!attempt.source && (!attempt.credentialsCommitted || !attempt.account) || !["connected_but_incomplete", "preview"].includes(attempt.state))
@@ -65475,7 +66011,7 @@ function registerConnectRoutes(app, deps) {
65475
66011
  attemptId: c.req.param("attemptId"),
65476
66012
  expectedRevision: input.expectedRevision,
65477
66013
  operationId: input.idempotencyKey,
65478
- inputDigest: createHash17("sha256").update(stableJson4({ ...input, action: "cancel" })).digest("hex"),
66014
+ inputDigest: createHash18("sha256").update(stableJson4({ ...input, action: "cancel" })).digest("hex"),
65479
66015
  purpose: "cancellation",
65480
66016
  authorize: async (tx, attempt, origin) => {
65481
66017
  const permission = attempt.providerId === "mcp-install" ? "capabilities:manage" : ["x", "reddit"].includes(attempt.providerId) ? attempt.ownership === "workspace" ? "workspace:admin" : "workspace:read" : attempt.providerId === "github-app" ? "github:manage" : attempt.providerId === "github-lens" ? "workspace:admin" : "connections:write";
@@ -65540,7 +66076,7 @@ function registerConnectRoutes(app, deps) {
65540
66076
  await requireConnectOwnerAuthority(actorTx, scope, setupPermission);
65541
66077
  const replay2 = await getConnectBeginReplay(actorTx, scope, {
65542
66078
  idempotencyKey: input.idempotencyKey,
65543
- requestDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
66079
+ requestDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65544
66080
  returnUrl: input.returnUrl,
65545
66081
  ...continuation ? { externalContinuation: continuation } : {}
65546
66082
  });
@@ -65574,7 +66110,7 @@ function registerConnectRoutes(app, deps) {
65574
66110
  }
65575
66111
  );
65576
66112
  });
65577
- const id = randomUUID4();
66113
+ const id = randomUUID5();
65578
66114
  if (input.providerId === "mcp-install") {
65579
66115
  if (input.ownership !== "workspace" || input.reconnectAccountId || input.installationTarget)
65580
66116
  throw new HTTPException39(422, {
@@ -65596,7 +66132,7 @@ function registerConnectRoutes(app, deps) {
65596
66132
  return beginConnectAttempt(tx, scope, {
65597
66133
  ...continuation ? { externalContinuation: continuation } : {},
65598
66134
  idempotencyKey: input.idempotencyKey,
65599
- requestDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
66135
+ requestDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65600
66136
  returnUrl: input.returnUrl,
65601
66137
  attempt: {
65602
66138
  id,
@@ -65654,7 +66190,7 @@ function registerConnectRoutes(app, deps) {
65654
66190
  return beginConnectAttempt(tx, scope, {
65655
66191
  ...continuation ? { externalContinuation: continuation } : {},
65656
66192
  idempotencyKey: input.idempotencyKey,
65657
- requestDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
66193
+ requestDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65658
66194
  returnUrl: input.returnUrl,
65659
66195
  attempt: {
65660
66196
  id,
@@ -65704,7 +66240,7 @@ function registerConnectRoutes(app, deps) {
65704
66240
  return beginConnectAttempt(tx, scope, {
65705
66241
  ...continuation ? { externalContinuation: continuation } : {},
65706
66242
  idempotencyKey: input.idempotencyKey,
65707
- requestDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
66243
+ requestDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65708
66244
  returnUrl: input.returnUrl,
65709
66245
  attempt: {
65710
66246
  id,
@@ -65739,7 +66275,7 @@ function registerConnectRoutes(app, deps) {
65739
66275
  return beginConnectAttempt(tx, scope, {
65740
66276
  ...continuation ? { externalContinuation: continuation } : {},
65741
66277
  idempotencyKey: input.idempotencyKey,
65742
- requestDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
66278
+ requestDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65743
66279
  returnUrl: input.returnUrl,
65744
66280
  attempt: {
65745
66281
  id,
@@ -65820,7 +66356,7 @@ function registerConnectRoutes(app, deps) {
65820
66356
  return beginConnectAttempt(tx, scope, {
65821
66357
  ...continuation ? { externalContinuation: continuation } : {},
65822
66358
  idempotencyKey: input.idempotencyKey,
65823
- requestDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
66359
+ requestDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65824
66360
  returnUrl: input.returnUrl,
65825
66361
  attempt: {
65826
66362
  id,
@@ -65958,7 +66494,7 @@ function registerConnectRoutes(app, deps) {
65958
66494
  return beginConnectAttempt(tx, scope, {
65959
66495
  ...continuation ? { externalContinuation: continuation } : {},
65960
66496
  idempotencyKey: input.idempotencyKey,
65961
- requestDigest: createHash17("sha256").update(stableJson4(input)).digest("hex"),
66497
+ requestDigest: createHash18("sha256").update(stableJson4(input)).digest("hex"),
65962
66498
  returnUrl: input.returnUrl,
65963
66499
  attempt: {
65964
66500
  id,
@@ -66947,7 +67483,8 @@ function registerDocumentRoutes(app, deps) {
66947
67483
  deps,
66948
67484
  await requireAccessGrantAuthorization8(c, deps, workspaceId, "documents:search"),
66949
67485
  "documents:search"
66950
- )
67486
+ ),
67487
+ settings: deps.settings
66951
67488
  }
66952
67489
  );
66953
67490
  await server.connect(transport);
@@ -67015,7 +67552,7 @@ function requireOrganizationDocumentAuthority(authorityKind, organizationAuthori
67015
67552
  }
67016
67553
 
67017
67554
  // src/routes/knowledge.ts
67018
- import { retryWhileMissing as retryWhileMissing4 } from "@opengeni/storage";
67555
+ import { retryWhileMissing as retryWhileMissing5 } from "@opengeni/storage";
67019
67556
  import { allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession3 } from "@opengeni/config";
67020
67557
  import {
67021
67558
  AgentLearningContext,
@@ -67034,6 +67571,8 @@ import {
67034
67571
  prepareKnowledgeFile as prepareKnowledgeFile2,
67035
67572
  prepareKnowledgeSave as prepareKnowledgeSave2,
67036
67573
  searchKnowledgeEntries as searchKnowledgeEntries3,
67574
+ KnowledgeVectorFundingError,
67575
+ KnowledgeVectorQueryRejectedError,
67037
67576
  hasPermission as hasPermission13,
67038
67577
  knowledgeContextForAccess as knowledgeContextForAccess2,
67039
67578
  requireAccessGrantAuthorization as requireAccessGrantAuthorization9,
@@ -67073,6 +67612,18 @@ var WriteSettings = ReadSettings.extend({
67073
67612
  var Id = z22.uuid();
67074
67613
  function knowledgeHttpError(error) {
67075
67614
  if (error instanceof HTTPException42) throw error;
67615
+ if (error instanceof KnowledgeVectorFundingError)
67616
+ throw new ApiHttpError(402, {
67617
+ code: "payment_required",
67618
+ message: error.message,
67619
+ details: { code: error.code, keywordAvailable: true }
67620
+ });
67621
+ if (error instanceof KnowledgeVectorQueryRejectedError)
67622
+ throw new ApiHttpError(error.code === "quota" ? 429 : 422, {
67623
+ code: error.code === "quota" ? "limit_exceeded" : "validation_failed",
67624
+ message: error.message,
67625
+ details: { code: error.code, keywordAvailable: true }
67626
+ });
67076
67627
  if (error instanceof z22.ZodError)
67077
67628
  throw new HTTPException42(422, {
67078
67629
  message: error.issues[0]?.message ?? "Invalid Knowledge request"
@@ -67155,7 +67706,7 @@ function registerKnowledgeRoutes(app, deps) {
67155
67706
  if (!file) throw new HTTPException42(404, { message: "Original file unavailable" });
67156
67707
  if (!deps.objectStorage)
67157
67708
  throw new HTTPException42(503, { message: "File storage is unavailable" });
67158
- if (!await retryWhileMissing4(
67709
+ if (!await retryWhileMissing5(
67159
67710
  async () => await deps.objectStorage.fileExists(file) ? true : null
67160
67711
  ))
67161
67712
  throw new HTTPException42(410, { message: "Original file bytes are unavailable" });
@@ -67204,7 +67755,9 @@ function registerKnowledgeRoutes(app, deps) {
67204
67755
  deps.db,
67205
67756
  context,
67206
67757
  KnowledgeSavePreparationRequest2.parse(await c.req.json()),
67207
- () => deps.getDocumentServices().embedder
67758
+ () => deps.getDocumentServices().embedder,
67759
+ void 0,
67760
+ deps.settings
67208
67761
  )
67209
67762
  );
67210
67763
  })
@@ -67219,7 +67772,8 @@ function registerKnowledgeRoutes(app, deps) {
67219
67772
  deps.db,
67220
67773
  context,
67221
67774
  KnowledgeEntryListRequest3.parse(await c.req.json()),
67222
- () => deps.getDocumentServices().embedder
67775
+ () => deps.getDocumentServices().embedder,
67776
+ deps.settings
67223
67777
  )
67224
67778
  )
67225
67779
  )
@@ -67266,7 +67820,8 @@ function registerKnowledgeRoutes(app, deps) {
67266
67820
  ...query,
67267
67821
  ...query.limit ? { limit: Number(query.limit) } : {}
67268
67822
  }),
67269
- () => deps.getDocumentServices().embedder
67823
+ () => deps.getDocumentServices().embedder,
67824
+ deps.settings
67270
67825
  )
67271
67826
  );
67272
67827
  })
@@ -67432,7 +67987,7 @@ function registerKnowledgeRoutes(app, deps) {
67432
67987
  }
67433
67988
 
67434
67989
  // src/routes/enrollments.ts
67435
- import { randomUUID as randomUUID5 } from "crypto";
67990
+ import { randomUUID as randomUUID6 } from "crypto";
67436
67991
  import {
67437
67992
  DeviceEnrollmentApproveRequest,
67438
67993
  DeviceEnrollmentApproveResponse,
@@ -67464,297 +68019,6 @@ import {
67464
68019
  requireAccessGrantAuthorization as requireAccessGrantAuthorization10,
67465
68020
  requirePermission
67466
68021
  } from "@opengeni/core";
67467
-
67468
- // src/sandbox/enrollment.ts
67469
- import { randomBytes as randomBytes8 } from "crypto";
67470
- import {
67471
- resolveEnrollmentSigningSecret,
67472
- resolveRelayTokenSecret
67473
- } from "@opengeni/config";
67474
- import {
67475
- DeviceEnrollmentState,
67476
- signEnrollmentBearer,
67477
- signEnrollToken,
67478
- signRelayToken,
67479
- verifyEnrollToken
67480
- } from "@opengeni/contracts";
67481
- import {
67482
- approveDeviceEnrollmentRequest,
67483
- consumeDeviceEnrollmentRequest,
67484
- createDeviceEnrollmentRequest,
67485
- denyDeviceEnrollmentRequest,
67486
- finalizeEnrollmentByToken,
67487
- getDeviceEnrollmentRequestByDeviceCode,
67488
- getEnrollment,
67489
- getPendingDeviceEnrollmentRequestByUserCode,
67490
- getPendingDeviceEnrollmentRequestByUserCodeGlobal
67491
- } from "@opengeni/db";
67492
- import { relayDialBaseFromSettings } from "@opengeni/core";
67493
- var DEVICE_CODE_TTL_SECONDS = 600;
67494
- var DEVICE_POLL_INTERVAL_SECONDS = 5;
67495
- var ENROLLMENT_BEARER_TTL_SECONDS = 30 * 24 * 3600;
67496
- var RELAY_TOKEN_TTL_SECONDS = 30 * 24 * 3600;
67497
- var ENROLL_TOKEN_TTL_SECONDS = 3600;
67498
- function mintDeviceCode() {
67499
- return randomBytes8(32).toString("base64url");
67500
- }
67501
- var USER_CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
67502
- function mintUserCode() {
67503
- const bytes = randomBytes8(8);
67504
- let out = "";
67505
- for (let i = 0; i < 8; i += 1) {
67506
- out += USER_CODE_ALPHABET[bytes[i] % USER_CODE_ALPHABET.length];
67507
- }
67508
- return `${out.slice(0, 4)}-${out.slice(4, 8)}`;
67509
- }
67510
- async function startDeviceEnrollment(services, input) {
67511
- const { db } = services;
67512
- const expiresAt = new Date(Date.now() + DEVICE_CODE_TTL_SECONDS * 1e3);
67513
- let request;
67514
- let lastError;
67515
- for (let attempt = 0; attempt < 5 && !request; attempt += 1) {
67516
- const deviceCode = mintDeviceCode();
67517
- const userCode = mintUserCode();
67518
- try {
67519
- request = await createDeviceEnrollmentRequest(db, {
67520
- accountId: input.accountId,
67521
- workspaceId: input.workspaceId,
67522
- deviceCode,
67523
- userCode,
67524
- pubkey: input.publicKey,
67525
- os: input.os,
67526
- arch: input.arch,
67527
- machineName: input.machineName ?? null,
67528
- requestedExposure: "whole-machine",
67529
- canOfferDisplay: input.canOfferDisplay,
67530
- requestsScreenControl: input.requestsScreenControl,
67531
- expiresAt
67532
- });
67533
- } catch (error) {
67534
- lastError = error;
67535
- }
67536
- }
67537
- if (!request) {
67538
- throw lastError instanceof Error ? lastError : new Error("failed to start device enrollment");
67539
- }
67540
- const base = input.verificationOrigin.replace(/\/+$/, "");
67541
- const verificationUri = `${base}/device`;
67542
- const verificationUriComplete = `${verificationUri}?user_code=${encodeURIComponent(request.userCode)}`;
67543
- return {
67544
- deviceCode: request.deviceCode,
67545
- userCode: request.userCode,
67546
- verificationUri,
67547
- verificationUriComplete,
67548
- intervalSeconds: DEVICE_POLL_INTERVAL_SECONDS,
67549
- expiresInSeconds: DEVICE_CODE_TTL_SECONDS
67550
- };
67551
- }
67552
- async function approveDeviceEnrollment(services, input) {
67553
- const { db } = services;
67554
- const pending = await getPendingDeviceEnrollmentRequestByUserCode(
67555
- db,
67556
- input.workspaceId,
67557
- input.userCode
67558
- );
67559
- if (!pending) {
67560
- return null;
67561
- }
67562
- const sandboxName = (pending.machineName?.trim() || `${pending.os} machine`).slice(0, 256);
67563
- const result = await approveDeviceEnrollmentRequest(db, {
67564
- accountId: input.accountId,
67565
- workspaceId: input.workspaceId,
67566
- ...input.scope ? { scope: input.scope } : {},
67567
- allowOrganization: input.allowOrganization === true,
67568
- requestId: pending.id,
67569
- allowScreenControl: input.allowScreenControl,
67570
- approvedBySubjectId: input.approvedBySubjectId,
67571
- approvedBySubjectLabel: input.approvedBySubjectLabel ?? null,
67572
- sandboxName
67573
- });
67574
- if (!result.approved || !result.enrollment || !result.sandbox) {
67575
- return null;
67576
- }
67577
- return {
67578
- enrollmentId: result.enrollment.id,
67579
- sandboxId: result.sandbox.id,
67580
- allowScreenControl: result.enrollment.allowScreenControl
67581
- };
67582
- }
67583
- async function lookupDeviceEnrollment(services, input) {
67584
- const { db } = services;
67585
- return await getPendingDeviceEnrollmentRequestByUserCodeGlobal(db, input.userCode);
67586
- }
67587
- function toLookupResponse(record6) {
67588
- return {
67589
- workspaceId: record6.workspaceId,
67590
- userCode: record6.userCode,
67591
- machine: {
67592
- machineName: record6.machineName,
67593
- os: record6.os,
67594
- arch: record6.arch,
67595
- canOfferDisplay: record6.canOfferDisplay,
67596
- requestsScreenControl: record6.requestsScreenControl
67597
- },
67598
- expiresAt: record6.expiresAt
67599
- };
67600
- }
67601
- async function denyDeviceEnrollment(services, input) {
67602
- const { db } = services;
67603
- const pending = await getPendingDeviceEnrollmentRequestByUserCode(
67604
- db,
67605
- input.workspaceId,
67606
- input.userCode
67607
- );
67608
- if (!pending) {
67609
- return { denied: false };
67610
- }
67611
- return await denyDeviceEnrollmentRequest(db, {
67612
- accountId: input.accountId,
67613
- workspaceId: input.workspaceId,
67614
- requestId: pending.id
67615
- });
67616
- }
67617
- async function mintEnrollToken(services, input) {
67618
- const { settings } = services;
67619
- const secret = resolveEnrollmentSigningSecret(settings);
67620
- if (!secret) {
67621
- return null;
67622
- }
67623
- const nowSeconds = Math.floor(Date.now() / 1e3);
67624
- const exp = nowSeconds + ENROLL_TOKEN_TTL_SECONDS;
67625
- const token = await signEnrollToken(secret, {
67626
- typ: "enroll",
67627
- workspaceId: input.workspaceId,
67628
- accountId: input.accountId,
67629
- allowScreenControl: input.allowScreenControl,
67630
- iat: nowSeconds,
67631
- exp
67632
- });
67633
- return {
67634
- token,
67635
- expiresAt: new Date(exp * 1e3).toISOString(),
67636
- expiresInSeconds: ENROLL_TOKEN_TTL_SECONDS
67637
- };
67638
- }
67639
- async function exchangeEnrollToken(services, input) {
67640
- const { db, settings } = services;
67641
- const secret = resolveEnrollmentSigningSecret(settings);
67642
- if (!secret) {
67643
- return { ok: false, reason: "disabled" };
67644
- }
67645
- const claims = await verifyEnrollToken(secret, input.token);
67646
- if (!claims) {
67647
- return { ok: false, reason: "invalid" };
67648
- }
67649
- const sandboxName = (input.machineName?.trim() || `${input.os} machine`).slice(0, 256);
67650
- const { enrollment } = await finalizeEnrollmentByToken(db, {
67651
- accountId: claims.accountId,
67652
- workspaceId: claims.workspaceId,
67653
- pubkey: input.publicKey,
67654
- hasDisplay: input.canOfferDisplay,
67655
- // The token's allowScreenControl is the AUTHORITATIVE consent (NOT the agent's
67656
- // requestsScreenControl) — it was baked in at mint by the authorizing user.
67657
- allowScreenControl: claims.allowScreenControl,
67658
- os: input.os,
67659
- arch: input.arch,
67660
- sandboxName
67661
- });
67662
- const credentials = await buildEnrollmentCredentials(services, {
67663
- secret,
67664
- workspaceId: claims.workspaceId,
67665
- agentId: enrollment.id,
67666
- credentialGeneration: enrollment.credentialGeneration,
67667
- consentedScreenControl: enrollment.allowScreenControl
67668
- });
67669
- return { ok: true, credentials };
67670
- }
67671
- async function pollDeviceEnrollment(services, input) {
67672
- const { db, settings } = services;
67673
- const request = await getDeviceEnrollmentRequestByDeviceCode(db, input.deviceCode);
67674
- if (!request) {
67675
- return { state: "expired" };
67676
- }
67677
- if (request.status === "denied") {
67678
- return { state: "denied" };
67679
- }
67680
- if (request.status === "pending") {
67681
- if (new Date(request.expiresAt).getTime() <= Date.now()) {
67682
- return { state: "expired" };
67683
- }
67684
- return { state: "pending" };
67685
- }
67686
- if (!request.enrollmentId) {
67687
- return { state: "expired" };
67688
- }
67689
- const secret = resolveEnrollmentSigningSecret(settings);
67690
- if (!secret) {
67691
- return { state: "disabled" };
67692
- }
67693
- const enrollment = await getEnrollment(db, request.workspaceId, request.enrollmentId);
67694
- if (!enrollment || enrollment.status !== "active") {
67695
- return { state: "denied" };
67696
- }
67697
- const credentials = await buildEnrollmentCredentials(services, {
67698
- secret,
67699
- workspaceId: request.workspaceId,
67700
- agentId: enrollment.id,
67701
- credentialGeneration: enrollment.credentialGeneration,
67702
- consentedScreenControl: enrollment.allowScreenControl
67703
- });
67704
- if (request.status === "approved") {
67705
- await consumeDeviceEnrollmentRequest(db, {
67706
- accountId: request.accountId,
67707
- workspaceId: request.workspaceId,
67708
- requestId: request.id
67709
- });
67710
- }
67711
- return { state: DeviceEnrollmentState.enum.authorized, credentials };
67712
- }
67713
- async function buildEnrollmentCredentials(services, input) {
67714
- const { settings } = services;
67715
- const subjectPrefix = `agent.${input.workspaceId}.${input.agentId}`;
67716
- const nowSeconds = Math.floor(Date.now() / 1e3);
67717
- const exp = nowSeconds + ENROLLMENT_BEARER_TTL_SECONDS;
67718
- const bearer = await signEnrollmentBearer(input.secret, {
67719
- workspaceId: input.workspaceId,
67720
- agentId: input.agentId,
67721
- enrollmentId: input.agentId,
67722
- credentialGeneration: input.credentialGeneration,
67723
- subjectPrefix,
67724
- exp
67725
- });
67726
- const natsUrls = settings.selfhostedNatsUrl ? [settings.selfhostedNatsUrl] : [];
67727
- const relayTokenSecret = resolveRelayTokenSecret(settings);
67728
- const relayToken = relayTokenSecret ? await signRelayToken(relayTokenSecret, {
67729
- workspaceId: input.workspaceId,
67730
- agentId: input.agentId,
67731
- exp: nowSeconds + RELAY_TOKEN_TTL_SECONDS
67732
- }) : "";
67733
- return {
67734
- agentId: input.agentId,
67735
- workspaceId: input.workspaceId,
67736
- bearer,
67737
- subjectPrefix,
67738
- natsUrls,
67739
- // Hand the agent the canonical `/stream` dial base, NOT the raw configured URL.
67740
- // The agent's relay producer appends only its routing query and assumes the base
67741
- // already carries the relay's `/stream` route; a path-less base 400s the dial and
67742
- // makes the terminal/desktop streams unreachable.
67743
- relayUrl: relayDialBaseFromSettings(settings),
67744
- relayToken,
67745
- // M-AUTH closes the placeholder: there is NO per-machine NATS Account creds
67746
- // file. The agent presents the BEARER as the NATS connect auth-token; the
67747
- // server's auth-callout responder validates it and mints a workspace-scoped
67748
- // user JWT. We echo the bearer here so a consumer reading this (vestigial) field
67749
- // as the connect credential still works — the value IS the bearer.
67750
- natsAccountCreds: bearer,
67751
- updatePublicKey: settings.agentUpdatePublicKey ?? "",
67752
- consentedWholeMachine: true,
67753
- consentedScreenControl: input.consentedScreenControl
67754
- };
67755
- }
67756
-
67757
- // src/routes/enrollments.ts
67758
68022
  function registerEnrollmentRoutes(app, deps) {
67759
68023
  const { settings, db } = deps;
67760
68024
  function assertSelfhostedEnabled() {
@@ -68023,7 +68287,7 @@ function registerEnrollmentRoutes(app, deps) {
68023
68287
  message: "invalid machine removal request"
68024
68288
  });
68025
68289
  }
68026
- const idempotencyKey = parsed.data.idempotencyKey?.trim() || c.req.header("idempotency-key")?.trim() || randomUUID5();
68290
+ const idempotencyKey = parsed.data.idempotencyKey?.trim() || c.req.header("idempotency-key")?.trim() || randomUUID6();
68027
68291
  try {
68028
68292
  const enrollmentId = c.req.param("enrollmentId");
68029
68293
  const enrollment = (await listEnrollments2(db, grant, { status: "active" })).find(
@@ -68154,7 +68418,7 @@ import {
68154
68418
  readMachineMetricsLatestForWorkspace
68155
68419
  } from "@opengeni/db";
68156
68420
  import { MachineView, MetricSample } from "@opengeni/contracts";
68157
- import { managedSessionGroupBackend as managedSessionGroupBackend3 } from "@opengeni/core";
68421
+ import { managedSessionGroupBackend as managedSessionGroupBackend3, sessionGroupMachinePresentation } from "@opengeni/core";
68158
68422
  import { selfhostedHeartbeatLiveness } from "@opengeni/runtime/sandbox";
68159
68423
  var ACTIVE_UPDATE_STATUSES = /* @__PURE__ */ new Set([
68160
68424
  "requested",
@@ -68291,23 +68555,24 @@ async function listMachines(services, input) {
68291
68555
  if (session && groupBackend) {
68292
68556
  const groupActive = activeSandboxId === null;
68293
68557
  const groupLease = await readLease3(db, workspaceId, session.sandboxGroupId);
68558
+ const presentation = sessionGroupMachinePresentation(groupBackend);
68559
+ const localHost = groupBackend === "local";
68294
68560
  machines.push(
68295
68561
  MachineView.parse({
68296
68562
  sandboxId: session.sandboxGroupId,
68297
68563
  enrollmentId: null,
68298
- name: "session sandbox",
68299
- kind: groupBackend === "opensandbox" ? "opensandbox" : "modal",
68564
+ name: presentation.name,
68565
+ kind: presentation.kind,
68300
68566
  state: "online",
68301
68567
  active: groupActive,
68302
68568
  isSessionGroup: true,
68303
68569
  workspaceGeneration: groupLease?.workspaceGeneration ?? null,
68304
68570
  archiveGeneration: groupLease?.archiveGeneration ?? null,
68305
68571
  archiveComplete: groupLease?.archiveComplete ?? false,
68306
- // The Modal group box is a cloud Linux box; its precise OS/arch is not
68307
- // surfaced as a metric, so the dashboard shows the canonical linux/x86_64.
68308
- os: "linux",
68309
- arch: "x86_64",
68310
- hasDisplay: false,
68572
+ // A local sandbox is this machine. Other managed providers run a Linux box.
68573
+ os: localHost ? process.platform : "linux",
68574
+ arch: localHost ? process.arch : "x86_64",
68575
+ hasDisplay: localHost,
68311
68576
  desktopUnavailableReason: null,
68312
68577
  allowScreenControl: false,
68313
68578
  sharedSessionCount: 1,
@@ -70222,11 +70487,12 @@ function browserIdentityRouteError(error) {
70222
70487
  }
70223
70488
 
70224
70489
  // src/routes/browser-sessions.ts
70225
- import { randomUUID as randomUUID6 } from "crypto";
70490
+ import { randomUUID as randomUUID7 } from "crypto";
70226
70491
  import {
70227
70492
  environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes5,
70228
70493
  resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret3,
70229
- resolveStreamTokenSecret as resolveStreamTokenSecret2
70494
+ resolveStreamTokenSecret as resolveStreamTokenSecret2,
70495
+ sandboxWarmRateMicrosPerSecond as sandboxWarmRateMicrosPerSecond3
70230
70496
  } from "@opengeni/config";
70231
70497
  import {
70232
70498
  BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX,
@@ -70286,6 +70552,7 @@ import {
70286
70552
  BrowserSessionNotFoundError,
70287
70553
  BrowserSessionOperationConflictError,
70288
70554
  BrowserSessionStateError as BrowserSessionStateError2,
70555
+ SandboxPaidComputeAdmissionError as SandboxPaidComputeAdmissionError3,
70289
70556
  completeBrowserSessionEnd,
70290
70557
  completeExternalAuth,
70291
70558
  completeBrowserDownloadSave,
@@ -70374,7 +70641,7 @@ import {
70374
70641
  provisionBrowserControlClient,
70375
70642
  renewSandboxProviderExpiration as renewSandboxProviderExpiration2
70376
70643
  } from "@opengeni/runtime/sandbox";
70377
- import { retryWhileMissing as retryWhileMissing5 } from "@opengeni/storage";
70644
+ import { retryWhileMissing as retryWhileMissing6 } from "@opengeni/storage";
70378
70645
  import { HTTPException as HTTPException51 } from "hono/http-exception";
70379
70646
 
70380
70647
  // src/browser-controller-authority.ts
@@ -70657,7 +70924,7 @@ async function withInteractionHolderHeartbeat(deps, input, run) {
70657
70924
  }
70658
70925
 
70659
70926
  // src/browser-state-authority.ts
70660
- import { createCipheriv, createDecipheriv, createHash as createHash18, createHmac as createHmac7, randomBytes as randomBytes9 } from "crypto";
70927
+ import { createCipheriv, createDecipheriv, createHash as createHash19, createHmac as createHmac7, randomBytes as randomBytes9 } from "crypto";
70661
70928
  import { BROWSER_PROFILE_ARTIFACT_FORMAT } from "@opengeni/contracts";
70662
70929
  var ROOT_KEY_BYTES = 32;
70663
70930
  var DATA_KEY_BYTES = 32;
@@ -70721,7 +70988,7 @@ function browserStateObjectKey(workspaceIdInput, operationIdInput) {
70721
70988
  return `workspaces/${workspaceId}/browser-state/revisions/${operationId}/chromium-profile.ogbs`;
70722
70989
  }
70723
70990
  function browserStateManifestDigest(manifest) {
70724
- return createHash18("sha256").update(canonicalJson2(manifest), "utf8").digest("hex");
70991
+ return createHash19("sha256").update(canonicalJson2(manifest), "utf8").digest("hex");
70725
70992
  }
70726
70993
  function operationScope(input) {
70727
70994
  const base = baseArtifactScope(input);
@@ -72013,7 +72280,7 @@ function registerBrowserSessionRoutes(app, deps) {
72013
72280
  message: `browser download exceeds single PUT limit of ${objectStorage.maxSinglePutSizeBytes} bytes`
72014
72281
  });
72015
72282
  }
72016
- const fileId = randomUUID6();
72283
+ const fileId = randomUUID7();
72017
72284
  const safeFilename = sanitizeFilename(source.download.filename);
72018
72285
  save = await prepareBrowserDownloadSave(deps.db, {
72019
72286
  ...identity2,
@@ -72187,6 +72454,33 @@ function registerBrowserSessionRoutes(app, deps) {
72187
72454
  return context.json(result);
72188
72455
  }
72189
72456
  );
72457
+ app.get(
72458
+ "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/targets/:targetId/screenshot",
72459
+ async (context) => {
72460
+ const { workspaceId, grant, browserSessionId } = await browserRoutePreamble(
72461
+ context,
72462
+ "sessions:read"
72463
+ );
72464
+ const targetId = requireOpaqueParam(context, "targetId");
72465
+ const frame = await withActiveBrowserController(
72466
+ context,
72467
+ grant,
72468
+ workspaceId,
72469
+ browserSessionId,
72470
+ "session.read",
72471
+ "browser.read",
72472
+ async ({ sessionClient }) => await sessionClient.capture(targetId)
72473
+ );
72474
+ return new Response(frame.data.slice().buffer, {
72475
+ status: 200,
72476
+ headers: {
72477
+ "cache-control": "no-store",
72478
+ "content-type": frame.mediaType,
72479
+ "x-opengeni-browser-frame": frame.metadataHeader
72480
+ }
72481
+ });
72482
+ }
72483
+ );
72190
72484
  app.post(
72191
72485
  "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/actions",
72192
72486
  async (context) => {
@@ -72916,7 +73210,7 @@ function registerBrowserSessionRoutes(app, deps) {
72916
73210
  if (!targets.some((target) => target.id === request.targetId)) {
72917
73211
  throw new HTTPException51(404, { message: "browser target does not exist" });
72918
73212
  }
72919
- const grantId = randomUUID6();
73213
+ const grantId = randomUUID7();
72920
73214
  const expiresAt = new Date(Date.now() + request.expiresInSeconds * 1e3).toISOString();
72921
73215
  const rootSecret = browserAuthorityRoot(deps);
72922
73216
  const relaySecret = placement.session.openBrowserFrames ? resolveStreamTokenSecret2(deps.settings) : null;
@@ -73914,6 +74208,11 @@ function registerBrowserSessionRoutes(app, deps) {
73914
74208
  const resolved = await handle.routingSession.prime();
73915
74209
  if (expectedPlacement?.kind === "connected_machine") {
73916
74210
  if (resolved.kind !== "selfhosted" || resolved.sandboxId !== expectedPlacement.sandboxId) {
74211
+ if (operation === "browser.create") {
74212
+ throw new HTTPException51(422, {
74213
+ message: "The requested Connected Machine is not this session's current placement. Move the session to that machine before creating an interaction resource."
74214
+ });
74215
+ }
73917
74216
  return await throwBrowserSourcePlacementChanged(
73918
74217
  grant,
73919
74218
  sourceSession.id,
@@ -74629,6 +74928,10 @@ async function ensureInteractionHolder(deps, grant, sourceSession, browserSessio
74629
74928
  holderId: interactionHolderId(browserSessionId),
74630
74929
  subjectId: sourceSession.id,
74631
74930
  backend: placement.lease.backend,
74931
+ warmBilling: {
74932
+ mode: deps.settings.sandboxWarmBillingMode,
74933
+ rateMicrosPerSecond: sandboxWarmRateMicrosPerSecond3(deps.settings, placement.lease.backend)
74934
+ },
74632
74935
  os: placement.lease.os,
74633
74936
  image: sandboxRuntime.image,
74634
74937
  rigVersionId: sourceSession.rigVersionId,
@@ -74664,7 +74967,7 @@ async function ensureDispatchedGeneration(deps, grant, workspaceId, browserSessi
74664
74967
  operationId
74665
74968
  });
74666
74969
  if (record6.operation?.state === "prepared") {
74667
- const controllerGeneration = randomUUID6();
74970
+ const controllerGeneration = randomUUID7();
74668
74971
  try {
74669
74972
  await dispatchBrowserSessionOperation(deps.db, {
74670
74973
  accountId: grant.accountId,
@@ -75273,7 +75576,7 @@ async function finalizeBrowserDownloadFile(deps, grant, workspaceId, uploadId) {
75273
75576
  throw new InteractionResourceStateError("Browser download file upload authority expired");
75274
75577
  }
75275
75578
  const pendingFile = upload.file;
75276
- const head = await retryWhileMissing5(async () => {
75579
+ const head = await retryWhileMissing6(async () => {
75277
75580
  if (!await objectStorage.fileExists(pendingFile)) return null;
75278
75581
  return await objectStorage.headFile(pendingFile);
75279
75582
  });
@@ -75312,6 +75615,8 @@ function browserRouteError(error) {
75312
75615
  const connectedMachineError = interactionControlApiError(error, "browser");
75313
75616
  if (connectedMachineError) return connectedMachineError;
75314
75617
  if (error instanceof HTTPException51) return error;
75618
+ if (error instanceof SandboxPaidComputeAdmissionError3)
75619
+ return new HTTPException51(402, { message: error.message, cause: error });
75315
75620
  if (error instanceof BrowserSessionNotFoundError) {
75316
75621
  return new HTTPException51(404, { message: error.message, cause: error });
75317
75622
  }
@@ -75364,8 +75669,12 @@ function isAbort(error) {
75364
75669
  }
75365
75670
 
75366
75671
  // src/routes/computer-sessions.ts
75367
- import { randomUUID as randomUUID7 } from "crypto";
75368
- import { resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret4, resolveStreamTokenSecret as resolveStreamTokenSecret3 } from "@opengeni/config";
75672
+ import { randomUUID as randomUUID8 } from "crypto";
75673
+ import {
75674
+ resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret4,
75675
+ resolveStreamTokenSecret as resolveStreamTokenSecret3,
75676
+ sandboxWarmRateMicrosPerSecond as sandboxWarmRateMicrosPerSecond4
75677
+ } from "@opengeni/config";
75369
75678
  import {
75370
75679
  BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX as BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX2,
75371
75680
  BROWSER_CONTROL_PORT as BROWSER_CONTROL_PORT2,
@@ -75392,6 +75701,7 @@ import {
75392
75701
  ComputerSessionNotFoundError,
75393
75702
  ComputerSessionOperationConflictError,
75394
75703
  ComputerSessionStateError,
75704
+ SandboxPaidComputeAdmissionError as SandboxPaidComputeAdmissionError4,
75395
75705
  dispatchComputerSessionOperation,
75396
75706
  failComputerSessionOperation,
75397
75707
  findComputerSessionControlRecordByOperation,
@@ -75888,7 +76198,7 @@ function registerComputerSessionRoutes(app, deps) {
75888
76198
  retryable: false
75889
76199
  });
75890
76200
  }
75891
- const grantId = randomUUID7();
76201
+ const grantId = randomUUID8();
75892
76202
  const expiresAt = new Date(Date.now() + request.expiresInSeconds * 1e3).toISOString();
75893
76203
  const token = deriveComputerViewGrantToken({
75894
76204
  rootSecret: controllerAuthorityRoot(deps),
@@ -76279,6 +76589,11 @@ function registerComputerSessionRoutes(app, deps) {
76279
76589
  }
76280
76590
  if (expectedPlacement?.kind === "connected_machine") {
76281
76591
  if (resolved.kind !== "selfhosted" || resolved.sandboxId !== expectedPlacement.sandboxId) {
76592
+ if (operation === "computer.create") {
76593
+ throw new HTTPException52(422, {
76594
+ message: "The requested Connected Machine is not this session's current placement. Move the session to that machine before creating an interaction resource."
76595
+ });
76596
+ }
76282
76597
  return await throwComputerSourcePlacementChanged(
76283
76598
  grant,
76284
76599
  sourceSession.id,
@@ -76565,6 +76880,10 @@ function registerComputerSessionRoutes(app, deps) {
76565
76880
  holderId: interactionHolderId2(computerSessionId),
76566
76881
  subjectId: sourceSession.id,
76567
76882
  backend: placement.lease.backend,
76883
+ warmBilling: {
76884
+ mode: deps.settings.sandboxWarmBillingMode,
76885
+ rateMicrosPerSecond: sandboxWarmRateMicrosPerSecond4(deps.settings, placement.lease.backend)
76886
+ },
76568
76887
  os: placement.lease.os,
76569
76888
  image: sandboxRuntime.image,
76570
76889
  rigVersionId: sourceSession.rigVersionId,
@@ -76599,7 +76918,7 @@ function registerComputerSessionRoutes(app, deps) {
76599
76918
  operationId
76600
76919
  });
76601
76920
  if (record6.operation?.state === "prepared") {
76602
- const controllerGeneration = randomUUID7();
76921
+ const controllerGeneration = randomUUID8();
76603
76922
  try {
76604
76923
  await dispatchComputerSessionOperation(deps.db, {
76605
76924
  accountId: grant.accountId,
@@ -76887,6 +77206,8 @@ function computerRouteError(error) {
76887
77206
  const connectedMachineError = interactionControlApiError(error, "computer");
76888
77207
  if (connectedMachineError) return connectedMachineError;
76889
77208
  if (error instanceof HTTPException52) return error;
77209
+ if (error instanceof SandboxPaidComputeAdmissionError4)
77210
+ return new HTTPException52(402, { message: error.message, cause: error });
76890
77211
  if (error instanceof ComputerSessionNotFoundError) {
76891
77212
  return new HTTPException52(404, { message: error.message, cause: error });
76892
77213
  }
@@ -78712,7 +79033,7 @@ import {
78712
79033
  personalGitHubGitBrokerRouteId
78713
79034
  } from "@opengeni/github";
78714
79035
  import { readResponseTextBounded as readResponseTextBounded2 } from "@opengeni/network";
78715
- import { randomUUID as randomUUID8 } from "crypto";
79036
+ import { randomUUID as randomUUID9 } from "crypto";
78716
79037
  import JSONBig2 from "json-bigint";
78717
79038
  var PERSONAL_GITHUB_SERVER_ID = "github:personal";
78718
79039
  var GITHUB_API_VERSION3 = "2022-11-28";
@@ -78930,7 +79251,7 @@ async function authorizeBrokerProviderRequest(deps, authority, destinationUrl, o
78930
79251
  turnId: claims.turnId,
78931
79252
  attemptId: claims.attemptId,
78932
79253
  executionGeneration: claims.executionGeneration,
78933
- physicalRequestId: randomUUID8(),
79254
+ physicalRequestId: randomUUID9(),
78934
79255
  usePhase: "credential_resolution"
78935
79256
  }
78936
79257
  });
@@ -78945,7 +79266,7 @@ async function authorizeBrokerProviderRequest(deps, authority, destinationUrl, o
78945
79266
  turnId: claims.turnId,
78946
79267
  attemptId: claims.attemptId,
78947
79268
  executionGeneration: claims.executionGeneration,
78948
- physicalRequestId: randomUUID8(),
79269
+ physicalRequestId: randomUUID9(),
78949
79270
  usePhase: "provider_request",
78950
79271
  serverId: PERSONAL_GITHUB_SERVER_ID,
78951
79272
  connectionId: claims.connectionId,
@@ -83105,7 +83426,7 @@ import {
83105
83426
  integrationSourceForOrganizationPolicy as integrationSourceForOrganizationPolicy2
83106
83427
  } from "@opengeni/core";
83107
83428
  import { withOrganizationIntegrationPolicyFence as withOrganizationIntegrationPolicyFence8 } from "@opengeni/db/organization-integration-policy";
83108
- import { createHash as createHash19 } from "crypto";
83429
+ import { createHash as createHash20 } from "crypto";
83109
83430
  import {
83110
83431
  createPinnedIntegrationTransport as createPinnedIntegrationTransport3,
83111
83432
  fetchIntegrationSourceDocument as fetchIntegrationSourceDocument2
@@ -83967,7 +84288,7 @@ function pluginFailureCode(error) {
83967
84288
  return "component_install_failed";
83968
84289
  }
83969
84290
  function sha2562(value) {
83970
- return createHash19("sha256").update(value).digest("hex");
84291
+ return createHash20("sha256").update(value).digest("hex");
83971
84292
  }
83972
84293
 
83973
84294
  // src/routes/skills.ts
@@ -84002,14 +84323,14 @@ import {
84002
84323
  SkillSourceRemovalAuthorityError as SkillSourceRemovalAuthorityError2
84003
84324
  } from "@opengeni/db";
84004
84325
  import { loadSkillLibrarySkill, skillLibraryRepositoryUrl } from "@opengeni/runtime/skill-library";
84005
- import { createHash as createHash20 } from "crypto";
84326
+ import { createHash as createHash21 } from "crypto";
84006
84327
  import { HTTPException as HTTPException67 } from "hono/http-exception";
84007
84328
 
84008
84329
  // src/routes/skill-content.ts
84009
84330
  import { z as z24 } from "zod";
84010
84331
  import { HTTPException as HTTPException66 } from "hono/http-exception";
84011
- import { SkillFile } from "@opengeni/contracts";
84012
- import { readSkillRemovalScope } from "@opengeni/db";
84332
+ import { SkillFile, SKILL_MAX_FILES } from "@opengeni/contracts";
84333
+ import { getSessionForSubject, readSkillRemovalScope } from "@opengeni/db";
84013
84334
  import {
84014
84335
  approveSkill,
84015
84336
  rejectSkill,
@@ -84375,8 +84696,8 @@ var saveRequest = z24.object({
84375
84696
  expectedScopeVersion: z24.number().int().nonnegative(),
84376
84697
  scope: scopeSchema.default("workspace"),
84377
84698
  stableKey: z24.string().min(1).max(96),
84378
- files: z24.array(SkillFile).max(128),
84379
- deletions: z24.array(z24.string().min(1).max(512)).max(128).default([]),
84699
+ files: z24.array(SkillFile).max(SKILL_MAX_FILES),
84700
+ deletions: z24.array(z24.string().min(1).max(512)).max(SKILL_MAX_FILES).default([]),
84380
84701
  reason: z24.string().min(1).max(2e3)
84381
84702
  }).strict();
84382
84703
  var revisionRequest = z24.object({
@@ -84405,9 +84726,20 @@ function registerSkillContentRoutes(app, deps) {
84405
84726
  const grant = await requireAccessGrant27(c, deps, workspaceId, "workspace:read");
84406
84727
  const query = z24.object({
84407
84728
  limit: z24.coerce.number().int().min(1).max(250).default(100),
84408
- cursor: z24.string().max(2048).optional()
84729
+ cursor: z24.string().max(2048).optional(),
84730
+ sessionId: z24.uuid().optional()
84409
84731
  }).safeParse(c.req.query());
84410
84732
  if (!query.success) throw new HTTPException66(422, { message: "Invalid Skill list query" });
84733
+ if (query.data.sessionId) {
84734
+ await requireAccessGrant27(c, deps, workspaceId, "sessions:read");
84735
+ const session = await getSessionForSubject(
84736
+ deps.db,
84737
+ workspaceId,
84738
+ query.data.sessionId,
84739
+ grant.subjectId
84740
+ );
84741
+ if (!session) throw new HTTPException66(404, { message: "Session not found" });
84742
+ }
84411
84743
  let after;
84412
84744
  if (query.data.cursor !== void 0) {
84413
84745
  try {
@@ -84419,7 +84751,12 @@ function registerSkillContentRoutes(app, deps) {
84419
84751
  const rows2 = await listSkills(
84420
84752
  deps.db,
84421
84753
  { accountId: grant.accountId, workspaceId, subjectId: grant.subjectId },
84422
- { limit: query.data.limit + 1, metadataOnly: true, ...after ? { after } : {} }
84754
+ {
84755
+ limit: query.data.limit + 1,
84756
+ metadataOnly: true,
84757
+ ...after ? { after } : {},
84758
+ ...query.data.sessionId ? { sessionId: query.data.sessionId } : {}
84759
+ }
84423
84760
  );
84424
84761
  const page = rows2.slice(0, query.data.limit);
84425
84762
  const last = page.at(-1);
@@ -84626,7 +84963,7 @@ function registerSkillRoutes(app, deps, overrides = {}) {
84626
84963
  path: file.path,
84627
84964
  content: file.content,
84628
84965
  byteSize: bytes.byteLength,
84629
- contentSha256: createHash20("sha256").update(bytes).digest("hex")
84966
+ contentSha256: createHash21("sha256").update(bytes).digest("hex")
84630
84967
  };
84631
84968
  });
84632
84969
  try {
@@ -85587,7 +85924,7 @@ import {
85587
85924
  readActiveSandbox as readActiveSandbox3,
85588
85925
  deleteSessionTreeIfQuiescent,
85589
85926
  getSessionEvent,
85590
- getSessionForSubject,
85927
+ getSessionForSubject as getSessionForSubject2,
85591
85928
  getSessionGoal as getSessionGoal2,
85592
85929
  getLatestSessionModelContext,
85593
85930
  getSessionHumanInputRequest,
@@ -87289,7 +87626,7 @@ function registerSessionRoutes(app, deps) {
87289
87626
  if (!z27.string().uuid().safeParse(sessionId).success) {
87290
87627
  throw new HTTPException72(404, { message: "session not found" });
87291
87628
  }
87292
- const session = await getSessionForSubject(
87629
+ const session = await getSessionForSubject2(
87293
87630
  db,
87294
87631
  workspaceId,
87295
87632
  sessionId,
@@ -87320,7 +87657,7 @@ function registerSessionRoutes(app, deps) {
87320
87657
  if (!z27.string().uuid().safeParse(sessionId).success) {
87321
87658
  throw new HTTPException72(404, { message: "session not found" });
87322
87659
  }
87323
- const session = await getSessionForSubject(
87660
+ const session = await getSessionForSubject2(
87324
87661
  db,
87325
87662
  workspaceId,
87326
87663
  sessionId,
@@ -88346,7 +88683,7 @@ function registerSessionRoutes(app, deps) {
88346
88683
  }
88347
88684
  throw error;
88348
88685
  }
88349
- const session = await getSessionForSubject(
88686
+ const session = await getSessionForSubject2(
88350
88687
  db,
88351
88688
  workspaceId,
88352
88689
  sessionId,
@@ -88421,7 +88758,7 @@ function registerSessionRoutes(app, deps) {
88421
88758
  if (result.status === "updated") {
88422
88759
  await publishDurableSessionEvents3(bus, workspaceId, sessionId, [result.event]);
88423
88760
  }
88424
- const session = await getSessionForSubject(
88761
+ const session = await getSessionForSubject2(
88425
88762
  db,
88426
88763
  workspaceId,
88427
88764
  sessionId,
@@ -88440,7 +88777,7 @@ function registerSessionRoutes(app, deps) {
88440
88777
  await assertSessionExists(db, workspaceId, sessionId);
88441
88778
  const payload = UpdateSessionRequest.parse(await c.req.json());
88442
88779
  const titleUpdate = await updateSessionTitle2(deps, grant, sessionId, payload.title, "user");
88443
- const session = await getSessionForSubject(
88780
+ const session = await getSessionForSubject2(
88444
88781
  db,
88445
88782
  workspaceId,
88446
88783
  sessionId,
@@ -91508,7 +91845,7 @@ import { SessionControlConflictError as SessionControlConflictError2, WorkspaceP
91508
91845
  import { updateWorkspaceSettingsWithToolDefaults } from "@opengeni/db/workspace-tool-defaults";
91509
91846
  import { WorkspacePauseTimerRequest } from "@opengeni/contracts";
91510
91847
  import { getWorkspaceConnectionModelRestrictions } from "@opengeni/db";
91511
- import { createHash as createHash21 } from "crypto";
91848
+ import { createHash as createHash22 } from "crypto";
91512
91849
  import {
91513
91850
  AddWorkspaceMemberRequest,
91514
91851
  CreateWorkspaceRequest,
@@ -91775,7 +92112,7 @@ function projectWorkspaceOpenRouterCustomModel(model) {
91775
92112
  });
91776
92113
  }
91777
92114
  function workspaceCustomModelRequestHash(value) {
91778
- return createHash21("sha256").update(stableJson7(value), "utf8").digest("hex");
92115
+ return createHash22("sha256").update(stableJson7(value), "utf8").digest("hex");
91779
92116
  }
91780
92117
  function workspaceMembersResponse(members) {
91781
92118
  return ListWorkspaceMembersResponse.parse({
@@ -92712,7 +93049,7 @@ function requireAccountPermission2(context, accountId, permission) {
92712
93049
  }
92713
93050
 
92714
93051
  // src/routes/workspace-instruction-policies.ts
92715
- import { randomUUID as randomUUID9 } from "crypto";
93052
+ import { randomUUID as randomUUID10 } from "crypto";
92716
93053
  import {
92717
93054
  ActivateWorkspaceInstructionPolicyRequest,
92718
93055
  CreateWorkspaceInstructionPolicyDraftRequest,
@@ -92878,7 +93215,7 @@ function registerWorkspaceInstructionPolicyRoutes(app, deps) {
92878
93215
  return context.json(
92879
93216
  WorkspaceInstructionPolicyRevision.parse(
92880
93217
  await createWorkspaceInstructionPolicyDraft(deps.db, {
92881
- operationId: request.operationId ?? randomUUID9(),
93218
+ operationId: request.operationId ?? randomUUID10(),
92882
93219
  accountId: grant.accountId,
92883
93220
  workspaceId,
92884
93221
  createdBySubjectId: grant.subjectId,
@@ -92906,7 +93243,7 @@ function registerWorkspaceInstructionPolicyRoutes(app, deps) {
92906
93243
  return context.json(
92907
93244
  WorkspaceInstructionPolicyRevision.parse(
92908
93245
  await importLegacyWorkspaceInstructionPolicyDraft(deps.db, {
92909
- operationId: request.operationId ?? randomUUID9(),
93246
+ operationId: request.operationId ?? randomUUID10(),
92910
93247
  accountId: grant.accountId,
92911
93248
  workspaceId,
92912
93249
  createdBySubjectId: grant.subjectId,
@@ -92948,7 +93285,7 @@ function registerWorkspaceInstructionPolicyRoutes(app, deps) {
92948
93285
  return context.json(
92949
93286
  WorkspaceInstructionPolicyOnboardingProposal.parse(
92950
93287
  await createWorkspaceInstructionPolicyOnboardingProposal(deps.db, {
92951
- operationId: request.operationId ?? randomUUID9(),
93288
+ operationId: request.operationId ?? randomUUID10(),
92952
93289
  accountId: grant.accountId,
92953
93290
  workspaceId,
92954
93291
  createdBySubjectId: grant.subjectId,
@@ -92998,7 +93335,7 @@ function registerWorkspaceInstructionPolicyRoutes(app, deps) {
92998
93335
  return context.json(
92999
93336
  WorkspaceInstructionPolicyActivationResponse.parse(
93000
93337
  await rollbackWorkspaceInstructionPolicyRevision(deps.db, {
93001
- operationId: request.operationId ?? randomUUID9(),
93338
+ operationId: request.operationId ?? randomUUID10(),
93002
93339
  accountId: grant.accountId,
93003
93340
  workspaceId,
93004
93341
  targetRevisionId: request.targetRevisionId,
@@ -93037,7 +93374,7 @@ function registerWorkspaceInstructionPolicyRoutes(app, deps) {
93037
93374
  return context.json(
93038
93375
  WorkspaceInstructionPolicyActivationResponse.parse(
93039
93376
  await activateWorkspaceInstructionPolicyRevision(deps.db, {
93040
- operationId: request.operationId ?? randomUUID9(),
93377
+ operationId: request.operationId ?? randomUUID10(),
93041
93378
  accountId: grant.accountId,
93042
93379
  workspaceId,
93043
93380
  revisionId: revisionId2,
@@ -93055,7 +93392,7 @@ function registerWorkspaceInstructionPolicyRoutes(app, deps) {
93055
93392
  }
93056
93393
 
93057
93394
  // src/routes/workspace-learning.ts
93058
- import { randomUUID as randomUUID10 } from "crypto";
93395
+ import { randomUUID as randomUUID11 } from "crypto";
93059
93396
  import {
93060
93397
  UndoGovernedLearningActivationHttpRequest,
93061
93398
  WorkspaceLearningHistoryResponse,
@@ -93178,7 +93515,7 @@ function registerWorkspaceLearningRoutes(app, deps) {
93178
93515
  const undo = await undoGovernedLearningActivation(deps.db, {
93179
93516
  caller: { workspaceId, subjectId: grant.subjectId },
93180
93517
  request: {
93181
- operationId: request.operationId ?? randomUUID10(),
93518
+ operationId: request.operationId ?? randomUUID11(),
93182
93519
  activationReceiptId: activationReceiptId.data
93183
93520
  }
93184
93521
  });
@@ -93193,7 +93530,7 @@ function registerWorkspaceLearningRoutes(app, deps) {
93193
93530
  }
93194
93531
 
93195
93532
  // src/routes/company-profile.ts
93196
- import { randomUUID as randomUUID11 } from "crypto";
93533
+ import { randomUUID as randomUUID12 } from "crypto";
93197
93534
  import {
93198
93535
  ActivateCompanyProfileRevisionRequest,
93199
93536
  CompanyProfileAgentPolicy,
@@ -93370,7 +93707,7 @@ function registerCompanyProfileRoutes(app, deps) {
93370
93707
  return context.json(
93371
93708
  CompanyProfileMutationResponse.parse(
93372
93709
  await updateCompanyProfile(deps.db, {
93373
- operationId: request.operationId ?? randomUUID11(),
93710
+ operationId: request.operationId ?? randomUUID12(),
93374
93711
  accountId: access.grant.accountId,
93375
93712
  workspaceId,
93376
93713
  profile: request.profile,
@@ -93422,7 +93759,7 @@ function registerCompanyProfileRoutes(app, deps) {
93422
93759
  return context.json(
93423
93760
  CompanyProfileMutationResponse.parse(
93424
93761
  await rollbackCompanyProfileRevision(deps.db, {
93425
- operationId: request.operationId ?? randomUUID11(),
93762
+ operationId: request.operationId ?? randomUUID12(),
93426
93763
  accountId: access.grant.accountId,
93427
93764
  workspaceId,
93428
93765
  targetRevisionId: request.targetRevisionId,
@@ -93469,7 +93806,7 @@ function registerCompanyProfileRoutes(app, deps) {
93469
93806
  return context.json(
93470
93807
  CompanyProfileMutationResponse.parse(
93471
93808
  await activateCompanyProfileRevision(deps.db, {
93472
- operationId: request.operationId ?? randomUUID11(),
93809
+ operationId: request.operationId ?? randomUUID12(),
93473
93810
  accountId: access.grant.accountId,
93474
93811
  workspaceId,
93475
93812
  revisionId: revisionId(context),
@@ -93488,7 +93825,7 @@ function registerCompanyProfileRoutes(app, deps) {
93488
93825
  }
93489
93826
 
93490
93827
  // src/routes/company-brain.ts
93491
- import { createHash as createHash23 } from "crypto";
93828
+ import { createHash as createHash24 } from "crypto";
93492
93829
  import {
93493
93830
  COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS,
93494
93831
  COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT,
@@ -93759,14 +94096,14 @@ function serializeCompanyBrainOkf(value) {
93759
94096
  }
93760
94097
 
93761
94098
  // src/workspace-state-projection.ts
93762
- import { createHash as createHash22 } from "crypto";
94099
+ import { createHash as createHash23 } from "crypto";
93763
94100
  import {
93764
94101
  WORKSPACE_STATE_MAX_ACTIVE_POLICY_HEADS,
93765
94102
  WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT,
93766
94103
  WorkspaceStateResponse
93767
94104
  } from "@opengeni/contracts";
93768
94105
  function hashIdentities(values) {
93769
- return createHash22("sha256").update(values.join("\n"), "utf8").digest("hex");
94106
+ return createHash23("sha256").update(values.join("\n"), "utf8").digest("hex");
93770
94107
  }
93771
94108
  function policyTargetKey(value) {
93772
94109
  return `${value.kind}:${value.scope}:${value.roleKey ?? ""}`;
@@ -93996,7 +94333,7 @@ var inspectorQuery = z32.object({
93996
94333
  message: "attemptId cannot be combined with cursor"
93997
94334
  });
93998
94335
  function receiptCursorScope(workspaceId, subjectId) {
93999
- return createHash23("sha256").update("opengeni:company-brain-context-receipts:v1\0").update(workspaceId).update("\0").update(subjectId).digest("hex");
94336
+ return createHash24("sha256").update("opengeni:company-brain-context-receipts:v1\0").update(workspaceId).update("\0").update(subjectId).digest("hex");
94000
94337
  }
94001
94338
  function encodeReceiptCursor(workspaceId, subjectId, cursor) {
94002
94339
  return Buffer.from(
@@ -94212,7 +94549,7 @@ function registerCompanyBrainRoutes(app, deps) {
94212
94549
  }
94213
94550
 
94214
94551
  // src/routes/slack-task-policy.ts
94215
- import { randomUUID as randomUUID12 } from "crypto";
94552
+ import { randomUUID as randomUUID13 } from "crypto";
94216
94553
  import {
94217
94554
  SlackTaskPolicyListResponse,
94218
94555
  SlackTaskPolicyMutationResponse,
@@ -94295,7 +94632,7 @@ function registerSlackTaskPolicyRoutes(app, deps) {
94295
94632
  return context.json(
94296
94633
  SlackTaskPolicyMutationResponse.parse(
94297
94634
  await updateSlackTaskPolicy(deps.db, {
94298
- operationId: parsed.data.operationId ?? randomUUID12(),
94635
+ operationId: parsed.data.operationId ?? randomUUID13(),
94299
94636
  accountId: access.grant.accountId,
94300
94637
  workspaceId,
94301
94638
  policy: parsed.data.policy,
@@ -94334,7 +94671,7 @@ import {
94334
94671
  import { HTTPException as HTTPException81 } from "hono/http-exception";
94335
94672
 
94336
94673
  // src/workspace-state-export.ts
94337
- import { createHash as createHash24 } from "crypto";
94674
+ import { createHash as createHash25 } from "crypto";
94338
94675
  import {
94339
94676
  WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
94340
94677
  WorkspaceStateExportResponse
@@ -94365,7 +94702,7 @@ function createWorkspaceStateExport(state) {
94365
94702
  kind: "opengeni.workspace_state.sanitized_export",
94366
94703
  schemaVersion: WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
94367
94704
  generatedAt: state.generatedAt,
94368
- stateSha256: createHash24("sha256").update(canonicalState, "utf8").digest("hex"),
94705
+ stateSha256: createHash25("sha256").update(canonicalState, "utf8").digest("hex"),
94369
94706
  omissions: WORKSPACE_STATE_EXPORT_OMISSIONS,
94370
94707
  state
94371
94708
  });
@@ -94473,7 +94810,7 @@ function registerWorkspaceStateRoutes(app, deps) {
94473
94810
  }
94474
94811
 
94475
94812
  // src/routes/workspace-artifacts.ts
94476
- import { createHash as createHash25 } from "crypto";
94813
+ import { createHash as createHash26 } from "crypto";
94477
94814
  import {
94478
94815
  CreateWorkspaceArtifactRequest,
94479
94816
  PublishWorkspaceArtifactVersionRequest,
@@ -94565,7 +94902,7 @@ function prepareContent(deps, grant, input) {
94565
94902
  }
94566
94903
  function provenance(subjectId, idempotencyKey) {
94567
94904
  return {
94568
- operationKey: `subject:${createHash25("sha256").update(`${subjectId}:${idempotencyKey}`).digest("hex")}`,
94905
+ operationKey: `subject:${createHash26("sha256").update(`${subjectId}:${idempotencyKey}`).digest("hex")}`,
94569
94906
  actorSubjectId: subjectId,
94570
94907
  sourceSessionId: null,
94571
94908
  sourceTurnId: null,
@@ -94924,7 +95261,7 @@ import {
94924
95261
  import { HTTPException as HTTPException84 } from "hono/http-exception";
94925
95262
 
94926
95263
  // src/artifact-catalog-cursor.ts
94927
- import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, createHash as createHash26, randomBytes as randomBytes11 } from "crypto";
95264
+ import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, createHash as createHash27, randomBytes as randomBytes11 } from "crypto";
94928
95265
  import { ArtifactCatalogKind } from "@opengeni/contracts";
94929
95266
  import { HTTPException as HTTPException83 } from "hono/http-exception";
94930
95267
  import { z as z34 } from "zod";
@@ -94939,8 +95276,8 @@ var Cursor = z34.object({
94939
95276
  }).strict()
94940
95277
  }).strict();
94941
95278
  function artifactCatalogCursorCodec(secret, binding) {
94942
- const key = createHash26("sha256").update("opengeni:artifact-catalog:v1\0").update(secret).digest();
94943
- const aad = createHash26("sha256").update(binding).digest();
95279
+ const key = createHash27("sha256").update("opengeni:artifact-catalog:v1\0").update(secret).digest();
95280
+ const aad = createHash27("sha256").update(binding).digest();
94944
95281
  return {
94945
95282
  encode(value) {
94946
95283
  const iv = randomBytes11(12);
@@ -95257,7 +95594,7 @@ import { requireAccessGrant as requireAccessGrant43, TranscriptionServiceError a
95257
95594
  import { getWorkspace as getWorkspace6 } from "@opengeni/db";
95258
95595
 
95259
95596
  // src/routes/transcription-recordings.ts
95260
- import { createHash as createHash27 } from "crypto";
95597
+ import { createHash as createHash28 } from "crypto";
95261
95598
  import {
95262
95599
  CreateTranscriptionRecordingRequest,
95263
95600
  FinalizeTranscriptionRecordingRequest,
@@ -95273,7 +95610,7 @@ import {
95273
95610
  TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS,
95274
95611
  TranscriptionServiceError
95275
95612
  } from "@opengeni/core";
95276
- import { retryWhileMissing as retryWhileMissing6 } from "@opengeni/storage";
95613
+ import { retryWhileMissing as retryWhileMissing7 } from "@opengeni/storage";
95277
95614
  import {
95278
95615
  claimNextTranscriptionRecordingSegment,
95279
95616
  claimTranscriptionRecordingAssembly,
@@ -95859,7 +96196,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
95859
96196
  attemptedProvider = claim.segment.providerId;
95860
96197
  fallbackAllowed = claim.fallbackAllowed === true;
95861
96198
  const segmentKey = claim.segment.objectKey;
95862
- const stored = deps.objectStorage ? await retryWhileMissing6(async () => deps.objectStorage.getObjectBytes(segmentKey)) : null;
96199
+ const stored = deps.objectStorage ? await retryWhileMissing7(async () => deps.objectStorage.getObjectBytes(segmentKey)) : null;
95863
96200
  if (!stored) {
95864
96201
  throw new RecordingProcessingError("Provider segment is missing", "invalid_audio", false);
95865
96202
  }
@@ -96018,7 +96355,7 @@ async function* verifiedChunkBytes(deps, chunks, signal) {
96018
96355
  }
96019
96356
  let stored;
96020
96357
  try {
96021
- stored = await retryWhileMissing6(
96358
+ stored = await retryWhileMissing7(
96022
96359
  async () => deps.objectStorage.getObjectBytes(chunk.objectKey)
96023
96360
  );
96024
96361
  } catch {
@@ -96102,7 +96439,7 @@ function valueAsUuid(value) {
96102
96439
  if (/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)) {
96103
96440
  return value;
96104
96441
  }
96105
- const hex2 = createHash27("sha256").update(value).digest("hex").slice(0, 32);
96442
+ const hex2 = createHash28("sha256").update(value).digest("hex").slice(0, 32);
96106
96443
  return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-4${hex2.slice(13, 16)}-a${hex2.slice(17, 20)}-${hex2.slice(20, 32)}`;
96107
96444
  }
96108
96445
  function nonnegativeInteger(value) {
@@ -96166,7 +96503,7 @@ async function readBoundedBody(request, maxBytes) {
96166
96503
  return body3;
96167
96504
  }
96168
96505
  function sha256Hex3(bytes) {
96169
- return createHash27("sha256").update(bytes).digest("hex");
96506
+ return createHash28("sha256").update(bytes).digest("hex");
96170
96507
  }
96171
96508
 
96172
96509
  // src/routes/transcriptions.ts
@@ -96457,7 +96794,7 @@ function videoGenerationFundingOptions(input) {
96457
96794
  }
96458
96795
 
96459
96796
  // src/routes/canonical-human-identities.ts
96460
- import { randomUUID as randomUUID14 } from "crypto";
96797
+ import { randomUUID as randomUUID15 } from "crypto";
96461
96798
  import {
96462
96799
  CanonicalHumanBindingOperationRequest,
96463
96800
  CanonicalHumanIdentityMutationResponse,
@@ -96506,7 +96843,7 @@ import { HTTPException as HTTPException88 } from "hono/http-exception";
96506
96843
  import { z as z36 } from "zod";
96507
96844
 
96508
96845
  // src/routes/managed-auth-session-sets.ts
96509
- import { randomUUID as randomUUID13 } from "crypto";
96846
+ import { randomUUID as randomUUID14 } from "crypto";
96510
96847
  import {
96511
96848
  BeginManagedAuthLoginTransactionRequest,
96512
96849
  BootstrapManagedAuthSessionSetRequest,
@@ -96717,7 +97054,7 @@ function registerManagedAuthSessionSetRoutes(app, deps) {
96717
97054
  app.post("/v1/auth/session-set/transactions", async (context) => {
96718
97055
  const body3 = await bodyAs(context, BeginManagedAuthLoginTransactionRequest);
96719
97056
  const { authority, actorEpoch } = await requireMutation(context, deps, body3.expectedGeneration);
96720
- const transactionId = randomUUID13();
97057
+ const transactionId = randomUUID14();
96721
97058
  const transactionSecret = managedAuthTransactionSecret(
96722
97059
  requireSigningSecret(deps),
96723
97060
  authority,
@@ -97938,7 +98275,7 @@ function registerCanonicalHumanIdentityRoutes(app, deps) {
97938
98275
  return await mutate(context, deps, {
97939
98276
  operationType: "link",
97940
98277
  ...request,
97941
- operationId: request.operationId ?? randomUUID14()
98278
+ operationId: request.operationId ?? randomUUID15()
97942
98279
  });
97943
98280
  });
97944
98281
  app.delete(`${base}/login-bindings/:bindingId`, async (context) => {
@@ -97956,7 +98293,7 @@ function registerCanonicalHumanIdentityRoutes(app, deps) {
97956
98293
  operationType: "unlink",
97957
98294
  bindingId: bindingId(context),
97958
98295
  ...request,
97959
- operationId: request.operationId ?? randomUUID14()
98296
+ operationId: request.operationId ?? randomUUID15()
97960
98297
  });
97961
98298
  });
97962
98299
  app.post(`${base}/login-bindings/:bindingId/recovery`, async (context) => {
@@ -97974,7 +98311,7 @@ function registerCanonicalHumanIdentityRoutes(app, deps) {
97974
98311
  operationType: "begin_recovery",
97975
98312
  bindingId: bindingId(context),
97976
98313
  ...request,
97977
- operationId: request.operationId ?? randomUUID14()
98314
+ operationId: request.operationId ?? randomUUID15()
97978
98315
  });
97979
98316
  });
97980
98317
  app.post(`${base}/login-bindings/:bindingId/recovery/complete`, async (context) => {
@@ -97989,7 +98326,7 @@ function registerCanonicalHumanIdentityRoutes(app, deps) {
97989
98326
  operationType: "recover",
97990
98327
  bindingId: bindingId(context),
97991
98328
  ...request,
97992
- operationId: request.operationId ?? randomUUID14(),
98329
+ operationId: request.operationId ?? randomUUID15(),
97993
98330
  ...managed ? { verifiedManagedRecovery: true } : {}
97994
98331
  });
97995
98332
  });
@@ -98195,7 +98532,8 @@ function registerOrganizationMembershipRoutes(app, deps) {
98195
98532
  await createManagedOrganization(deps.db, {
98196
98533
  subjectId,
98197
98534
  subjectLabel: session.user.email || session.user.name,
98198
- ...payload
98535
+ ...payload,
98536
+ trialCreditsEnabled: deps.settings.productAccessMode === "managed" && deps.settings.verifiedSignupTrialCreditsEnabled
98199
98537
  })
98200
98538
  ),
98201
98539
  201
@@ -99407,7 +99745,8 @@ function registerManagedOnboardingRoutes(app, deps, options = {}) {
99407
99745
  actorSubjectId: `user:${session.user.id}`,
99408
99746
  organizationName,
99409
99747
  operationId: parsed.data.operationId,
99410
- requestFingerprint
99748
+ requestFingerprint,
99749
+ trialCreditsEnabled: deps.settings.productAccessMode === "managed" && deps.settings.verifiedSignupTrialCreditsEnabled
99411
99750
  })
99412
99751
  )
99413
99752
  );
@@ -100297,11 +100636,12 @@ async function exactAvailable(providers, providerId, context) {
100297
100636
  }
100298
100637
 
100299
100638
  // src/integrations/slack-interactions.ts
100300
- import { createHash as createHash29, createHmac as createHmac10, timingSafeEqual as timingSafeEqual3 } from "crypto";
100639
+ import { createHash as createHash30, createHmac as createHmac10, timingSafeEqual as timingSafeEqual3 } from "crypto";
100301
100640
  import {
100302
100641
  approvalIdentifier,
100303
100642
  ApproveSlackUserLinkAccessRequest,
100304
100643
  DEFAULT_FIRST_PARTY_MCP_TOOLS,
100644
+ resolveWorkspaceSessionToolDefaults,
100305
100645
  hasOpenGeniSlackReactionScope,
100306
100646
  ListSlackUserLinkAccessRequestsResponse,
100307
100647
  PrepareSlackUserLinkAccessRequest,
@@ -100341,6 +100681,7 @@ import {
100341
100681
  getOrCreateSlackInteraction,
100342
100682
  getLatestSessionModelForSubject,
100343
100683
  getSession as getSession11,
100684
+ getSessionByCreateIdempotencyKey,
100344
100685
  getSessionEvent as getSessionEvent2,
100345
100686
  getSessionHumanInputRequest as getSessionHumanInputRequest2,
100346
100687
  childRequiresActionResolutionExists,
@@ -100400,6 +100741,7 @@ import {
100400
100741
  acceptSessionUserMessage as acceptSessionUserMessage2,
100401
100742
  controlHumanSessionWorkstream as controlHumanSessionWorkstream2,
100402
100743
  createSessionForRequest as createSessionForRequest2,
100744
+ getActorNewSessionDefaults,
100403
100745
  hasPermission as hasPermission26,
100404
100746
  requireAccessContext as requireAccessContext8,
100405
100747
  requireAccessGrant as requireAccessGrant45,
@@ -100775,9 +101117,9 @@ function record4(value) {
100775
101117
  }
100776
101118
 
100777
101119
  // src/slack-reaction-files.ts
100778
- import { createHash as createHash28 } from "crypto";
101120
+ import { createHash as createHash29 } from "crypto";
100779
101121
  import { completeFileUpload as completeFileUpload5, prepareGeneratedWorkspaceFile as prepareGeneratedWorkspaceFile2 } from "@opengeni/db";
100780
- import { retryWhileMissing as retryWhileMissing7 } from "@opengeni/storage";
101122
+ import { retryWhileMissing as retryWhileMissing8 } from "@opengeni/storage";
100781
101123
  var IMPORT_IDENTITY_VERSION = "slack-reaction-image-v1";
100782
101124
  var UPLOAD_INTENT_TTL_MS2 = 60 * 6e4;
100783
101125
  var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
@@ -100792,7 +101134,7 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
100792
101134
  if (image.bytes.byteLength < 1 || image.bytes.byteLength > 4 * 1024 * 1024) {
100793
101135
  throw new Error("Slack reaction image size is invalid");
100794
101136
  }
100795
- const sha2563 = createHash28("sha256").update(image.bytes).digest("hex");
101137
+ const sha2563 = createHash29("sha256").update(image.bytes).digest("hex");
100796
101138
  const identity2 = slackReactionImageIdentity(source, image.fileId);
100797
101139
  const fileId = deterministicUuid(`${identity2}:file`);
100798
101140
  const uploadId = deterministicUuid(`${identity2}:upload`);
@@ -100877,7 +101219,7 @@ function safeSlackImageFilename(value, contentType) {
100877
101219
  return `${stem}${extension}`;
100878
101220
  }
100879
101221
  async function assertVisibleSlackImage(storage, objectKey, image, sha2563) {
100880
- const head = await retryWhileMissing7(async () => await storage.headObject(objectKey));
101222
+ const head = await retryWhileMissing8(async () => await storage.headObject(objectKey));
100881
101223
  assertStoredSlackImage(head, image, sha2563);
100882
101224
  }
100883
101225
  function assertStoredSlackImage(head, image, sha2563) {
@@ -100886,7 +101228,7 @@ function assertStoredSlackImage(head, image, sha2563) {
100886
101228
  }
100887
101229
  }
100888
101230
  function deterministicUuid(seed) {
100889
- const bytes = createHash28("sha256").update(seed, "utf8").digest().subarray(0, 16);
101231
+ const bytes = createHash29("sha256").update(seed, "utf8").digest().subarray(0, 16);
100890
101232
  bytes[6] = (bytes[6] ?? 0) & 15 | 80;
100891
101233
  bytes[8] = (bytes[8] ?? 0) & 63 | 128;
100892
101234
  const hex2 = bytes.toString("hex");
@@ -101045,7 +101387,7 @@ function slackReactionInboxEntry(payload, bot, settings) {
101045
101387
  if (!settings.enabled || !event || event.type !== "reaction_added" || item?.type !== "message" || !teamId || !eventId || !userId || userId === bot.botUserId || !reaction || reaction !== settings.emoji || !channelId || !workspaceSlackReactionChannelAllowed(settings, channelId) || !timestamp) {
101046
101388
  return null;
101047
101389
  }
101048
- const stableReactionIdentity = createHash29("sha256").update([teamId, userId, channelId, timestamp, reaction].join("\n")).digest("hex");
101390
+ const stableReactionIdentity = createHash30("sha256").update([teamId, userId, channelId, timestamp, reaction].join("\n")).digest("hex");
101049
101391
  return {
101050
101392
  providerEventId: eventId,
101051
101393
  providerMessageId: `reaction:${stableReactionIdentity}`,
@@ -101235,7 +101577,7 @@ function registerSlackInteractionRoutes(app, deps) {
101235
101577
  const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
101236
101578
  accountId: installation.accountId,
101237
101579
  workspaceId,
101238
- tokenDigest: createHash29("sha256").update(payload.linkToken).digest("hex"),
101580
+ tokenDigest: createHash30("sha256").update(payload.linkToken).digest("hex"),
101239
101581
  connectionId: link.connectionId,
101240
101582
  slackTeamId: link.slackTeamId,
101241
101583
  slackUserId: link.slackUserId,
@@ -101442,7 +101784,7 @@ function registerSlackInteractionRoutes(app, deps) {
101442
101784
  const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
101443
101785
  accountId: grant.accountId,
101444
101786
  workspaceId,
101445
- tokenDigest: createHash29("sha256").update(linkToken).digest("hex"),
101787
+ tokenDigest: createHash30("sha256").update(linkToken).digest("hex"),
101446
101788
  connectionId: link.connectionId,
101447
101789
  slackTeamId: link.slackTeamId,
101448
101790
  slackUserId: link.slackUserId,
@@ -102405,6 +102747,14 @@ async function processSlackInboxEntry(deps, entry2) {
102405
102747
  );
102406
102748
  let session;
102407
102749
  try {
102750
+ const defaults = await slackNewSessionDefaults(
102751
+ deps,
102752
+ grant,
102753
+ interaction,
102754
+ `slack:${entry2.connectionId}:${entry2.providerEventId}`
102755
+ );
102756
+ const workspace = await getWorkspace7(deps.db, interaction.workspaceId);
102757
+ const defaultTools = defaults.firstPartyMcpTools ?? resolveWorkspaceSessionToolDefaults(workspace?.settings)?.firstPartyMcpTools;
102408
102758
  const prepared = slackInvocationPreparedMessage(
102409
102759
  preparedEntry,
102410
102760
  preparedAttachments,
@@ -102415,13 +102765,14 @@ async function processSlackInboxEntry(deps, entry2) {
102415
102765
  grant,
102416
102766
  interaction.workspaceId,
102417
102767
  {
102768
+ ...defaults,
102418
102769
  requestedSessionId: interaction.sessionReservationId,
102419
102770
  initialMessage: prepared.entry.text,
102420
102771
  ...prepared.modelContext ? { modelContext: prepared.modelContext } : {},
102421
102772
  instructions: SLACK_SESSION_INSTRUCTIONS,
102422
- firstPartyMcpTools: slackTaskFirstPartyMcpTools(deps.settings),
102423
- resources: preparedAttachments.resources,
102424
- ...preferredModel ? { model: preferredModel } : {},
102773
+ firstPartyMcpTools: defaultTools !== void 0 ? allowedFirstPartyMcpToolsForSession4(deps.settings, defaultTools) : slackTaskFirstPartyMcpTools(deps.settings),
102774
+ resources: [...defaults.resources, ...preparedAttachments.resources],
102775
+ ...!defaults.model && preferredModel ? { model: preferredModel } : {},
102425
102776
  idempotencyKey: `slack:${entry2.connectionId}:${entry2.providerEventId}`,
102426
102777
  clientEventId: `slack:${entry2.providerEventId}`
102427
102778
  }
@@ -102842,19 +103193,25 @@ async function processSlackReactionInboxEntry(deps, entry2) {
102842
103193
  const preparedEntry = slackReactionPreparedEntry(entry2, context, preparedTask);
102843
103194
  let session;
102844
103195
  try {
103196
+ const defaults = await slackNewSessionDefaults(
103197
+ deps,
103198
+ grant,
103199
+ interaction,
103200
+ `slack-interaction:${interaction.id}`
103201
+ );
102845
103202
  session = await createSessionForRequest2(
102846
103203
  await withCatalogSettings(deps, grant),
102847
103204
  grant,
102848
103205
  interaction.workspaceId,
102849
103206
  {
103207
+ ...defaults,
102850
103208
  requestedSessionId: interaction.sessionReservationId,
102851
103209
  initialMessage: preparedEntry.text,
102852
103210
  instructions: SLACK_SESSION_INSTRUCTIONS,
102853
- // The exact reacted message and bounded containing thread are already in
102854
- // the prompt; do not expose general Slack history tools for this trigger.
102855
- firstPartyMcpTools: resolveFirstPartyMcpToolPolicy(deps.settings).default,
102856
- resources: preparedTask.resources,
102857
- ...preferredModel ? { model: preferredModel } : {},
103211
+ // Reaction context stays bounded; ordinary tools follow the same saved
103212
+ // selection/workspace defaults as the website, without adding Slack tools.
103213
+ resources: [...defaults.resources, ...preparedTask.resources],
103214
+ ...!defaults.model && preferredModel ? { model: preferredModel } : {},
102858
103215
  // Every reaction entry converging on this route must use the same create
102859
103216
  // key. This closes the same-owner multi-event race while the owner check
102860
103217
  // above prevents a different subject from winning creation authority.
@@ -102904,6 +103261,35 @@ function slackReactionPreparedEntry(entry2, context, prepared) {
102904
103261
  text: slackReactionTaskText(context, prepared)
102905
103262
  };
102906
103263
  }
103264
+ async function slackNewSessionDefaults(deps, grant, interaction, createKey) {
103265
+ const pending = await getSessionByCreateIdempotencyKey(
103266
+ deps.db,
103267
+ interaction.workspaceId,
103268
+ createKey
103269
+ );
103270
+ if (!pending) return getActorNewSessionDefaults(deps, grant, interaction.workspaceId);
103271
+ if (pending.id !== interaction.sessionReservationId || interaction.owningSubjectId !== grant.subjectId || pending.createdBy.kind !== "subject" || pending.createdBy.subjectId !== grant.subjectId) {
103272
+ throw new SlackInteractionPermanentError("slack_session_create_identity_mismatch");
103273
+ }
103274
+ return {
103275
+ model: pending.model,
103276
+ reasoningEffort: pending.reasoningEffort,
103277
+ latencyMode: pending.latencyMode,
103278
+ resources: pending.resources.filter((resource) => resource.kind === "repository"),
103279
+ ...pending.toolPolicy.mode === "explicit" ? { tools: pending.tools } : {
103280
+ excludedMcpServerIds: pending.toolPolicy.excludedMcpServerIds
103281
+ },
103282
+ firstPartyMcpTools: pending.firstPartyMcpTools,
103283
+ ...pending.firstPartyMcpPermissions ? { firstPartyMcpPermissions: pending.firstPartyMcpPermissions } : {},
103284
+ variableSetIds: pending.variableSetIds,
103285
+ ...pending.rigId ? { rigId: pending.rigId } : {},
103286
+ sandboxBackend: pending.sandboxBackend,
103287
+ ...pending.sandboxBackend === "selfhosted" && pending.activeSandboxId ? {
103288
+ targetSandboxId: pending.activeSandboxId,
103289
+ ...pending.workingDir ? { workingDir: pending.workingDir } : {}
103290
+ } : {}
103291
+ };
103292
+ }
102907
103293
  async function remountSlackReactionTaskForSession(deps, workspaceId, sessionId, prepared) {
102908
103294
  if (prepared.attachments.length === 0) return prepared;
102909
103295
  const session = await getSession11(deps.db, workspaceId, sessionId);
@@ -104335,7 +104721,7 @@ function normalizedBlockActionInteraction(payload) {
104335
104721
  if (!teamId || !userId || !channelId || !messageTs || !actionTs || !actionId || !SLACK_BLOCK_ACTION_IDS.has(actionId) || !handleId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(handleId) || !Array.isArray(payload.actions) || payload.actions.length !== 1) {
104336
104722
  throw new HTTPException95(400, { message: "invalid Slack block action" });
104337
104723
  }
104338
- const identity2 = createHash29("sha256").update([teamId, userId, channelId, messageTs, actionTs, actionId, handleId].join("\n")).digest("hex");
104724
+ const identity2 = createHash30("sha256").update([teamId, userId, channelId, messageTs, actionTs, actionId, handleId].join("\n")).digest("hex");
104339
104725
  return {
104340
104726
  providerEventId: `block:${identity2}`,
104341
104727
  providerMessageId: `block:${identity2}`,
@@ -104600,7 +104986,7 @@ function usesPrivateBotDm(interaction, entry2) {
104600
104986
  return isDirectMessageShortcut(entry2) || interaction.visibility === "private" && entry2.triggerKind !== "dm";
104601
104987
  }
104602
104988
  function deterministicUuid2(value) {
104603
- const bytes = createHash29("sha256").update(value).digest().subarray(0, 16);
104989
+ const bytes = createHash30("sha256").update(value).digest().subarray(0, 16);
104604
104990
  bytes[6] = bytes[6] & 15 | 80;
104605
104991
  bytes[8] = bytes[8] & 63 | 128;
104606
104992
  const hex2 = bytes.toString("hex");
@@ -104825,7 +105211,7 @@ function createAppComposition(deps) {
104825
105211
  }) : deps.transcriptionSegmenter;
104826
105212
  const routeDeps = {
104827
105213
  ...deps,
104828
- resolveCatalogSettings: () => resolveCatalogSettings(deps.db, deps.settings),
105214
+ resolveCatalogSettings: () => resolveCatalogSettings2(deps.db, deps.settings),
104829
105215
  observability,
104830
105216
  githubStateSecret: deps.githubStateSecret ?? deps.settings.githubAppManifestStateSecret ?? crypto.randomUUID(),
104831
105217
  managedAuth,
@@ -105255,9 +105641,9 @@ function createAppComposition(deps) {
105255
105641
  registerMcpOAuthRoutes(app, routeDeps);
105256
105642
  app.get("/v1/config/client", async (c) => {
105257
105643
  c.header("cache-control", "no-store");
105258
- const resolvedCatalog = await resolveCatalogSettings(deps.db, deps.settings);
105644
+ const resolvedCatalog = await resolveCatalogSettings2(deps.db, deps.settings);
105259
105645
  const baseCatalogSettings = resolvedCatalog.settings;
105260
- const codexCatalogSettings = baseCatalogSettings.codexSubscriptionEnabled ? withCodexCatalogProvider2(baseCatalogSettings) : baseCatalogSettings;
105646
+ const codexCatalogSettings = baseCatalogSettings.codexSubscriptionEnabled ? withCodexCatalogProvider3(baseCatalogSettings) : baseCatalogSettings;
105261
105647
  const catalogSettings = baseCatalogSettings.supergrokSubscriptionEnabled ? withXaiSubscriptionCatalogProvider3(codexCatalogSettings) : codexCatalogSettings;
105262
105648
  return c.json(
105263
105649
  ClientConfig.parse({
@@ -105270,7 +105656,7 @@ function createAppComposition(deps) {
105270
105656
  // union of the built-in allow-list and every registry provider's models, in
105271
105657
  // selection order (default model first); project each to the client-safe
105272
105658
  // provider-blind ClientModel shape (execution topology remains server-side).
105273
- models: configuredModels3(catalogSettings).map(projectClientModel),
105659
+ models: configuredModels4(catalogSettings).map(projectClientModel),
105274
105660
  defaultReasoningEffort: deps.settings.openaiReasoningEffort,
105275
105661
  allowedReasoningEfforts: configuredAllowedReasoningEfforts(deps.settings),
105276
105662
  defaultSandboxBackend: deps.settings.sandboxBackend,
@@ -105952,7 +106338,7 @@ function readinessChecks(deps) {
105952
106338
  } else {
105953
106339
  await deps.db.execute(dbSql`select 1`);
105954
106340
  }
105955
- await resolveCatalogSettings(deps.db, deps.settings);
106341
+ await resolveCatalogSettings2(deps.db, deps.settings);
105956
106342
  },
105957
106343
  nats: async () => {
105958
106344
  requireSessionEventDurableFanoutCapability2(deps.bus);
@@ -106882,4 +107268,4 @@ export {
106882
107268
  withDefaultEnabledCapabilityMcpTools,
106883
107269
  workflowIdForSession4 as workflowIdForSession
106884
107270
  };
106885
- //# sourceMappingURL=chunk-E7TZFJ65.js.map
107271
+ //# sourceMappingURL=chunk-PHQ7C36N.js.map