@opengeni/api-router 1.2.1-canary.37222965366001 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/{chunk-Q77DZ2M4.js → chunk-IZEGLLKZ.js} +1236 -468
  3. package/dist/{chunk-Q77DZ2M4.js.map → chunk-IZEGLLKZ.js.map} +1 -1
  4. package/dist/codemode.d.ts +3 -1
  5. package/dist/fatal-process-boundary.d.ts +9 -1
  6. package/dist/http/api-error.d.ts +11 -0
  7. package/dist/index.js +40 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/integrations/slack-bot.d.ts +72 -1
  10. package/dist/integrations/slack-tool-review.d.ts +5 -0
  11. package/dist/mcp/action-catalog-types.d.ts +5 -0
  12. package/dist/mcp/scheduled-task-view.d.ts +191 -0
  13. package/dist/mcp/server.d.ts +1 -1
  14. package/dist/organization-mcp.d.ts +8 -0
  15. package/dist/routes/client-errors.d.ts +1 -0
  16. package/dist/routes/organization-integration-policy.d.ts +6 -1
  17. package/dist/routes/slack-bot-access.d.ts +3 -0
  18. package/package.json +20 -20
  19. package/src/app.ts +38 -1
  20. package/src/auth/managed-auth-session-adapter.ts +8 -2
  21. package/src/auth/managed-auth.ts +19 -0
  22. package/src/codemode.ts +14 -7
  23. package/src/fatal-process-boundary.ts +50 -1
  24. package/src/http/api-error.ts +26 -1
  25. package/src/integrations/slack-bot.ts +276 -23
  26. package/src/integrations/slack-interactions.ts +177 -11
  27. package/src/integrations/slack-tool-review.ts +65 -0
  28. package/src/mcp/action-catalog-types.ts +5 -0
  29. package/src/mcp/action-catalog.gen.ts +85 -19
  30. package/src/mcp/scheduled-task-view.ts +10 -0
  31. package/src/mcp/server.ts +38 -16
  32. package/src/organization-mcp.ts +63 -3
  33. package/src/routes/api-integrations.ts +0 -17
  34. package/src/routes/capabilities.ts +2 -0
  35. package/src/routes/codex.ts +25 -12
  36. package/src/routes/managed-auth-session-sets.ts +11 -5
  37. package/src/routes/organization-integration-policy.ts +4 -4
  38. package/src/routes/sessions.ts +44 -0
  39. package/src/routes/slack-bot-access.ts +108 -0
  40. package/src/workspace-tool-gateway.ts +90 -4
@@ -511,13 +511,19 @@ async function requireRedemptionHuman(c, deps, workspaceId) {
511
511
  message: "reset redemption requires managed product mode"
512
512
  });
513
513
  }
514
- if (c.req.header("authorization")) {
515
- throw new HTTPException4(403, {
516
- message: "authorization bearer is not allowed for redemption"
517
- });
514
+ const agent = isAgentActingAsPerson(c) ? agentActingAsPersonBeforeGrantCheck(c) : null;
515
+ if (!agent) {
516
+ if (c.req.header("authorization")) {
517
+ throw new HTTPException4(403, {
518
+ message: "authorization bearer is not allowed for redemption"
519
+ });
520
+ }
521
+ requireSameOriginBrowserMutation(c, deps);
518
522
  }
519
- requireSameOriginBrowserMutation(c, deps);
520
- const human = await managedHumanOrAgent(c, deps);
523
+ const human = agent ? {
524
+ subjectId: agent.subjectId,
525
+ browserSessionHash: await hashCodexBrowserSession(`agent:${agent.subjectId}`)
526
+ } : await managedCookieHuman(c, deps);
521
527
  if (!human) {
522
528
  throw new HTTPException4(401, {
523
529
  message: "managed browser session required"
@@ -5239,9 +5245,11 @@ import {
5239
5245
  } from "@opengeni/contracts";
5240
5246
  import { hasPermission as hasPermission3, isDeveloperSetupGrant, requireSessionAuthorization as requireSessionAuthorization2 } from "@opengeni/core";
5241
5247
  import {
5248
+ CodemodeToolApprovalRequiredError,
5242
5249
  getActiveSessionTurnForExecution,
5243
5250
  getAttemptToolCatalog,
5244
5251
  getCodemodeOperation,
5252
+ readTurnCodemodeOperation,
5245
5253
  submitCodemodeOperation
5246
5254
  } from "@opengeni/db";
5247
5255
  import { getSession } from "@opengeni/db";
@@ -5672,11 +5680,12 @@ async function requireActiveCodemodeCatalog(deps, grant) {
5672
5680
  return { authority, catalog, turn: active };
5673
5681
  }
5674
5682
  async function submitAndDispatchCodemodeCall(deps, grant, rawRequest) {
5675
- const request = CodemodeCallRequest.parse(rawRequest);
5683
+ const { durableApproval, ...request } = CodemodeCallRequest.parse(rawRequest);
5676
5684
  const { authority, catalog } = await requireActiveCodemodeCatalog(deps, grant);
5677
5685
  if (request.catalogDigest !== catalog.digest) throw new CodemodeCatalogStaleError();
5678
5686
  const submitted = await submitCodemodeOperation(deps.db, {
5679
5687
  ...authority,
5688
+ ...durableApproval ? { durableApproval: true } : {},
5680
5689
  call: {
5681
5690
  ...request,
5682
5691
  caller: { kind: "codemode", subjectId: authority.subjectId }
@@ -5718,15 +5727,18 @@ async function submitAndDispatchCodemodeCall(deps, grant, rawRequest) {
5718
5727
  function codemodeOperationNeedsDispatch(operation) {
5719
5728
  return operation.state === "queued" || operation.state === "running";
5720
5729
  }
5721
- async function readCodemodeOperation(deps, grant, operationId) {
5722
- if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError("invalid_grant");
5723
- const authority = codemodeAuthorityForGrant(grant);
5724
- return await getCodemodeOperation(deps.db, {
5725
- accountId: authority.accountId,
5726
- workspaceId: authority.workspaceId,
5727
- attemptId: authority.attemptId,
5730
+ async function readCodemodeOperation(deps, grant, operationId, options = {}) {
5731
+ const { authority } = await requireActiveCodemodeCatalog(deps, grant);
5732
+ const operation = await readTurnCodemodeOperation(deps.db, {
5733
+ ...authority,
5734
+ callerSubjectId: authority.subjectId,
5728
5735
  operationId
5729
5736
  });
5737
+ if (operation?.state === "waiting_for_approval" && !options.durableApproval)
5738
+ throw new CodemodeToolApprovalRequiredError();
5739
+ if (!operation || options.durableApproval) return operation;
5740
+ const { durableApproval: _capability, approvalRequestId: _requestId, ...legacy } = operation;
5741
+ return legacy;
5730
5742
  }
5731
5743
  function terminal(operation) {
5732
5744
  return ["completed", "failed", "outcome_unknown", "cancelled"].includes(operation.state);
@@ -6502,6 +6514,7 @@ function registerModelConnectionAccessRoutes(app, deps) {
6502
6514
  import {
6503
6515
  codeSearchDeploymentPolicy,
6504
6516
  agentConfigDeploymentPolicy,
6517
+ canonicalPublicOrigin as canonicalPublicOrigin3,
6505
6518
  configuredAllowedReasoningEfforts,
6506
6519
  resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy2,
6507
6520
  resolveVoiceInputProviderRegistry as resolveVoiceInputProviderRegistry2,
@@ -6532,7 +6545,8 @@ import {
6532
6545
  CodemodeOperationConflictError,
6533
6546
  CodemodeOperationNotExecutableError,
6534
6547
  CodemodePayloadTooLargeError,
6535
- CodemodeToolApprovalRequiredError,
6548
+ CodemodeOperationLimitError,
6549
+ CodemodeToolApprovalRequiredError as CodemodeToolApprovalRequiredError2,
6536
6550
  CodemodeToolNotInCatalogError,
6537
6551
  ConnectAttemptConflictError,
6538
6552
  ConnectAttemptNotFoundError,
@@ -6588,7 +6602,7 @@ import { bodyLimit as bodyLimit3 } from "hono/body-limit";
6588
6602
  import { compress } from "hono/compress";
6589
6603
  import { cors } from "hono/cors";
6590
6604
  import { getCookie as getCookie3 } from "hono/cookie";
6591
- import { HTTPException as HTTPException117 } from "hono/http-exception";
6605
+ import { HTTPException as HTTPException118 } from "hono/http-exception";
6592
6606
 
6593
6607
  // src/http/api-error.ts
6594
6608
  import {
@@ -6597,7 +6611,7 @@ import {
6597
6611
  ModelUnavailableError,
6598
6612
  ScheduledTaskTargetAccessChange
6599
6613
  } from "@opengeni/contracts";
6600
- import { WorkspaceControlBusyError } from "@opengeni/db";
6614
+ import { isDatabaseConnectionLoss, WorkspaceControlBusyError } from "@opengeni/db";
6601
6615
  import { HTTPException as HTTPException20 } from "hono/http-exception";
6602
6616
  var ApiHttpError = class extends HTTPException20 {
6603
6617
  code;
@@ -6650,6 +6664,20 @@ function workspaceControlBusyHttpError(error) {
6650
6664
  details: { code: busy.code, lockTimeoutMs: busy.lockTimeoutMs }
6651
6665
  });
6652
6666
  }
6667
+ var DATABASE_UNAVAILABLE_ERROR_DETAIL_CODE = "DATABASE_UNAVAILABLE";
6668
+ function databaseUnavailableHttpError(error, method) {
6669
+ if (error instanceof ApiHttpError) return null;
6670
+ if (error instanceof HTTPException20 && error.status < 500) return null;
6671
+ if (!isDatabaseConnectionLoss(error)) return null;
6672
+ const safeMethod = method === "GET" || method === "HEAD" || method === "OPTIONS";
6673
+ return new ApiHttpError(503, {
6674
+ code: "upstream_unavailable",
6675
+ message: "Opengeni is temporarily unavailable. Retry shortly.",
6676
+ retryable: true,
6677
+ outcomeUnknown: !safeMethod,
6678
+ details: { code: DATABASE_UNAVAILABLE_ERROR_DETAIL_CODE }
6679
+ });
6680
+ }
6653
6681
  function agentConfigHttpError(error) {
6654
6682
  const cause = error instanceof AgentConfigError ? error : error instanceof HTTPException20 && error.cause instanceof AgentConfigError ? error.cause : null;
6655
6683
  if (!cause) return null;
@@ -7037,7 +7065,7 @@ import {
7037
7065
  CodexCompactionV2ProviderLockedError,
7038
7066
  ManagedAuthActorLeaseOutcomeUnknownError,
7039
7067
  hasPermission as hasPermission34,
7040
- requireAccessGrant as requireAccessGrant53,
7068
+ requireAccessGrant as requireAccessGrant54,
7041
7069
  requireAccessGrantAuthorization as requireAccessGrantAuthorization40,
7042
7070
  accountScopedApiKeyWorkspaceAuthority as accountScopedApiKeyWorkspaceAuthority10,
7043
7071
  requireAccessContext as requireAccessContext15,
@@ -7090,7 +7118,8 @@ function createManagedAuthNewSignupsGate(input) {
7090
7118
  import { canonicalPublicOrigin, managedUserEmailAllowed } from "@opengeni/config";
7091
7119
  import { MANAGED_AUTH_NEW_SIGNUPS_PAUSED_CODE } from "@opengeni/contracts";
7092
7120
  import {
7093
- configureManagedUserAdmission
7121
+ configureManagedUserAdmission,
7122
+ recordManagedAuthLoggedFailure
7094
7123
  } from "@opengeni/core";
7095
7124
  import { ensureManagedAccessForUser } from "@opengeni/db";
7096
7125
  import {
@@ -8676,6 +8705,13 @@ async function hashManagedAuthPassword(password) {
8676
8705
  async function verifyManagedAuthPassword(password, hash) {
8677
8706
  return await verifyPassword({ password, hash });
8678
8707
  }
8708
+ function logBetterAuthMessage(level, message, ...args) {
8709
+ if (level === "error") recordManagedAuthLoggedFailure(args);
8710
+ const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} [Better Auth]: ${message}`;
8711
+ if (level === "error") console.error(line, ...args);
8712
+ else if (level === "warn") console.warn(line, ...args);
8713
+ else console.log(line, ...args);
8714
+ }
8679
8715
  var MANAGED_AUTH_DATABASE_POOL_OPTIONS = {
8680
8716
  max: 10,
8681
8717
  connectionTimeoutMillis: 1e4,
@@ -8731,6 +8767,7 @@ function createManagedAuth(settings, db, managedEmailTransport, options = {}) {
8731
8767
  secret: settings.betterAuthSecret,
8732
8768
  database: pool,
8733
8769
  trustedOrigins: betterAuthTrustedOrigins(settings),
8770
+ logger: { log: logBetterAuthMessage },
8734
8771
  hooks: {
8735
8772
  before: createManagedAuthEmailThrottleHook(
8736
8773
  db,
@@ -9216,7 +9253,10 @@ import {
9216
9253
  } from "@opengeni/core/managed-auth-session-sets";
9217
9254
 
9218
9255
  // src/auth/managed-auth-session-adapter.ts
9219
- import { assertManagedUserAdmission } from "@opengeni/core";
9256
+ import {
9257
+ assertManagedUserAdmission,
9258
+ withManagedAuthSessionLookup
9259
+ } from "@opengeni/core";
9220
9260
  import { createHmac as createHmac5, timingSafeEqual as timingSafeEqual2 } from "crypto";
9221
9261
  async function managedAuthSelectedProofHeaders(auth, token) {
9222
9262
  const context = await auth.$context;
@@ -9275,7 +9315,9 @@ function createBetterAuthSessionAdapter(auth, db) {
9275
9315
  const headers = new Headers({
9276
9316
  cookie: `${cookie.name}=${signedCookieValue(input.token, context.secret)}`
9277
9317
  });
9278
- const resolved = await auth.api.getSession({ headers, returnHeaders: true });
9318
+ const resolved = await withManagedAuthSessionLookup(
9319
+ () => auth.api.getSession({ headers, returnHeaders: true })
9320
+ );
9279
9321
  if (!resolved.response) return null;
9280
9322
  assertManagedUserAdmission(auth, resolved.response.user);
9281
9323
  return resolved.response;
@@ -13959,6 +14001,7 @@ async function withOptionalSiteCommandOrigin(db, workspaceId, siteId, versionId,
13959
14001
 
13960
14002
  // src/workspace-tool-gateway.ts
13961
14003
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
14004
+ import { recordToolApproval } from "@opengeni/observability";
13962
14005
  import {
13963
14006
  CallToolRequestSchema,
13964
14007
  ListToolsRequestSchema
@@ -13971,7 +14014,8 @@ import {
13971
14014
  ToolGatewayCallResponse,
13972
14015
  ToolGatewayApprovalRequest,
13973
14016
  ToolGatewayApprovalResponse,
13974
- ToolGatewayDeclarationsResponse
14017
+ ToolGatewayDeclarationsResponse,
14018
+ toolPolicyActionName
13975
14019
  } from "@opengeni/contracts";
13976
14020
  import {
13977
14021
  availableMcpAccountBindings,
@@ -14000,13 +14044,18 @@ import {
14000
14044
  issueToolGatewayApproval,
14001
14045
  ToolGatewayApprovalOperationStartedError,
14002
14046
  ToolGatewayApprovalRateLimitError,
14003
- WorkspaceArtifactNotFoundError as WorkspaceArtifactNotFoundError2
14047
+ WorkspaceArtifactNotFoundError as WorkspaceArtifactNotFoundError2,
14048
+ listConnectorToolPermissionPolicies,
14049
+ projectConnectorToolPermission,
14050
+ resolveConnectorActionPolicy,
14051
+ connectorActionPolicyDecision
14004
14052
  } from "@opengeni/db";
14005
14053
  import {
14006
14054
  prepareWorkspaceToolGatewayTools
14007
14055
  } from "@opengeni/runtime/workspace-tool-gateway";
14008
14056
  import {
14009
14057
  ToolGatewayApprovalRequiredError,
14058
+ ToolGatewayBlockedError,
14010
14059
  ToolGatewayCatalogStaleError,
14011
14060
  ToolGatewayInputValidationError,
14012
14061
  ToolGatewayToolNotFoundError,
@@ -14403,6 +14452,32 @@ async function prepareWorkspaceToolGatewayForGrantInternal(routeDeps, grant, all
14403
14452
  };
14404
14453
  })() : void 0;
14405
14454
  const localMcpServers = [...firstPartyServers, ...apiIntegrationServers];
14455
+ const policyTargets = new Map(
14456
+ gatewaySettings.mcpServers.map((config) => {
14457
+ const binding = accountBindings.find((candidate) => candidate.serverId === config.id);
14458
+ return [
14459
+ config.id,
14460
+ {
14461
+ connectionId: binding?.connectionId ?? config.connectionRef?.connectionId ?? `session-mcp:${config.id}:${createHash3("sha256").update(config.url, "utf8").digest("hex")}`,
14462
+ serverId: binding?.canonicalServerId ?? config.id
14463
+ }
14464
+ ];
14465
+ })
14466
+ );
14467
+ const policiesByServer = new Map(
14468
+ await Promise.all(
14469
+ [...policyTargets].map(
14470
+ async ([id, target2]) => [
14471
+ id,
14472
+ await listConnectorToolPermissionPolicies(routeDeps.db, {
14473
+ ...grant,
14474
+ connectionId: target2.connectionId
14475
+ })
14476
+ ]
14477
+ )
14478
+ )
14479
+ );
14480
+ const recommendations = /* @__PURE__ */ new Map();
14406
14481
  const prepared = await prepareWorkspaceToolGatewayTools(
14407
14482
  gatewaySettings,
14408
14483
  allGatewayToolRefs(gatewaySettings),
@@ -14417,7 +14492,43 @@ async function prepareWorkspaceToolGatewayForGrantInternal(routeDeps, grant, all
14417
14492
  localMcpServers,
14418
14493
  ...codexAppsAuth ? { codexAppsAuth } : {},
14419
14494
  workspaceToolGateway: {
14420
- requireApproval: (entry2, _caller, context) => entry2.approval === "human" && context.transportMeta?.approvalConfirmed !== true,
14495
+ mapDefinition: (definition) => {
14496
+ const recommendation = definition.approval === "human" ? "ask" : "allow";
14497
+ recommendations.set(workspaceToolGatewayIdentityKey(definition.identity), recommendation);
14498
+ const target2 = policyTargets.get(definition.identity.serverId);
14499
+ if (!target2) return definition;
14500
+ const permission = projectConnectorToolPermission(
14501
+ policiesByServer.get(definition.identity.serverId) ?? [],
14502
+ {
14503
+ ...target2,
14504
+ toolName: definition.identity.toolName,
14505
+ defaultDecision: recommendation
14506
+ }
14507
+ );
14508
+ return { ...definition, approval: permission.approvalRequired ? "human" : "policy" };
14509
+ },
14510
+ resolveApproval: async ({ call, entry: entry2 }) => {
14511
+ const defaultDecision = recommendations.get(workspaceToolGatewayIdentityKey(entry2.identity)) ?? (entry2.approval === "human" ? "ask" : "allow");
14512
+ const target2 = policyTargets.get(entry2.identity.serverId);
14513
+ if (!target2) return defaultDecision;
14514
+ const policies = await listConnectorToolPermissionPolicies(routeDeps.db, {
14515
+ ...grant,
14516
+ connectionId: target2.connectionId
14517
+ });
14518
+ const resolved = resolveConnectorActionPolicy(policies, {
14519
+ ...target2,
14520
+ toolName: entry2.identity.toolName,
14521
+ defaultDecision,
14522
+ actionName: toolPolicyActionName(
14523
+ entry2.identity.toolName,
14524
+ entry2.inputSchema,
14525
+ call.arguments
14526
+ )
14527
+ });
14528
+ const decision = !resolved.managed ? defaultDecision : connectorActionPolicyDecision(resolved);
14529
+ recordToolApproval(decision, resolved.managed ? resolved.source : "default");
14530
+ return decision;
14531
+ },
14421
14532
  filterDefinition: workspaceToolGatewayDefinitionFilter(gatewaySettings, allowedIdentities)
14422
14533
  }
14423
14534
  }
@@ -14559,7 +14670,7 @@ async function callWorkspaceToolGateway(prepared, grant, input, db, consumeAppro
14559
14670
  );
14560
14671
  await prepared.reauthorize?.();
14561
14672
  let approvalConfirmed = false;
14562
- const approvalRequired = preparedCall.entry.approval === "human";
14673
+ const approvalRequired = preparedCall.approvalDecision === "ask" || preparedCall.approvalDecision === void 0 && preparedCall.entry.approval === "human";
14563
14674
  if (approvalRequired && request.approvalToken && db) {
14564
14675
  approvalConfirmed = await consumeApproval(db, {
14565
14676
  tokenHash: hashOpaqueValue(request.approvalToken),
@@ -14614,7 +14725,7 @@ async function approveWorkspaceToolGatewayCall(prepared, grant, db, input, issue
14614
14725
  } catch (error) {
14615
14726
  throwWorkspaceToolGatewayHttpError(error);
14616
14727
  }
14617
- if (preparedCall.entry.approval !== "human") {
14728
+ if (preparedCall.approvalDecision !== "ask" && !(preparedCall.approvalDecision === void 0 && preparedCall.entry.approval === "human")) {
14618
14729
  throw new HTTPException24(422, { message: "tool_does_not_require_human_approval" });
14619
14730
  }
14620
14731
  const observation = startWorkspaceToolGatewayObservation(observability, {
@@ -14698,6 +14809,9 @@ function throwWorkspaceToolGatewayHttpError(error) {
14698
14809
  if (error instanceof ToolGatewayApprovalRequiredError) {
14699
14810
  throw new HTTPException24(409, { message: error.code, cause: error });
14700
14811
  }
14812
+ if (error instanceof ToolGatewayBlockedError) {
14813
+ throw new HTTPException24(403, { message: error.code, cause: error });
14814
+ }
14701
14815
  if (error instanceof IntegrationInvocationError && error.outcome === "unknown") {
14702
14816
  throw new ApiHttpError(502, {
14703
14817
  code: "upstream_unavailable",
@@ -21883,7 +21997,16 @@ function buildScheduledTaskDetailMcp(task, budget, reducedForBytes) {
21883
21997
  maxAutoContinuations: task.agentConfig.goal?.maxAutoContinuations ?? null
21884
21998
  } : null
21885
21999
  },
21886
- taskMetadataKeys
22000
+ taskMetadataKeys,
22001
+ slackPosting: {
22002
+ identity: task.agentConfig.slackBotConnectionId ? "opengeni_bot" : null,
22003
+ connectionId: task.agentConfig.slackBotConnectionId ?? null,
22004
+ channelId: task.agentConfig.slackBotChannelId ?? null,
22005
+ configured: Boolean(
22006
+ task.agentConfig.slackBotConnectionId && task.agentConfig.slackBotChannelId
22007
+ ),
22008
+ setup: "A person can choose Post to Slack in the schedule editor. Personal Slack tools send as their connected account; they do not select the Opengeni bot."
22009
+ }
21887
22010
  },
21888
22011
  detailProjection: {
21889
22012
  bounded: true,
@@ -22906,6 +23029,7 @@ import {
22906
23029
  hasOpenGeniSlackReactionScope
22907
23030
  } from "@opengeni/contracts";
22908
23031
  import {
23032
+ grantHasAgentAttemptAuthority as grantHasAgentAttemptAuthority2,
22909
23033
  isOpenGeniSlackBotConnection,
22910
23034
  isTrustedScheduledSlackBotSession,
22911
23035
  openGeniSlackBotMetadata,
@@ -22924,10 +23048,11 @@ import {
22924
23048
  getScheduledTask,
22925
23049
  getSession as getSession2,
22926
23050
  getSlackBotPostOperation,
22927
- listConnectionsMetadata as listConnectionsMetadata2,
22928
- prepareScheduledSlackBotMessage,
22929
- readScheduledSlackBotMessage,
22930
- ScheduledSlackBotMessageRefusedError,
23051
+ availableSlackBotConnectionMetadata,
23052
+ sharedOrganizationSlackBots,
23053
+ prepareBotMessage,
23054
+ readPreparedBotMessage,
23055
+ freezeAgentLearningPolicy as freezeAgentLearningPolicy2,
22931
23056
  markSlackBotDeleteOperationProviderStarted,
22932
23057
  markSlackBotPostOperationProviderStarted,
22933
23058
  recordAuditEvent as recordAuditEvent3,
@@ -23154,11 +23279,13 @@ async function resolveSlackBotConnectionForTool(input) {
23154
23279
  }
23155
23280
  let connectionId = boundConnectionId ?? input.requestedConnectionId;
23156
23281
  if (!connectionId) {
23157
- const activeConnections = (await listConnectionsMetadata2(input.db, input.grant.workspaceId, null)).filter(
23282
+ const activeConnections = (await availableSlackBotConnectionMetadata(input.db, input.grant)).filter(
23158
23283
  (connection2) => connection2.status === "active" && isOpenGeniSlackBotConnection(connection2)
23159
23284
  );
23160
23285
  if (activeConnections.length === 0) {
23161
- throw new Error("no active Opengeni Slack bot connection is installed in this workspace");
23286
+ throw new Error(
23287
+ "No active Opengeni bot is available. Connect it in Capabilities or ask an organization administrator to share the installed bot."
23288
+ );
23162
23289
  }
23163
23290
  if (activeConnections.length > 1) {
23164
23291
  const principals = new Set(
@@ -23169,7 +23296,7 @@ async function resolveSlackBotConnectionForTool(input) {
23169
23296
  );
23170
23297
  if (principals.size > 1) {
23171
23298
  throw new Error(
23172
- "connectionId is required because this workspace has multiple active Opengeni Slack bot connections"
23299
+ `Choose connectionId because multiple Opengeni bots are available: ${activeConnections.slice(0, 20).map((bot) => `${openGeniSlackBotMetadataLabel(bot)} (${bot.id})`).join(", ")}`
23173
23300
  );
23174
23301
  }
23175
23302
  }
@@ -23184,18 +23311,29 @@ async function resolveSlackBotConnectionForTool(input) {
23184
23311
  if (!metadata) {
23185
23312
  throw new Error("Opengeni Slack bot connection metadata is invalid");
23186
23313
  }
23314
+ const sharingGeneration = connection.workspaceId === input.grant.workspaceId ? 0 : Number(connection.metadata.organizationSharingGeneration);
23187
23315
  return {
23188
23316
  connection,
23189
23317
  metadata,
23318
+ sharingGeneration,
23319
+ ...connection.workspaceId !== input.grant.workspaceId ? {
23320
+ authorizeOrganizationAccess: async () => (await sharedOrganizationSlackBots(input.db, input.grant)).some(
23321
+ (share) => share.connectionId === connection.id && share.homeWorkspaceId === connection.workspaceId && share.generation === sharingGeneration && share.connectionVersion === connection.version
23322
+ )
23323
+ } : {},
23190
23324
  context: {
23191
23325
  accountId: input.grant.accountId,
23192
- workspaceId: input.grant.workspaceId,
23326
+ workspaceId: connection.workspaceId,
23193
23327
  subjectId: input.grant.subjectId,
23194
23328
  sessionId: input.sessionId,
23195
23329
  scheduledTaskId: typeof session?.metadata.scheduledTaskId === "string" ? session.metadata.scheduledTaskId : null
23196
23330
  }
23197
23331
  };
23198
23332
  }
23333
+ function openGeniSlackBotMetadataLabel(connection) {
23334
+ const metadata = openGeniSlackBotMetadata(connection.metadata);
23335
+ return metadata?.slackTeamName ?? "Slack workspace";
23336
+ }
23199
23337
  async function resolveScheduledSlackBotPostTarget(input) {
23200
23338
  const refuse = (reason) => {
23201
23339
  throw new Error(`Posting to the task's Slack channel is unavailable: ${reason}`);
@@ -23225,21 +23363,18 @@ async function resolveScheduledSlackBotPostTarget(input) {
23225
23363
  }
23226
23364
  async function prepareScheduledSlackBotPost(input) {
23227
23365
  const target2 = await resolveScheduledSlackBotPostTarget(input);
23228
- const message = await prepareScheduledSlackBotMessage(input.db, {
23366
+ const message = await prepareBotMessage(input.db, {
23229
23367
  accountId: input.grant.accountId,
23230
23368
  workspaceId: input.grant.workspaceId,
23231
23369
  sessionId: input.sessionId,
23232
23370
  scheduledTaskId: target2.scheduledTaskId,
23233
23371
  connectionId: target2.connection.id,
23234
23372
  connectionVersion: target2.connection.version,
23373
+ homeWorkspaceId: target2.connection.workspaceId,
23374
+ sharingGeneration: target2.sharingGeneration ?? 0,
23235
23375
  channelId: target2.channelId,
23236
23376
  threadTimestamp: input.threadTimestamp ?? null,
23237
23377
  text: input.text
23238
- }).catch((error) => {
23239
- if (error instanceof ScheduledSlackBotMessageRefusedError) {
23240
- throw new Error(`Posting to the task's Slack channel is unavailable: ${error.message}`);
23241
- }
23242
- throw error;
23243
23378
  });
23244
23379
  return {
23245
23380
  messageId: message.id,
@@ -23250,9 +23385,143 @@ async function prepareScheduledSlackBotPost(input) {
23250
23385
  sent: false
23251
23386
  };
23252
23387
  }
23388
+ async function prepareSlackBotPost(input) {
23389
+ if (!input.sessionId) throw new Error("Bot posting requires a chat");
23390
+ const session = await getSession2(input.db, input.grant.workspaceId, input.sessionId);
23391
+ if (!session) throw new Error("Bot posting chat is unavailable");
23392
+ if (isTrustedScheduledSlackBotSession(session)) {
23393
+ if (input.channelId || input.connectionId)
23394
+ throw new Error(
23395
+ "A scheduled task uses only the bot and channel chosen by a person on the task"
23396
+ );
23397
+ return prepareScheduledSlackBotPost(input);
23398
+ }
23399
+ await authorizeOrdinaryBotPosting(input);
23400
+ if (!input.grant.permissions.includes("connections:read"))
23401
+ throw new Error("connections:read is required to post as the bot");
23402
+ if (!input.channelId)
23403
+ throw new Error(
23404
+ "Choose the Slack channel for this bot message. For a schedule, choose Post to Slack in the schedule editor."
23405
+ );
23406
+ const target2 = await resolveSlackBotConnectionForTool({
23407
+ db: input.db,
23408
+ grant: input.grant,
23409
+ sessionId: input.sessionId,
23410
+ ...input.connectionId ? { requestedConnectionId: input.connectionId } : {}
23411
+ });
23412
+ const message = await prepareBotMessage(input.db, {
23413
+ accountId: input.grant.accountId,
23414
+ workspaceId: input.grant.workspaceId,
23415
+ sessionId: input.sessionId,
23416
+ scheduledTaskId: null,
23417
+ connectionId: target2.connection.id,
23418
+ connectionVersion: target2.connection.version,
23419
+ homeWorkspaceId: target2.connection.workspaceId,
23420
+ sharingGeneration: target2.sharingGeneration ?? 0,
23421
+ channelId: input.channelId,
23422
+ threadTimestamp: input.threadTimestamp ?? null,
23423
+ text: input.text
23424
+ });
23425
+ return {
23426
+ messageId: message.id,
23427
+ identity: "organization_bot",
23428
+ channelId: message.channelId,
23429
+ threadTimestamp: message.threadTimestamp,
23430
+ text: message.text,
23431
+ sent: false
23432
+ };
23433
+ }
23434
+ async function sendSlackBotPost(input) {
23435
+ if (!input.sessionId) throw new Error("Bot posting requires a chat");
23436
+ const message = await readPreparedBotMessage(input.db, {
23437
+ accountId: input.grant.accountId,
23438
+ workspaceId: input.grant.workspaceId,
23439
+ sessionId: input.sessionId,
23440
+ id: input.messageId
23441
+ });
23442
+ if (!message) throw new Error("This prepared bot message does not exist in this chat");
23443
+ if (message.scheduledTaskId) return sendScheduledSlackBotPost(input);
23444
+ await authorizeOrdinaryBotPosting(input);
23445
+ const earlier = await getSlackBotPostOperation(
23446
+ input.db,
23447
+ message.homeWorkspaceId,
23448
+ message.connectionId,
23449
+ message.id
23450
+ );
23451
+ if (earlier?.status === "completed") return completedPreparedBotPost(message, earlier);
23452
+ if (!input.grant.permissions.includes("connections:read"))
23453
+ throw new Error("connections:read is required to post as the bot");
23454
+ const target2 = await resolveSlackBotConnectionForTool({
23455
+ db: input.db,
23456
+ grant: input.grant,
23457
+ sessionId: input.sessionId,
23458
+ requestedConnectionId: message.connectionId
23459
+ });
23460
+ if (message.connectionVersion !== target2.connection.version || message.homeWorkspaceId !== target2.connection.workspaceId || message.sharingGeneration !== (target2.sharingGeneration ?? 0)) {
23461
+ throw new Error(
23462
+ "Bot access changed after this message was prepared. It was not sent again; an earlier interrupted send may already have posted it."
23463
+ );
23464
+ }
23465
+ const client = createOpenGeniSlackBotClient(input, target2);
23466
+ return client.postMessage({
23467
+ operationId: message.id,
23468
+ channelId: message.channelId,
23469
+ ...message.threadTimestamp ? { threadTimestamp: message.threadTimestamp } : {},
23470
+ text: message.text,
23471
+ requireActiveNonSharedChannel: true
23472
+ });
23473
+ }
23474
+ async function authorizeOrdinaryBotPosting(input) {
23475
+ if (!grantHasAgentAttemptAuthority2(input.grant)) return;
23476
+ const { sessionId, turnId, attemptId, executionGeneration } = input.grant.metadata ?? {};
23477
+ if (typeof sessionId !== "string" || sessionId !== input.sessionId || typeof turnId !== "string" || typeof attemptId !== "string" || typeof executionGeneration !== "number" || !Number.isSafeInteger(executionGeneration) || executionGeneration < 1)
23478
+ throw new Error("Exact live agent attempt required for bot posting");
23479
+ const policy = await freezeAgentLearningPolicy2(input.db, {
23480
+ accountId: input.grant.accountId,
23481
+ workspaceId: input.grant.workspaceId,
23482
+ actor: { kind: "agent", sessionId, turnId, attemptId, executionGeneration }
23483
+ });
23484
+ if (typeof policy.scheduledTaskRunId === "string") {
23485
+ throw new Error(
23486
+ "A scheduled occurrence can post as the bot only to its person-chosen Post to Slack destination. Tasks that continue an existing chat cannot post as the bot."
23487
+ );
23488
+ }
23489
+ }
23490
+ function completedPreparedBotPost(message, operation) {
23491
+ return {
23492
+ channelId: operation.slackChannelId,
23493
+ timestamp: operation.slackMessageTimestamp,
23494
+ threadTimestamp: message.threadTimestamp,
23495
+ alreadySent: true,
23496
+ receipt: {
23497
+ credentialRole: OPENGENI_SLACK_BOT_CREDENTIAL_ROLE,
23498
+ credentialLabel: OPENGENI_SLACK_BOT_CREDENTIAL_LABEL,
23499
+ connectionId: message.connectionId,
23500
+ operation: "message.post",
23501
+ operationId: message.id,
23502
+ clientMessageId: message.id
23503
+ }
23504
+ };
23505
+ }
23253
23506
  async function sendScheduledSlackBotPost(input) {
23507
+ if (!input.sessionId) throw new Error("Bot posting requires a chat");
23508
+ const saved = await readPreparedBotMessage(input.db, {
23509
+ accountId: input.grant.accountId,
23510
+ workspaceId: input.grant.workspaceId,
23511
+ sessionId: input.sessionId,
23512
+ id: input.messageId
23513
+ });
23514
+ if (saved) {
23515
+ const earlier2 = await getSlackBotPostOperation(
23516
+ input.db,
23517
+ saved.homeWorkspaceId,
23518
+ saved.connectionId,
23519
+ saved.id
23520
+ );
23521
+ if (earlier2?.status === "completed") return completedPreparedBotPost(saved, earlier2);
23522
+ }
23254
23523
  const target2 = await resolveScheduledSlackBotPostTarget(input);
23255
- const message = await readScheduledSlackBotMessage(input.db, {
23524
+ const message = await readPreparedBotMessage(input.db, {
23256
23525
  accountId: input.grant.accountId,
23257
23526
  workspaceId: input.grant.workspaceId,
23258
23527
  sessionId: input.sessionId,
@@ -23262,13 +23531,20 @@ async function sendScheduledSlackBotPost(input) {
23262
23531
  throw new Error("This prepared Slack message does not exist in this chat");
23263
23532
  }
23264
23533
  const channelChanged = message.channelId !== target2.channelId || message.scheduledTaskId !== target2.scheduledTaskId;
23265
- const botChanged = message.connectionId !== target2.connection.id || message.connectionVersion !== target2.connection.version;
23534
+ const botChanged = message.connectionId !== target2.connection.id || message.connectionVersion !== target2.connection.version || message.homeWorkspaceId !== target2.connection.workspaceId || message.sharingGeneration !== (target2.sharingGeneration ?? 0);
23266
23535
  const client = createOpenGeniSlackBotClient(
23267
23536
  {
23268
23537
  db: input.db,
23269
23538
  settings: input.settings,
23270
23539
  ...input.slackFetch ? { slackFetch: input.slackFetch } : {},
23271
- ...input.authorizeProviderRequest ? { authorizeProviderRequest: input.authorizeProviderRequest } : {}
23540
+ ...input.authorizeProviderRequest ? { authorizeProviderRequest: input.authorizeProviderRequest } : {},
23541
+ authorizeBotMutation: async () => {
23542
+ const current = await resolveScheduledSlackBotPostTarget(input);
23543
+ if (current.channelId !== message.channelId || current.connection.id !== message.connectionId || current.connection.version !== message.connectionVersion || (current.sharingGeneration ?? 0) !== message.sharingGeneration)
23544
+ throw new Error(
23545
+ "The task's bot destination changed before sending; the message was not sent again"
23546
+ );
23547
+ }
23272
23548
  },
23273
23549
  target2
23274
23550
  );
@@ -23282,7 +23558,7 @@ async function sendScheduledSlackBotPost(input) {
23282
23558
  if (!channelChanged && !botChanged) return await post();
23283
23559
  const earlier = await getSlackBotPostOperation(
23284
23560
  input.db,
23285
- input.grant.workspaceId,
23561
+ message.homeWorkspaceId,
23286
23562
  message.connectionId,
23287
23563
  message.id
23288
23564
  );
@@ -23322,7 +23598,7 @@ async function verifyScheduledTaskSlackChannel(deps, input) {
23322
23598
  }
23323
23599
  }
23324
23600
  var OpenGeniSlackBotClient = class {
23325
- constructor(db, settings, connection, metadata, context, fetchImpl = fetch, authorizeProviderRequest) {
23601
+ constructor(db, settings, connection, metadata, context, fetchImpl = fetch, authorizeProviderRequest, authorizeBotAccess) {
23326
23602
  this.db = db;
23327
23603
  this.settings = settings;
23328
23604
  this.connection = connection;
@@ -23330,6 +23606,7 @@ var OpenGeniSlackBotClient = class {
23330
23606
  this.context = context;
23331
23607
  this.fetchImpl = fetchImpl;
23332
23608
  this.authorizeProviderRequest = authorizeProviderRequest;
23609
+ this.authorizeBotAccess = authorizeBotAccess;
23333
23610
  this.resolveCredential = buildConnectionTokenResolver2(db, settings);
23334
23611
  this.rateLimit = buildSlackApiRateLimiter2(db, settings);
23335
23612
  }
@@ -24442,6 +24719,9 @@ var OpenGeniSlackBotClient = class {
24442
24719
  if (result.status !== "ok" || result.connectionId !== this.connection.id) {
24443
24720
  throw new Error("Opengeni Slack bot connection needs to be reinstalled");
24444
24721
  }
24722
+ if (this.authorizeProviderRequest && await this.authorizeProviderRequest() === false) {
24723
+ throw new Error("Opengeni Slack bot provider request is no longer authorized");
24724
+ }
24445
24725
  const current = await requireOpenGeniSlackBotConnection(
24446
24726
  this.db,
24447
24727
  this.context.workspaceId,
@@ -24454,12 +24734,12 @@ var OpenGeniSlackBotClient = class {
24454
24734
  if (current.accountId !== this.context.accountId || current.version !== this.connection.version || result.connectionVersion !== this.connection.version || !currentMetadata || currentMetadata.slackTeamId !== this.metadata.slackTeamId || currentMetadata.botId !== this.metadata.botId || currentMetadata.botUserId !== this.metadata.botUserId) {
24455
24735
  throw new Error("Opengeni Slack bot connection authority changed");
24456
24736
  }
24457
- if (this.authorizeProviderRequest && await this.authorizeProviderRequest() === false) {
24458
- throw new Error("Opengeni Slack bot provider request is no longer authorized");
24459
- }
24460
24737
  if (credentialResolutionMode === "execution" && result.authorizeProviderRequest && !await result.authorizeProviderRequest()) {
24461
24738
  throw new Error("Opengeni Slack bot provider request is no longer authorized");
24462
24739
  }
24740
+ if (this.authorizeBotAccess && await this.authorizeBotAccess(new URL(destinationUrl).pathname.split("/").at(-1) ?? "") === false) {
24741
+ throw new Error("Opengeni Slack bot organization access is no longer authorized");
24742
+ }
24463
24743
  return result.headers;
24464
24744
  }
24465
24745
  async fetchPrivateFile(url, operation, authorizeBeforeFetch) {
@@ -24656,7 +24936,13 @@ function createOpenGeniSlackBotClient(deps, resolved) {
24656
24936
  resolved.metadata,
24657
24937
  resolved.context,
24658
24938
  deps.slackFetch,
24659
- deps.authorizeProviderRequest
24939
+ deps.authorizeProviderRequest,
24940
+ async (method) => {
24941
+ if (method === "chat.postMessage") await deps.authorizeBotMutation?.();
24942
+ if (resolved.authorizeOrganizationAccess && await resolved.authorizeOrganizationAccess() === false)
24943
+ return false;
24944
+ return true;
24945
+ }
24660
24946
  );
24661
24947
  }
24662
24948
  async function createOpenGeniSlackBotInteractionClient(deps, input) {
@@ -24677,13 +24963,20 @@ async function createOpenGeniSlackBotInteractionClient(deps, input) {
24677
24963
  metadata,
24678
24964
  {
24679
24965
  accountId: input.accountId,
24680
- workspaceId: input.workspaceId,
24966
+ workspaceId: connection.workspaceId,
24681
24967
  subjectId: input.subjectId,
24682
24968
  sessionId: input.sessionId ?? null,
24683
24969
  scheduledTaskId: null
24684
24970
  },
24685
24971
  deps.slackFetch,
24686
- deps.authorizeProviderRequest
24972
+ deps.authorizeProviderRequest,
24973
+ async () => {
24974
+ if (connection.workspaceId !== input.workspaceId && !(await sharedOrganizationSlackBots(deps.db, input)).some(
24975
+ (share) => share.connectionId === connection.id && share.homeWorkspaceId === connection.workspaceId && share.generation === Number(connection.metadata.organizationSharingGeneration) && share.connectionVersion === connection.version
24976
+ ))
24977
+ return false;
24978
+ return true;
24979
+ }
24687
24980
  );
24688
24981
  }
24689
24982
  async function slackApiFetch(fetchImpl, method, token, params) {
@@ -25774,7 +26067,7 @@ import {
25774
26067
  getConnectAttempt as getConnectAttempt4,
25775
26068
  finishConnectOperation as finishConnectOperation3,
25776
26069
  getConnectionMetadata as getConnectionMetadata2,
25777
- listConnectionsMetadata as listConnectionsMetadata3,
26070
+ listConnectionsMetadata as listConnectionsMetadata2,
25778
26071
  loadConnectionCredentialForBroker,
25779
26072
  recordAuditEvent as recordAuditEvent5,
25780
26073
  setConnectionStatus as setConnectionStatus2,
@@ -25972,7 +26265,7 @@ async function resolveFikenConnectionForTool(input) {
25972
26265
  input.requestedConnectionId,
25973
26266
  null
25974
26267
  ) : preferredFikenConnection(
25975
- (await listConnectionsMetadata3(input.db, input.grant.workspaceId, null)).filter(
26268
+ (await listConnectionsMetadata2(input.db, input.grant.workspaceId, null)).filter(
25976
26269
  isFikenConnection
25977
26270
  )
25978
26271
  );
@@ -32605,7 +32898,22 @@ function registerSlackBotTools(server, deps, grant, sessionId, json2) {
32605
32898
  sessionId,
32606
32899
  ...connectionId ? { requestedConnectionId: connectionId } : {}
32607
32900
  });
32608
- return createOpenGeniSlackBotClient(deps, resolved);
32901
+ return createOpenGeniSlackBotClient(
32902
+ {
32903
+ ...deps,
32904
+ authorizeProviderRequest: async () => {
32905
+ if (sessionId !== null)
32906
+ await authorizeFirstPartySession(
32907
+ deps,
32908
+ grant,
32909
+ sessionId,
32910
+ "session.first_party_mcp.call"
32911
+ );
32912
+ return true;
32913
+ }
32914
+ },
32915
+ resolved
32916
+ );
32609
32917
  };
32610
32918
  server.registerTool(
32611
32919
  "slack_bot_list_channels",
@@ -32775,29 +33083,33 @@ function registerSlackBotTools(server, deps, grant, sessionId, json2) {
32775
33083
  );
32776
33084
  }
32777
33085
  );
32778
- const authorizeScheduledPost = async () => {
33086
+ const authorizeBotPost = async () => {
32779
33087
  if (sessionId === null) {
32780
- throw new Error("Posting to the task's Slack channel requires a scheduled task run");
33088
+ throw new Error("Bot posting requires a chat");
32781
33089
  }
32782
33090
  await authorizeFirstPartySession(deps, grant, sessionId, "session.first_party_mcp.call");
32783
33091
  };
32784
33092
  server.registerTool(
32785
33093
  "slack_bot_prepare_message",
32786
33094
  {
32787
- description: "Prepare a message for this scheduled task's Slack channel, posted as the Opengeni workspace bot. The channel was chosen by a person on the task; you cannot pick another one. This saves the exact text without sending it. Then call slack_bot_send_prepared_message with the returned messageId. Pass threadTimestamp (a timestamp returned by an earlier send) to reply in that thread of the same channel.",
33095
+ description: "Prepare a message as the Opengeni bot, never as a personal Slack account. In an ordinary chat supply channelId and, if multiple bots are available, connectionId. Scheduled runs omit both and use only the channel chosen by a person in Post to Slack. Nothing is sent until slack_bot_send_prepared_message is called with the returned messageId. Reuse that messageId for retries. Bot membership and organization sharing are checked when sending.",
32788
33096
  inputSchema: {
33097
+ connectionId: z43.string().uuid().optional(),
33098
+ channelId: z43.string().regex(/^[CG][A-Z0-9]{2,63}$/).optional(),
32789
33099
  text: z43.string().min(1).max(4e4),
32790
33100
  threadTimestamp: z43.string().regex(/^\d{1,20}\.\d{1,12}$/).optional()
32791
33101
  }
32792
33102
  },
32793
- async ({ text, threadTimestamp }) => {
32794
- await authorizeScheduledPost();
33103
+ async ({ text, threadTimestamp, channelId, connectionId }) => {
33104
+ await authorizeBotPost();
32795
33105
  return json2(
32796
- await prepareScheduledSlackBotPost({
33106
+ await prepareSlackBotPost({
32797
33107
  db: deps.db,
32798
33108
  grant,
32799
33109
  sessionId,
32800
33110
  text,
33111
+ ...channelId ? { channelId } : {},
33112
+ ...connectionId ? { connectionId } : {},
32801
33113
  ...threadTimestamp ? { threadTimestamp } : {}
32802
33114
  })
32803
33115
  );
@@ -32806,20 +33118,20 @@ function registerSlackBotTools(server, deps, grant, sessionId, json2) {
32806
33118
  server.registerTool(
32807
33119
  "slack_bot_send_prepared_message",
32808
33120
  {
32809
- description: "Send a message prepared by slack_bot_prepare_message in this chat, exactly as saved, to the task's Slack channel as the Opengeni workspace bot. If a send is interrupted or its outcome is unclear, retry with the same messageId: Opengeni checks Slack and never posts the same message twice. Do not prepare a new message just to retry.",
33121
+ description: "Send a message prepared by slack_bot_prepare_message in this chat, exactly as saved, as the Opengeni bot. If a send is interrupted or its outcome is unclear, retry with the same messageId: Opengeni checks Slack and never posts the same message twice. Do not prepare a new message just to retry.",
32810
33122
  inputSchema: { messageId: z43.string().uuid() }
32811
33123
  },
32812
33124
  async ({ messageId }) => {
32813
- await authorizeScheduledPost();
33125
+ await authorizeBotPost();
32814
33126
  return json2(
32815
- await sendScheduledSlackBotPost({
33127
+ await sendSlackBotPost({
32816
33128
  db: deps.db,
32817
33129
  settings: deps.settings,
32818
33130
  grant,
32819
33131
  sessionId,
32820
33132
  messageId,
32821
33133
  ...deps.slackFetch ? { slackFetch: deps.slackFetch } : {},
32822
- authorizeProviderRequest: authorizeScheduledPost
33134
+ authorizeProviderRequest: authorizeBotPost
32823
33135
  })
32824
33136
  );
32825
33137
  }
@@ -36817,7 +37129,8 @@ var ACTION_CATALOG = [
36817
37129
  ],
36818
37130
  "response": [
36819
37131
  "AcceptOrganizationInvitationResponse"
36820
- ]
37132
+ ],
37133
+ "browserOnly": "it acts on the person's own account across organizations (creating an organization, listing their memberships or invitations, accepting an invitation), and a connection is limited to one organization"
36821
37134
  },
36822
37135
  {
36823
37136
  "id": "acceptOrganizationRecoveryCustody",
@@ -36828,7 +37141,8 @@ var ACTION_CATALOG = [
36828
37141
  ],
36829
37142
  "response": [
36830
37143
  "OrganizationRecoveryMutationResponse"
36831
- ]
37144
+ ],
37145
+ "browserOnly": "organization recovery is a ceremony in the person's own browser session"
36832
37146
  },
36833
37147
  {
36834
37148
  "id": "acknowledgeStream",
@@ -37070,7 +37384,8 @@ var ACTION_CATALOG = [
37070
37384
  ],
37071
37385
  "response": [
37072
37386
  "OrganizationRecoveryMutationResponse"
37073
- ]
37387
+ ],
37388
+ "browserOnly": "organization recovery is a ceremony in the person's own browser session"
37074
37389
  },
37075
37390
  {
37076
37391
  "id": "approveSlackUserLinkAccessRequest",
@@ -37165,7 +37480,8 @@ var ACTION_CATALOG = [
37165
37480
  ],
37166
37481
  "response": [
37167
37482
  "BeginExternalIdentityLinkResponse"
37168
- ]
37483
+ ],
37484
+ "browserOnly": "linking a product user to an Opengeni account is confirmed by that person signed in to Opengeni, or started by the embedding product as its user"
37169
37485
  },
37170
37486
  {
37171
37487
  "id": "beginSessionRealtime",
@@ -37226,7 +37542,8 @@ var ACTION_CATALOG = [
37226
37542
  ],
37227
37543
  "response": [
37228
37544
  "OrganizationRecoveryMutationResponse"
37229
- ]
37545
+ ],
37546
+ "browserOnly": "organization recovery is a ceremony in the person's own browser session"
37230
37547
  },
37231
37548
  {
37232
37549
  "id": "cancelSessionBackgroundCommand",
@@ -37417,7 +37734,8 @@ var ACTION_CATALOG = [
37417
37734
  ],
37418
37735
  "response": [
37419
37736
  "OrganizationRecoveryMutationResponse"
37420
- ]
37737
+ ],
37738
+ "browserOnly": "organization recovery is a ceremony in the person's own browser session"
37421
37739
  },
37422
37740
  {
37423
37741
  "id": "confirmIdentityLink",
@@ -37429,7 +37747,8 @@ var ACTION_CATALOG = [
37429
37747
  "response": [
37430
37748
  "ExternalIdentityLink",
37431
37749
  "ExternalIdentityLinkPreview"
37432
- ]
37750
+ ],
37751
+ "browserOnly": "linking a product user to an Opengeni account is confirmed by that person signed in to Opengeni, or started by the embedding product as its user"
37433
37752
  },
37434
37753
  {
37435
37754
  "id": "connectClaudeSubscriptionSetupToken",
@@ -37482,7 +37801,8 @@ var ACTION_CATALOG = [
37482
37801
  ],
37483
37802
  "response": [
37484
37803
  "CreateAdditionalOrganizationResponse"
37485
- ]
37804
+ ],
37805
+ "browserOnly": "it acts on the person's own account across organizations (creating an organization, listing their memberships or invitations, accepting an invitation), and a connection is limited to one organization"
37486
37806
  },
37487
37807
  {
37488
37808
  "id": "createApiKey",
@@ -37707,7 +38027,8 @@ var ACTION_CATALOG = [
37707
38027
  ],
37708
38028
  "response": [
37709
38029
  "CreateOrganizationResponse"
37710
- ]
38030
+ ],
38031
+ "browserOnly": "it acts on the person's own account across organizations (creating an organization, listing their memberships or invitations, accepting an invitation), and a connection is limited to one organization"
37711
38032
  },
37712
38033
  {
37713
38034
  "id": "createOrganizationApiKey",
@@ -38290,7 +38611,8 @@ var ACTION_CATALOG = [
38290
38611
  ],
38291
38612
  "response": [
38292
38613
  "OrganizationRecoveryMutationResponse"
38293
- ]
38614
+ ],
38615
+ "browserOnly": "organization recovery is a ceremony in the person's own browser session"
38294
38616
  },
38295
38617
  {
38296
38618
  "id": "disableSource",
@@ -38457,7 +38779,8 @@ var ACTION_CATALOG = [
38457
38779
  ],
38458
38780
  "response": [
38459
38781
  "OrganizationRecoveryMutationResponse"
38460
- ]
38782
+ ],
38783
+ "browserOnly": "organization recovery is a ceremony in the person's own browser session"
38461
38784
  },
38462
38785
  {
38463
38786
  "id": "exportCompanyBrainOkf",
@@ -38717,7 +39040,8 @@ var ACTION_CATALOG = [
38717
39040
  "method": "GET",
38718
39041
  "path": "/v1/workspaces/:workspaceId/identity-links/:linkId/:operation",
38719
39042
  "request": [],
38720
- "response": []
39043
+ "response": [],
39044
+ "browserOnly": "linking a product user to an Opengeni account is confirmed by that person signed in to Opengeni, or started by the embedding product as its user"
38721
39045
  },
38722
39046
  {
38723
39047
  "id": "GET /v1/workspaces/:workspaceId/insights/calls",
@@ -39168,7 +39492,8 @@ var ACTION_CATALOG = [
39168
39492
  "request": [],
39169
39493
  "response": [
39170
39494
  "ExternalIdentityLink"
39171
- ]
39495
+ ],
39496
+ "browserOnly": "linking a product user to an Opengeni account is confirmed by that person signed in to Opengeni, or started by the embedding product as its user"
39172
39497
  },
39173
39498
  {
39174
39499
  "id": "getInstalledPluginDetails",
@@ -39242,6 +39567,15 @@ var ACTION_CATALOG = [
39242
39567
  "NewSessionDraft"
39243
39568
  ]
39244
39569
  },
39570
+ {
39571
+ "id": "getOpenGeniSlackBotOrganizationAccess",
39572
+ "method": "GET",
39573
+ "path": "/v1/workspaces/:workspaceId/connections/:connectionId/slack-bot/organization-access",
39574
+ "request": [],
39575
+ "response": [
39576
+ "OpenGeniSlackBotOrganizationAccess"
39577
+ ]
39578
+ },
39245
39579
  {
39246
39580
  "id": "getOrganizationAdministrationOverview",
39247
39581
  "method": "GET",
@@ -39348,7 +39682,8 @@ var ACTION_CATALOG = [
39348
39682
  "request": [],
39349
39683
  "response": [
39350
39684
  "OrganizationRecoveryOverview"
39351
- ]
39685
+ ],
39686
+ "browserOnly": "organization recovery is a ceremony in the person's own browser session"
39352
39687
  },
39353
39688
  {
39354
39689
  "id": "getOrganizationRetentionPolicy",
@@ -39587,6 +39922,24 @@ var ACTION_CATALOG = [
39587
39922
  "SessionCapabilities"
39588
39923
  ]
39589
39924
  },
39925
+ {
39926
+ "id": "getToolActionReview",
39927
+ "method": "GET",
39928
+ "path": "/v1/workspaces/:workspaceId/sessions/:sessionId/tool-reviews/:approvalId",
39929
+ "request": [],
39930
+ "response": [
39931
+ "ToolActionReview"
39932
+ ]
39933
+ },
39934
+ {
39935
+ "id": "getToolReviewDetails",
39936
+ "method": "GET",
39937
+ "path": "/v1/workspaces/:workspaceId/sessions/:sessionId/tool-reviews/:approvalId/details",
39938
+ "request": [],
39939
+ "response": [
39940
+ "ToolReviewDetailsPage"
39941
+ ]
39942
+ },
39590
39943
  {
39591
39944
  "id": "getTranscriptionRecording",
39592
39945
  "method": "GET",
@@ -40123,6 +40476,15 @@ var ACTION_CATALOG = [
40123
40476
  "AuthRunListResponse"
40124
40477
  ]
40125
40478
  },
40479
+ {
40480
+ "id": "listAvailableOpenGeniSlackBots",
40481
+ "method": "GET",
40482
+ "path": "/v1/workspaces/:workspaceId/connections/slack-bot/available",
40483
+ "request": [],
40484
+ "response": [
40485
+ "AvailableOpenGeniSlackBots"
40486
+ ]
40487
+ },
40126
40488
  {
40127
40489
  "id": "listBrowserDiagnostics",
40128
40490
  "method": "GET",
@@ -40381,7 +40743,8 @@ var ACTION_CATALOG = [
40381
40743
  "request": [],
40382
40744
  "response": [
40383
40745
  "ExternalIdentityLinkPage"
40384
- ]
40746
+ ],
40747
+ "browserOnly": "linking a product user to an Opengeni account is confirmed by that person signed in to Opengeni, or started by the embedding product as its user"
40385
40748
  },
40386
40749
  {
40387
40750
  "id": "listInstalledPlugins",
@@ -40556,7 +40919,8 @@ var ACTION_CATALOG = [
40556
40919
  "request": [],
40557
40920
  "response": [
40558
40921
  "ListOrganizationInvitationsPageResponse"
40559
- ]
40922
+ ],
40923
+ "browserOnly": "it acts on the person's own account across organizations (creating an organization, listing their memberships or invitations, accepting an invitation), and a connection is limited to one organization"
40560
40924
  },
40561
40925
  {
40562
40926
  "id": "listOrganizationInvitationsForOrganization",
@@ -40574,7 +40938,8 @@ var ACTION_CATALOG = [
40574
40938
  "request": [],
40575
40939
  "response": [
40576
40940
  "ListManagedOrganizationMembershipsResponse"
40577
- ]
40941
+ ],
40942
+ "browserOnly": "it acts on the person's own account across organizations (creating an organization, listing their memberships or invitations, accepting an invitation), and a connection is limited to one organization"
40578
40943
  },
40579
40944
  {
40580
40945
  "id": "listOrganizationProviderCustomModels",
@@ -41327,7 +41692,8 @@ var ACTION_CATALOG = [
41327
41692
  "method": "POST",
41328
41693
  "path": "/v1/workspaces/:workspaceId/identity-links/:linkId",
41329
41694
  "request": [],
41330
- "response": []
41695
+ "response": [],
41696
+ "browserOnly": "linking a product user to an Opengeni account is confirmed by that person signed in to Opengeni, or started by the embedding product as its user"
41331
41697
  },
41332
41698
  {
41333
41699
  "id": "POST /v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/pause",
@@ -42435,6 +42801,17 @@ var ACTION_CATALOG = [
42435
42801
  "MemberAllowance"
42436
42802
  ]
42437
42803
  },
42804
+ {
42805
+ "id": "setOpenGeniSlackBotOrganizationAccess",
42806
+ "method": "PUT",
42807
+ "path": "/v1/workspaces/:workspaceId/connections/:connectionId/slack-bot/organization-access",
42808
+ "request": [
42809
+ "UpdateOpenGeniSlackBotOrganizationAccess"
42810
+ ],
42811
+ "response": [
42812
+ "OpenGeniSlackBotOrganizationAccess"
42813
+ ]
42814
+ },
42438
42815
  {
42439
42816
  "id": "setOrganizationClaudeSubscriptionAccountAllocator",
42440
42817
  "method": "PATCH",
@@ -42614,7 +42991,8 @@ var ACTION_CATALOG = [
42614
42991
  ],
42615
42992
  "response": [
42616
42993
  "OrganizationRecoveryMutationResponse"
42617
- ]
42994
+ ],
42995
+ "browserOnly": "organization recovery is a ceremony in the person's own browser session"
42618
42996
  },
42619
42997
  {
42620
42998
  "id": "startPersonalGitHubOAuth",
@@ -43572,9 +43950,45 @@ var TOOLS = [
43572
43950
  annotations: { readOnlyHint: false, openWorldHint: false }
43573
43951
  }
43574
43952
  ];
43953
+ var BRAND_MARK_PATH = "M251 83.5966L207 109L163 83.5966L119 109L75 83.5966L141 45.4915A44 44 0 0 1 185 45.4915ZM185.25 172.3642A44.5 44.5 0 0 1 140.75 172.3642L75 134.4034L119 109L163 134.4034L207 109L251 134.4034Z";
43954
+ function brandMarkSvgDataUri(fill) {
43955
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="71 17 184 184" width="184" height="184"><path fill="${fill}" d="${BRAND_MARK_PATH}"/></svg>`;
43956
+ return `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`;
43957
+ }
43958
+ function organizationMcpIcons(publicOrigin) {
43959
+ const icons = [
43960
+ {
43961
+ src: brandMarkSvgDataUri("#111111"),
43962
+ mimeType: "image/svg+xml",
43963
+ sizes: ["any"],
43964
+ theme: "light"
43965
+ },
43966
+ {
43967
+ src: brandMarkSvgDataUri("#FFFFFF"),
43968
+ mimeType: "image/svg+xml",
43969
+ sizes: ["any"],
43970
+ theme: "dark"
43971
+ }
43972
+ ];
43973
+ if (publicOrigin) {
43974
+ icons.push({
43975
+ src: new URL("/icon-512.png", publicOrigin).href,
43976
+ mimeType: "image/png",
43977
+ sizes: ["512x512"],
43978
+ theme: "light"
43979
+ });
43980
+ }
43981
+ return icons;
43982
+ }
43575
43983
  function buildOrganizationMcpServer(input) {
43576
43984
  const server = new Server2(
43577
- { name: "opengeni", version: "1.0.0" },
43985
+ {
43986
+ name: "opengeni",
43987
+ title: "Opengeni",
43988
+ version: "1.0.0",
43989
+ websiteUrl: "https://opengeni.ai",
43990
+ icons: organizationMcpIcons(input.publicOrigin ?? null)
43991
+ },
43578
43992
  {
43579
43993
  capabilities: { tools: {} },
43580
43994
  instructions: "Opengeni actions: use opengeni_actions_search to find what you need, opengeni_action_describe for its inputs, then opengeni_action_call. Most actions take a workspaceId path parameter; list workspaces first (search 'workspaces')."
@@ -43596,6 +44010,7 @@ function buildOrganizationMcpServer(input) {
43596
44010
  if (!parsed.success) return invalid(parsed.error);
43597
44011
  const entry2 = findAction(parsed.data.id);
43598
44012
  if (!entry2) return failure(`No action "${parsed.data.id}". Search for it first.`);
44013
+ if (entry2.browserOnly) return browserOnly(entry2);
43599
44014
  return json(describeAction(entry2));
43600
44015
  }
43601
44016
  case "opengeni_action_call": {
@@ -43618,11 +44033,12 @@ function findAction(id) {
43618
44033
  function words(value) {
43619
44034
  return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/u).filter(Boolean);
43620
44035
  }
44036
+ var CALLABLE_ACTIONS = ACTION_CATALOG.filter((entry2) => !entry2.browserOnly);
43621
44037
  function searchActions(input) {
43622
44038
  const stem = (word) => word.endsWith("ies") ? `${word.slice(0, -3)}y` : word.replace(/(?<!s)s$/u, "");
43623
44039
  const wanted = words(input.query).map(stem);
43624
44040
  const plural = words(input.query).some((word) => /[^s]s$/u.test(word));
43625
- const scored = ACTION_CATALOG.map((entry2) => {
44041
+ const scored = CALLABLE_ACTIONS.map((entry2) => {
43626
44042
  const name = words(entry2.id).map(stem);
43627
44043
  const path = [...words(entry2.path.replace(/:\w+/g, "")), entry2.method.toLowerCase()].map(stem);
43628
44044
  const score = wanted.reduce(
@@ -43679,6 +44095,7 @@ function describeAction(entry2) {
43679
44095
  async function callAction(context, input) {
43680
44096
  const entry2 = findAction(input.id);
43681
44097
  if (!entry2) return failure(`No action "${input.id}". Search for it first.`);
44098
+ if (entry2.browserOnly) return browserOnly(entry2);
43682
44099
  const reads = entry2.method === "GET" || entry2.method === "HEAD";
43683
44100
  if (context.caller.kind === "person" && !reads && !READ_ONLY_POST_ACTIONS.has(entry2.path) && isReadOnlyPermissionSet(context.caller.access.permissions)) {
43684
44101
  return failure("This connection is read only, so it can't change anything.");
@@ -43757,7 +44174,7 @@ async function toolResult(response) {
43757
44174
  if (truncated)
43758
44175
  result.truncated = { returnedBytes: MAX_RESPONSE_BYTES, totalBytes: bytes.byteLength };
43759
44176
  }
43760
- if (status === 403 && /human|browser|cookie|same-origin/i.test(JSON.stringify(result.body ?? ""))) {
44177
+ if ((status === 401 || status === 403) && /human|browser|cookie|same-origin/i.test(JSON.stringify(result.body ?? ""))) {
43761
44178
  result.hint = "This action has to be done by the person in the Opengeni app in a browser.";
43762
44179
  }
43763
44180
  return {
@@ -43793,6 +44210,11 @@ async function readStream(response) {
43793
44210
  function json(value) {
43794
44211
  return { content: [{ type: "text", text: JSON.stringify(value) }] };
43795
44212
  }
44213
+ function browserOnly(entry2) {
44214
+ return failure(
44215
+ `${entry2.id} isn't available to connected agents or API keys: ${entry2.browserOnly}. The person has to do it in the Opengeni app in a browser.`
44216
+ );
44217
+ }
43796
44218
  function failure(message) {
43797
44219
  return { content: [{ type: "text", text: message }], isError: true };
43798
44220
  }
@@ -73401,7 +73823,9 @@ function registerCapabilityRoutes(app, deps) {
73401
73823
  workspaceId,
73402
73824
  grant: access.grant,
73403
73825
  capabilityId: decodeURIComponent(c.req.param("capabilityId")),
73404
- personalOwnerVerified: isPersonalConnectionOwnerPrincipal(access)
73826
+ personalOwnerVerified: isPersonalConnectionOwnerPrincipal(access),
73827
+ ...c.req.query("connectionId") ? { connectionId: c.req.query("connectionId") } : {},
73828
+ ...c.req.query("instanceKey") ? { instanceKey: c.req.query("instanceKey") } : {}
73405
73829
  })
73406
73830
  )
73407
73831
  );
@@ -73814,7 +74238,7 @@ import {
73814
74238
  import { nestedPostgresSqlState as nestedPostgresSqlState6 } from "@opengeni/db";
73815
74239
  import { HTTPException as HTTPException46 } from "hono/http-exception";
73816
74240
  import { z as z32 } from "zod";
73817
- async function authorizeAdministration(context, deps, accountId, mutation) {
74241
+ async function authorizeOrganizationIntegrationAdministration(context, deps, accountId, mutation) {
73818
74242
  const access = await requireAccessContext4(context, deps);
73819
74243
  const service = accountScopedApiKeyWorkspaceAuthority3(access);
73820
74244
  if (service) {
@@ -73868,7 +74292,7 @@ function registerOrganizationIntegrationPolicyRoutes(app, deps) {
73868
74292
  () => getOrganizationIntegrationPolicy(
73869
74293
  deps.db,
73870
74294
  { accountId },
73871
- () => authorizeAdministration(context, deps, accountId, false)
74295
+ () => authorizeOrganizationIntegrationAdministration(context, deps, accountId, false)
73872
74296
  )
73873
74297
  );
73874
74298
  context.header("cache-control", "private, no-store");
@@ -73882,7 +74306,7 @@ function registerOrganizationIntegrationPolicyRoutes(app, deps) {
73882
74306
  () => getOrganizationIntegrationPolicy(
73883
74307
  deps.db,
73884
74308
  { accountId },
73885
- () => authorizeAdministration(context, deps, accountId, false)
74309
+ () => authorizeOrganizationIntegrationAdministration(context, deps, accountId, false)
73886
74310
  )
73887
74311
  )
73888
74312
  );
@@ -73900,7 +74324,7 @@ function registerOrganizationIntegrationPolicyRoutes(app, deps) {
73900
74324
  deps.db,
73901
74325
  { accountId },
73902
74326
  parsed.data,
73903
- () => authorizeAdministration(context, deps, accountId, true)
74327
+ () => authorizeOrganizationIntegrationAdministration(context, deps, accountId, true)
73904
74328
  )
73905
74329
  )
73906
74330
  );
@@ -74062,7 +74486,7 @@ import {
74062
74486
  encryptEnvironmentValue as encryptEnvironmentValue15,
74063
74487
  brokeredCredentialBundleProblem,
74064
74488
  getConnectionMetadata as getConnectionMetadata8,
74065
- listConnectionsMetadata as listConnectionsMetadata5,
74489
+ listConnectionsMetadata as listConnectionsMetadata4,
74066
74490
  listSlackInstallationBindings,
74067
74491
  persistSlackBotInstallationWithSuccessAudit,
74068
74492
  recordSlackBotInstallCallbackFailure,
@@ -74813,7 +75237,7 @@ import {
74813
75237
  disconnectConnectionIdempotently as disconnectConnectionIdempotently3,
74814
75238
  encryptEnvironmentValue as encryptEnvironmentValue14,
74815
75239
  getConnectionMetadata as getConnectionMetadata7,
74816
- listConnectionsMetadata as listConnectionsMetadata4,
75240
+ listConnectionsMetadata as listConnectionsMetadata3,
74817
75241
  persistProviderOAuthConnection as persistProviderOAuthConnection2
74818
75242
  } from "@opengeni/db";
74819
75243
  import { createSignedState as createSignedState11, readSignedState as readSignedState10 } from "@opengeni/github";
@@ -74854,7 +75278,7 @@ function requirePersonalGitHubOAuthSettings(settings) {
74854
75278
  };
74855
75279
  }
74856
75280
  async function listPersonalGitHubConnections(deps, input) {
74857
- const visible = await listConnectionsMetadata4(deps.db, input.workspaceId, input.subjectId);
75281
+ const visible = await listConnectionsMetadata3(deps.db, input.workspaceId, input.subjectId);
74858
75282
  return visible.filter(
74859
75283
  (connection) => connection.subjectId === input.subjectId && isPersonalGitHubConnection(connection)
74860
75284
  );
@@ -75418,7 +75842,7 @@ function registerConnectionRoutes(app, deps) {
75418
75842
  const grant = await requireAccessGrant14(c, deps, workspaceId, "connections:read");
75419
75843
  return c.json(
75420
75844
  ListConnectionsResponse.parse({
75421
- connections: await listConnectionsMetadata5(db, workspaceId, grant.subjectId)
75845
+ connections: await listConnectionsMetadata4(db, workspaceId, grant.subjectId)
75422
75846
  })
75423
75847
  );
75424
75848
  });
@@ -76904,7 +77328,7 @@ import {
76904
77328
  listPendingConnectAttempts,
76905
77329
  withWorkspaceSubjectRls as withWorkspaceSubjectRls4,
76906
77330
  installApiIntegration as installApiIntegration2,
76907
- listConnectionsMetadata as listConnectionsMetadata6,
77331
+ listConnectionsMetadata as listConnectionsMetadata5,
76908
77332
  getConnectionMetadata as getConnectionMetadata10,
76909
77333
  createConnection as createConnection5,
76910
77334
  updateConnection as updateConnection6,
@@ -77481,9 +77905,7 @@ import { ATLASSIAN_NATIVE_RETIRED_MESSAGE as ATLASSIAN_NATIVE_RETIRED_MESSAGE2 }
77481
77905
  import {
77482
77906
  CORE_INTEGRATION_DEFINITIONS as CORE_INTEGRATION_DEFINITIONS2,
77483
77907
  INTEGRATION_DEFINITION_PRESENTATIONS,
77484
- autoApprovalForbidden,
77485
77908
  createPinnedIntegrationTransport,
77486
- integrationDefinitionById as integrationDefinitionById3,
77487
77909
  integrationFacetDefinitions
77488
77910
  } from "@opengeni/capabilities";
77489
77911
  import {
@@ -77884,16 +78306,6 @@ function validatedIntegrationInstallInput(grant, workspaceId, payload, resolved)
77884
78306
  throw new HTTPException53(422, {
77885
78307
  message: `autoApprovedTools must name selected Integration tools: ${outside.slice(0, 200)}`
77886
78308
  });
77887
- if (resolved.preview.definitionProvenance === "curated") {
77888
- const definition = integrationDefinitionById3(resolved.preview.definitionId);
77889
- const forbidden = resolved.preview.tools.find(
77890
- (tool) => payload.autoApprovedTools.includes(tool.id) && autoApprovalForbidden(definition, tool.operationKey)
77891
- );
77892
- if (forbidden)
77893
- throw new HTTPException53(422, {
77894
- message: `This Integration requires human approval for ${forbidden.id.slice(0, 200)}; it cannot be auto-approved.`
77895
- });
77896
- }
77897
78309
  }
77898
78310
  return {
77899
78311
  accountId: grant.accountId,
@@ -78543,7 +78955,7 @@ function registerConnectRoutes(app, deps) {
78543
78955
  app.get("/v1/workspaces/:workspaceId/connect/accounts", async (c) => {
78544
78956
  const workspaceId = c.req.param("workspaceId");
78545
78957
  const { grant } = await requireAccessGrantAuthorization9(c, deps, workspaceId, "workspace:read");
78546
- const connections = hasPermission13(grant.permissions, "connections:read") ? await listConnectionsMetadata6(deps.db, workspaceId, grant.subjectId) : [];
78958
+ const connections = hasPermission13(grant.permissions, "connections:read") ? await listConnectionsMetadata5(deps.db, workspaceId, grant.subjectId) : [];
78547
78959
  const githubInstallations = hasPermission13(grant.permissions, "github:use") || hasPermission13(grant.permissions, "github:manage") ? await listGitHubInstallationAccessForWorkspace2(deps.db, workspaceId) : [];
78548
78960
  const lensRegistrations = await listPrReviewAppRegistrations(
78549
78961
  deps.db,
@@ -79440,7 +79852,7 @@ function registerConnectRoutes(app, deps) {
79440
79852
  throw new HTTPException54(422, { message: "Select the service connection during setup" });
79441
79853
  if (input.ownership === "personal" && !isPersonalConnectionOwnerPrincipal(authorization))
79442
79854
  throw new HTTPException54(403, { message: "personal ownership unavailable" });
79443
- const choices = hasPermission13(authorization.grant.permissions, "connections:read") ? (await listConnectionsMetadata6(tx, workspaceId, scope.subjectId)).filter(
79855
+ const choices = hasPermission13(authorization.grant.permissions, "connections:read") ? (await listConnectionsMetadata5(tx, workspaceId, scope.subjectId)).filter(
79444
79856
  (connection) => connection.status === "active" && (input.ownership === "workspace" ? connection.subjectId === null : connection.subjectId === scope.subjectId)
79445
79857
  ).map((connection) => ({
79446
79858
  value: connection.id,
@@ -81621,7 +82033,7 @@ import {
81621
82033
  } from "@opengeni/db";
81622
82034
  import { HTTPException as HTTPException59 } from "hono/http-exception";
81623
82035
  import {
81624
- grantHasAgentAttemptAuthority as grantHasAgentAttemptAuthority2,
82036
+ grantHasAgentAttemptAuthority as grantHasAgentAttemptAuthority3,
81625
82037
  requireAccessGrant as requireAccessGrant18,
81626
82038
  requireAccessGrantAuthorization as requireAccessGrantAuthorization14,
81627
82039
  requireSessionAuthorization as requireSessionAuthorization7,
@@ -82165,7 +82577,7 @@ function registerMachineRoutes(app, deps) {
82165
82577
  const grant = await requireAccessGrant18(c, deps, workspaceId, "sessions:control");
82166
82578
  assertSelfhostedEnabled();
82167
82579
  const sessionId = c.req.param("sessionId");
82168
- if (grantHasAgentAttemptAuthority2(grant)) {
82580
+ if (grantHasAgentAttemptAuthority3(grant)) {
82169
82581
  try {
82170
82582
  await requireSessionAuthorization7(deps, grant, {
82171
82583
  sessionId,
@@ -99785,6 +100197,7 @@ import { SandboxRecoveryRequest } from "@opengeni/contracts";
99785
100197
  import { getManagedHumanSandboxRecovery, consentManagedHumanSandboxRecovery } from "@opengeni/core";
99786
100198
  import { SandboxRecoveryConflictError } from "@opengeni/db";
99787
100199
  import { getSessionCodexAccounts } from "@opengeni/db";
100200
+ import { getToolActionReview, getToolReviewDetailsPage } from "@opengeni/db";
99788
100201
  import {
99789
100202
  AcknowledgeStreamRequest,
99790
100203
  ApplySessionGoalRevisionRequest,
@@ -101675,6 +102088,44 @@ function registerSessionRoutes(app, deps) {
101675
102088
  throw error;
101676
102089
  }
101677
102090
  });
102091
+ app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/tool-reviews/:approvalId", async (c) => {
102092
+ const workspaceId = c.req.param("workspaceId");
102093
+ const grant = await requireAccessGrant37(c, deps, workspaceId, "sessions:read");
102094
+ const input = {
102095
+ accountId: grant.accountId,
102096
+ workspaceId,
102097
+ sessionId: c.req.param("sessionId"),
102098
+ approvalId: c.req.param("approvalId")
102099
+ };
102100
+ const review = await getToolActionReview(db, input);
102101
+ if (!review) throw new HTTPException88(404, { message: "Review not found" });
102102
+ c.header("Cache-Control", "private, no-store");
102103
+ return c.json(review);
102104
+ });
102105
+ app.get(
102106
+ "/v1/workspaces/:workspaceId/sessions/:sessionId/tool-reviews/:approvalId/details",
102107
+ async (c) => {
102108
+ const workspaceId = c.req.param("workspaceId");
102109
+ const grant = await requireAccessGrant37(c, deps, workspaceId, "sessions:read");
102110
+ const query = z45.object({
102111
+ actionDigest: z45.string().regex(/^[0-9a-f]{64}$/),
102112
+ path: z45.string().max(2048).default(""),
102113
+ offset: z45.coerce.number().int().min(0).max(4194304).default(0)
102114
+ }).safeParse(c.req.query());
102115
+ if (!query.success) throw new HTTPException88(400, { message: "Invalid review page" });
102116
+ const input = {
102117
+ accountId: grant.accountId,
102118
+ workspaceId,
102119
+ sessionId: c.req.param("sessionId"),
102120
+ approvalId: c.req.param("approvalId"),
102121
+ ...query.data
102122
+ };
102123
+ const page2 = await getToolReviewDetailsPage(db, input);
102124
+ if (!page2) throw new HTTPException88(404, { message: "Review details unavailable" });
102125
+ c.header("Cache-Control", "private, no-store");
102126
+ return c.json(page2);
102127
+ }
102128
+ );
101678
102129
  app.get("/v1/workspaces/:workspaceId/sessions/:sessionId", async (c) => {
101679
102130
  const workspaceId = c.req.param("workspaceId");
101680
102131
  const grant = await requireAccessGrant37(c, deps, workspaceId, "sessions:read");
@@ -105131,6 +105582,7 @@ function sessionAuthorizationOperationForHttp(method, pathname, sessionId) {
105131
105582
  if (suffix === "/lineage" && verb === "GET") return "session.lineage.read";
105132
105583
  if (suffix === "/background-commands" && verb === "GET") return "session.read";
105133
105584
  if (suffix === "/model-context" && verb === "GET") return "session.read";
105585
+ if (/^\/tool-reviews\/[^/]+(?:\/details)?$/.test(suffix) && verb === "GET") return "session.read";
105134
105586
  if (suffix === "/codex-accounts" && verb === "GET") return "session.read";
105135
105587
  if (/^\/background-commands\/[^/]+$/.test(suffix) && verb === "DELETE") {
105136
105588
  return "session.control";
@@ -106347,7 +106799,7 @@ function socialHttpException(error) {
106347
106799
 
106348
106800
  // src/routes/workspaces.ts
106349
106801
  import { withDirectModelProviders } from "@opengeni/config";
106350
- import { listConnectionsMetadata as listConnectionsMetadata7 } from "@opengeni/db";
106802
+ import { listConnectionsMetadata as listConnectionsMetadata6 } from "@opengeni/db";
106351
106803
  import {
106352
106804
  workspaceProviderApiKeyConnectionMetadataFromConnections,
106353
106805
  listWorkspaceProviderCustomModelsByKind,
@@ -106936,7 +107388,7 @@ function registerWorkspaceRoutes(app, deps) {
106936
107388
  workspaceId,
106937
107389
  subjectId: grant.subjectId
106938
107390
  }),
106939
- listConnectionsMetadata7(deps.db, workspaceId, null),
107391
+ listConnectionsMetadata6(deps.db, workspaceId, null),
106940
107392
  listWorkspaceProviderCustomModelsByKind(deps.db, {
106941
107393
  accountId: grant.accountId,
106942
107394
  workspaceId,
@@ -109577,6 +110029,111 @@ function registerSlackTaskPolicyRoutes(app, deps) {
109577
110029
  });
109578
110030
  }
109579
110031
 
110032
+ // src/routes/slack-bot-access.ts
110033
+ import {
110034
+ AvailableOpenGeniSlackBots,
110035
+ OpenGeniSlackBotOrganizationAccess,
110036
+ UpdateOpenGeniSlackBotOrganizationAccess
110037
+ } from "@opengeni/contracts";
110038
+ import {
110039
+ isOpenGeniSlackBotConnection as isOpenGeniSlackBotConnection5,
110040
+ requireAccessGrant as requireAccessGrant45
110041
+ } from "@opengeni/core";
110042
+ import {
110043
+ availableSlackBotConnectionMetadata as availableSlackBotConnectionMetadata2,
110044
+ nestedPostgresSqlState as nestedPostgresSqlState13,
110045
+ readOrganizationSlackBotAccess,
110046
+ setOrganizationSlackBotAccess,
110047
+ sharedOrganizationSlackBots as sharedOrganizationSlackBots2
110048
+ } from "@opengeni/db";
110049
+ import { HTTPException as HTTPException99 } from "hono/http-exception";
110050
+ import { z as z51 } from "zod";
110051
+ function registerSlackBotAccessRoutes(app, deps) {
110052
+ const base = "/v1/workspaces/:workspaceId/connections";
110053
+ app.get(`${base}/:connectionId/slack-bot/organization-access`, async (context) => {
110054
+ const grant = await requireAccessGrant45(
110055
+ context,
110056
+ deps,
110057
+ context.req.param("workspaceId"),
110058
+ "connections:read"
110059
+ );
110060
+ const connectionId = z51.string().uuid().safeParse(context.req.param("connectionId"));
110061
+ if (!connectionId.success) throw new HTTPException99(422, { message: "Invalid bot connection" });
110062
+ context.header("cache-control", "private, no-store");
110063
+ try {
110064
+ return context.json(
110065
+ OpenGeniSlackBotOrganizationAccess.parse(
110066
+ await readOrganizationSlackBotAccess(deps.db, {
110067
+ accountId: grant.accountId,
110068
+ workspaceId: grant.workspaceId,
110069
+ connectionId: connectionId.data
110070
+ })
110071
+ )
110072
+ );
110073
+ } catch (error) {
110074
+ if (nestedPostgresSqlState13(error) === "42501")
110075
+ throw new HTTPException99(404, { message: "Local bot installation unavailable" });
110076
+ throw error;
110077
+ }
110078
+ });
110079
+ app.get(`${base}/slack-bot/available`, async (context) => {
110080
+ const grant = await requireAccessGrant45(
110081
+ context,
110082
+ deps,
110083
+ context.req.param("workspaceId"),
110084
+ "connections:read"
110085
+ );
110086
+ context.header("cache-control", "private, no-store");
110087
+ const [connections, shares] = await Promise.all([
110088
+ availableSlackBotConnectionMetadata2(deps.db, grant),
110089
+ sharedOrganizationSlackBots2(deps.db, grant)
110090
+ ]);
110091
+ return context.json(
110092
+ AvailableOpenGeniSlackBots.parse({
110093
+ connections: connections.filter(
110094
+ (connection) => connection.status === "active" && isOpenGeniSlackBotConnection5(connection)
110095
+ ),
110096
+ organizationSharedConnectionIds: shares.map((share) => share.connectionId)
110097
+ })
110098
+ );
110099
+ });
110100
+ app.put(`${base}/:connectionId/slack-bot/organization-access`, async (context) => {
110101
+ const grant = await requireAccessGrant45(
110102
+ context,
110103
+ deps,
110104
+ context.req.param("workspaceId"),
110105
+ "connections:write"
110106
+ );
110107
+ const connectionId = z51.string().uuid().safeParse(context.req.param("connectionId"));
110108
+ const request = UpdateOpenGeniSlackBotOrganizationAccess.safeParse(
110109
+ await context.req.json().catch(() => null)
110110
+ );
110111
+ if (!connectionId.success || !request.success)
110112
+ throw new HTTPException99(422, { message: "Invalid bot access setting" });
110113
+ context.header("cache-control", "private, no-store");
110114
+ try {
110115
+ const result = await setOrganizationSlackBotAccess(
110116
+ deps.db,
110117
+ {
110118
+ accountId: grant.accountId,
110119
+ workspaceId: grant.workspaceId,
110120
+ connectionId: connectionId.data,
110121
+ enabled: request.data.enabled
110122
+ },
110123
+ () => authorizeOrganizationIntegrationAdministration(context, deps, grant.accountId, true)
110124
+ );
110125
+ return context.json(OpenGeniSlackBotOrganizationAccess.parse(result));
110126
+ } catch (error) {
110127
+ if (error instanceof HTTPException99) throw error;
110128
+ if (nestedPostgresSqlState13(error) === "42501")
110129
+ throw new HTTPException99(403, {
110130
+ message: "Organization administrator and a verified local bot installation required"
110131
+ });
110132
+ throw error;
110133
+ }
110134
+ });
110135
+ }
110136
+
109580
110137
  // src/routes/workspace-state.ts
109581
110138
  import {
109582
110139
  WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT as WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT3,
@@ -109595,7 +110152,7 @@ import {
109595
110152
  listKnowledgeEntries as listKnowledgeEntries4,
109596
110153
  listWorkspaceInstructionPolicyRevisions as listWorkspaceInstructionPolicyRevisions3
109597
110154
  } from "@opengeni/db";
109598
- import { HTTPException as HTTPException99 } from "hono/http-exception";
110155
+ import { HTTPException as HTTPException100 } from "hono/http-exception";
109599
110156
 
109600
110157
  // src/workspace-state-export.ts
109601
110158
  import { createHash as createHash27 } from "crypto";
@@ -109667,7 +110224,7 @@ async function readWorkspaceState(deps, input) {
109667
110224
  attemptId: query.attemptId
109668
110225
  }) : Promise.resolve(null)
109669
110226
  ]);
109670
- if (!workspace) throw new HTTPException99(404, { message: "workspace not found" });
110227
+ if (!workspace) throw new HTTPException100(404, { message: "workspace not found" });
109671
110228
  const attemptGovernance = query.attemptId ? acceptedAttempt ? {
109672
110229
  status: "available",
109673
110230
  attemptId: acceptedAttempt.attemptId,
@@ -109754,7 +110311,7 @@ import {
109754
110311
  normalizeWorkspaceArtifactSlug as normalizeWorkspaceArtifactSlug2
109755
110312
  } from "@opengeni/contracts";
109756
110313
  import {
109757
- requireAccessGrant as requireAccessGrant45,
110314
+ requireAccessGrant as requireAccessGrant46,
109758
110315
  requireAccessGrantAuthorization as requireAccessGrantAuthorization35,
109759
110316
  requireSessionAuthorization as requireSessionAuthorization13,
109760
110317
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError13,
@@ -109765,7 +110322,7 @@ import {
109765
110322
  getWorkspaceArtifact as getWorkspaceArtifact3,
109766
110323
  getWorkspaceArtifactContentRef as getWorkspaceArtifactContentRef3,
109767
110324
  listWorkspaceArtifacts as listWorkspaceArtifacts2,
109768
- nestedPostgresSqlState as nestedPostgresSqlState13,
110325
+ nestedPostgresSqlState as nestedPostgresSqlState14,
109769
110326
  publishWorkspaceArtifactVersion as publishWorkspaceArtifactVersion2,
109770
110327
  rollbackWorkspaceArtifact as rollbackWorkspaceArtifact2,
109771
110328
  setWorkspaceArtifactStatus as setWorkspaceArtifactStatus2,
@@ -109773,23 +110330,23 @@ import {
109773
110330
  WorkspaceArtifactNotFoundError as WorkspaceArtifactNotFoundError3,
109774
110331
  WorkspaceArtifactOperationError as WorkspaceArtifactOperationError2
109775
110332
  } from "@opengeni/db";
109776
- import { HTTPException as HTTPException100 } from "hono/http-exception";
109777
- import { z as z51 } from "zod";
109778
- var ArtifactId3 = z51.string().uuid();
110333
+ import { HTTPException as HTTPException101 } from "hono/http-exception";
110334
+ import { z as z52 } from "zod";
110335
+ var ArtifactId3 = z52.string().uuid();
109779
110336
  async function body(context, schema) {
109780
110337
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
109781
- if (!parsed.success) throw new HTTPException100(422, { message: "Invalid artifact request" });
110338
+ if (!parsed.success) throw new HTTPException101(422, { message: "Invalid artifact request" });
109782
110339
  return parsed.data;
109783
110340
  }
109784
110341
  function parseVersionId(value) {
109785
110342
  if (value !== void 0 && !ArtifactId3.safeParse(value).success) {
109786
- throw new HTTPException100(422, { message: "Invalid artifact version id" });
110343
+ throw new HTTPException101(422, { message: "Invalid artifact version id" });
109787
110344
  }
109788
110345
  return value;
109789
110346
  }
109790
110347
  function artifactId(context) {
109791
110348
  const parsed = ArtifactId3.safeParse(context.req.param("artifactId"));
109792
- if (!parsed.success) throw new HTTPException100(422, { message: "Invalid artifact id" });
110349
+ if (!parsed.success) throw new HTTPException101(422, { message: "Invalid artifact id" });
109793
110350
  return parsed.data;
109794
110351
  }
109795
110352
  function workspaceArtifactErrorResponse(context, error) {
@@ -109812,7 +110369,7 @@ function workspaceArtifactErrorResponse(context, error) {
109812
110369
  422
109813
110370
  );
109814
110371
  }
109815
- if (nestedPostgresSqlState13(error) === "23505") {
110372
+ if (nestedPostgresSqlState14(error) === "23505") {
109816
110373
  return context.json(
109817
110374
  {
109818
110375
  code: "WORKSPACE_ARTIFACT_CONFLICT",
@@ -109825,7 +110382,7 @@ function workspaceArtifactErrorResponse(context, error) {
109825
110382
  }
109826
110383
  function prepareContent(deps, grant, input) {
109827
110384
  if (!deps.objectStorage)
109828
- throw new HTTPException100(503, {
110385
+ throw new HTTPException101(503, {
109829
110386
  message: "Object storage is not configured"
109830
110387
  });
109831
110388
  return prepareWorkspaceArtifactPublication(deps, grant, input);
@@ -109852,7 +110409,7 @@ async function canReadProvenanceSession(deps, grant, sessionId) {
109852
110409
  } catch (error) {
109853
110410
  if (error instanceof SessionAuthorizationDeniedError13) return false;
109854
110411
  if (error instanceof SessionAuthorizationUnavailableError11) {
109855
- throw new HTTPException100(503, {
110412
+ throw new HTTPException101(503, {
109856
110413
  message: "Session authorization is unavailable"
109857
110414
  });
109858
110415
  }
@@ -109870,7 +110427,7 @@ async function mutationResponse(deps, grant, response) {
109870
110427
  function registerWorkspaceArtifactRoutes(app, deps) {
109871
110428
  const base = "/v1/workspaces/:workspaceId/published-artifacts";
109872
110429
  app.post(`${base}/uploads`, async (context) => {
109873
- const grant = await requireAccessGrant45(
110430
+ const grant = await requireAccessGrant46(
109874
110431
  context,
109875
110432
  deps,
109876
110433
  context.req.param("workspaceId"),
@@ -109880,9 +110437,9 @@ function registerWorkspaceArtifactRoutes(app, deps) {
109880
110437
  });
109881
110438
  app.get(`${base}/:artifactId/downloads`, async (context) => {
109882
110439
  const workspaceId = context.req.param("workspaceId");
109883
- await requireAccessGrant45(context, deps, workspaceId, "artifacts:read");
110440
+ await requireAccessGrant46(context, deps, workspaceId, "artifacts:read");
109884
110441
  if (!deps.objectStorage)
109885
- throw new HTTPException100(503, {
110442
+ throw new HTTPException101(503, {
109886
110443
  message: "Object storage is not configured"
109887
110444
  });
109888
110445
  const ref = await getWorkspaceArtifactContentRef3(
@@ -109895,16 +110452,16 @@ function registerWorkspaceArtifactRoutes(app, deps) {
109895
110452
  });
109896
110453
  app.get(base, async (context) => {
109897
110454
  const workspaceId = context.req.param("workspaceId");
109898
- const grant = await requireAccessGrant45(context, deps, workspaceId, "artifacts:read");
110455
+ const grant = await requireAccessGrant46(context, deps, workspaceId, "artifacts:read");
109899
110456
  const query = WorkspaceArtifactListQuery.safeParse({
109900
110457
  limit: context.req.query("limit"),
109901
110458
  cursor: context.req.query("cursor"),
109902
110459
  status: context.req.query("status"),
109903
110460
  sourceSessionId: context.req.query("sourceSessionId")
109904
110461
  });
109905
- if (!query.success) throw new HTTPException100(422, { message: "Invalid artifact list query" });
110462
+ if (!query.success) throw new HTTPException101(422, { message: "Invalid artifact list query" });
109906
110463
  if (query.data.sourceSessionId && !await canReadProvenanceSession(deps, grant, query.data.sourceSessionId)) {
109907
- throw new HTTPException100(404, { message: "Session not found" });
110464
+ throw new HTTPException101(404, { message: "Session not found" });
109908
110465
  }
109909
110466
  try {
109910
110467
  return context.json(
@@ -109967,7 +110524,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
109967
110524
  });
109968
110525
  app.get(`${base}/:artifactId`, async (context) => {
109969
110526
  const workspaceId = context.req.param("workspaceId");
109970
- const grant = await requireAccessGrant45(context, deps, workspaceId, "artifacts:read");
110527
+ const grant = await requireAccessGrant46(context, deps, workspaceId, "artifacts:read");
109971
110528
  try {
109972
110529
  const detail = await getWorkspaceArtifact3(deps.db, workspaceId, artifactId(context));
109973
110530
  return context.json(
@@ -109984,9 +110541,9 @@ function registerWorkspaceArtifactRoutes(app, deps) {
109984
110541
  });
109985
110542
  app.get(`${base}/:artifactId/html`, async (context) => {
109986
110543
  const workspaceId = context.req.param("workspaceId");
109987
- await requireAccessGrant45(context, deps, workspaceId, "artifacts:read");
110544
+ await requireAccessGrant46(context, deps, workspaceId, "artifacts:read");
109988
110545
  if (!deps.objectStorage)
109989
- throw new HTTPException100(503, {
110546
+ throw new HTTPException101(503, {
109990
110547
  message: "Object storage is not configured"
109991
110548
  });
109992
110549
  const ref = await getWorkspaceArtifactContentRef3(
@@ -110030,9 +110587,9 @@ function registerWorkspaceArtifactRoutes(app, deps) {
110030
110587
  });
110031
110588
  app.get(`${base}/:artifactId/content`, async (context) => {
110032
110589
  const workspaceId = context.req.param("workspaceId");
110033
- await requireAccessGrant45(context, deps, workspaceId, "artifacts:read");
110590
+ await requireAccessGrant46(context, deps, workspaceId, "artifacts:read");
110034
110591
  if (!deps.objectStorage)
110035
- throw new HTTPException100(503, {
110592
+ throw new HTTPException101(503, {
110036
110593
  message: "Object storage is not configured"
110037
110594
  });
110038
110595
  const parsedVersion = parseVersionId(context.req.query("versionId"));
@@ -110047,7 +110604,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
110047
110604
  try {
110048
110605
  content = await readWorkspaceArtifactContent(deps.objectStorage, ref);
110049
110606
  } catch (error) {
110050
- throw new HTTPException100(503, {
110607
+ throw new HTTPException101(503, {
110051
110608
  message: error instanceof Error ? error.message : "Artifact content is unavailable",
110052
110609
  cause: error
110053
110610
  });
@@ -110183,7 +110740,7 @@ import {
110183
110740
  editableArtifactId as editableArtifactId3,
110184
110741
  editableArtifactScope as editableArtifactScope2,
110185
110742
  fileOwnerContextForAccess as fileOwnerContextForAccess5,
110186
- grantHasAgentAttemptAuthority as grantHasAgentAttemptAuthority3,
110743
+ grantHasAgentAttemptAuthority as grantHasAgentAttemptAuthority4,
110187
110744
  hasPermission as hasPermission27,
110188
110745
  requireAccessGrantAuthorization as requireAccessGrantAuthorization36,
110189
110746
  requireLiveAgentAttemptAuthorization as requireLiveAgentAttemptAuthorization5,
@@ -110196,27 +110753,27 @@ import {
110196
110753
  getGeneratedImageArtifact as getGeneratedImageArtifact2,
110197
110754
  getSessionAuthorityProjection as getSessionAuthorityProjection3,
110198
110755
  listArtifactCatalogCandidates,
110199
- nestedPostgresSqlState as nestedPostgresSqlState14,
110756
+ nestedPostgresSqlState as nestedPostgresSqlState15,
110200
110757
  updateArtifactPin,
110201
110758
  withSessionRlsActorContext as withSessionRlsActorContext9
110202
110759
  } from "@opengeni/db";
110203
- import { HTTPException as HTTPException102 } from "hono/http-exception";
110204
- import { z as z53 } from "zod";
110760
+ import { HTTPException as HTTPException103 } from "hono/http-exception";
110761
+ import { z as z54 } from "zod";
110205
110762
 
110206
110763
  // src/artifact-catalog-cursor.ts
110207
110764
  import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, createHash as createHash29, randomBytes as randomBytes12 } from "crypto";
110208
110765
  import { ArtifactCatalogKind } from "@opengeni/contracts";
110209
- import { HTTPException as HTTPException101 } from "hono/http-exception";
110210
- import { z as z52 } from "zod";
110211
- var Cursor = z52.object({
110212
- version: z52.literal(1),
110213
- snapshotAt: z52.string().datetime(),
110214
- expiresAt: z52.number().int().positive(),
110215
- after: z52.object({
110216
- key: z52.string().max(4096),
110766
+ import { HTTPException as HTTPException102 } from "hono/http-exception";
110767
+ import { z as z53 } from "zod";
110768
+ var Cursor = z53.object({
110769
+ version: z53.literal(1),
110770
+ snapshotAt: z53.string().datetime(),
110771
+ expiresAt: z53.number().int().positive(),
110772
+ after: z53.object({
110773
+ key: z53.string().max(4096),
110217
110774
  kind: ArtifactCatalogKind,
110218
- id: z52.string().min(1).max(128),
110219
- pinned: z52.boolean().optional()
110775
+ id: z53.string().min(1).max(128),
110776
+ pinned: z53.boolean().optional()
110220
110777
  }).strict()
110221
110778
  }).strict();
110222
110779
  function artifactCatalogCursorCodec(secret, binding) {
@@ -110250,7 +110807,7 @@ function artifactCatalogCursorCodec(secret, binding) {
110250
110807
  throw new Error();
110251
110808
  return parsed;
110252
110809
  } catch {
110253
- throw new HTTPException101(422, { message: "Invalid or expired artifact catalog cursor" });
110810
+ throw new HTTPException102(422, { message: "Invalid or expired artifact catalog cursor" });
110254
110811
  }
110255
110812
  }
110256
110813
  };
@@ -110278,22 +110835,22 @@ function registerArtifactCatalogRoutes(app, deps) {
110278
110835
  );
110279
110836
  const grant = authorization.grant;
110280
110837
  if (!hasPermission27(grant.permissions, "artifacts:publish"))
110281
- throw new HTTPException102(403, { message: "Artifact publish access required" });
110838
+ throw new HTTPException103(403, { message: "Artifact publish access required" });
110282
110839
  const kind = ArtifactCatalogKind2.safeParse(context.req.param("kind"));
110283
110840
  const body3 = UpdateArtifactPinRequest.safeParse(await context.req.json().catch(() => null));
110284
110841
  const requestedId = context.req.param("artifactId");
110285
- if (!kind.success || !body3.success || !(editableKinds.has(kind.data) ? /^[0-9a-f]{32}$/.test(requestedId) : z53.string().uuid().safeParse(requestedId).success))
110286
- throw new HTTPException102(422, { message: "Invalid artifact pin request" });
110842
+ if (!kind.success || !body3.success || !(editableKinds.has(kind.data) ? /^[0-9a-f]{32}$/.test(requestedId) : z54.string().uuid().safeParse(requestedId).success))
110843
+ throw new HTTPException103(422, { message: "Invalid artifact pin request" });
110287
110844
  const artifactId2 = editableKinds.has(kind.data) ? requestedId : requestedId.toLowerCase();
110288
110845
  const fileKind = kind.data === "file" || kind.data === "image";
110289
110846
  if (!hasPermission27(grant.permissions, fileKind ? "files:read" : "artifacts:read"))
110290
- throw new HTTPException102(403, { message: "Artifact read access required" });
110847
+ throw new HTTPException103(403, { message: "Artifact read access required" });
110291
110848
  const actor = fileKind ? await fileOwnerContextForAccess5(deps, authorization, "files:read") : { subjectId: grant.subjectId, privateFileOwnerSubjectId: null };
110292
110849
  context.header("cache-control", "private, no-store");
110293
110850
  return withSessionRlsActorContext9(actor, async () => {
110294
110851
  if (editableKinds.has(kind.data)) {
110295
110852
  if (!deps.editableArtifacts)
110296
- throw new HTTPException102(503, { message: "Editable artifacts are unavailable" });
110853
+ throw new HTTPException103(503, { message: "Editable artifacts are unavailable" });
110297
110854
  try {
110298
110855
  const scope = editableArtifactScope2({ accountId: grant.accountId, workspaceId });
110299
110856
  const artifact = await deps.editableArtifacts.readArtifact({
@@ -110302,10 +110859,10 @@ function registerArtifactCatalogRoutes(app, deps) {
110302
110859
  artifactId: editableArtifactId3(artifactId2)
110303
110860
  });
110304
110861
  if (artifact.scope.accountId !== scope.accountId || artifact.scope.workspaceId !== scope.workspaceId || artifact.id !== artifactId2 || artifact.modality !== kind.data)
110305
- throw new HTTPException102(404, { message: "Artifact not found" });
110862
+ throw new HTTPException103(404, { message: "Artifact not found" });
110306
110863
  } catch (error) {
110307
110864
  if (isInvisibleListCandidate(error))
110308
- throw new HTTPException102(404, { message: "Artifact not found" });
110865
+ throw new HTTPException103(404, { message: "Artifact not found" });
110309
110866
  throw editableArtifactHttpError(error);
110310
110867
  }
110311
110868
  }
@@ -110318,7 +110875,7 @@ function registerArtifactCatalogRoutes(app, deps) {
110318
110875
  fileIds: [artifactId2]
110319
110876
  });
110320
110877
  if (!file || file.status !== "ready")
110321
- throw new HTTPException102(404, { message: "Artifact not found" });
110878
+ throw new HTTPException103(404, { message: "Artifact not found" });
110322
110879
  }
110323
110880
  try {
110324
110881
  await updateArtifactPin(
@@ -110331,8 +110888,8 @@ function registerArtifactCatalogRoutes(app, deps) {
110331
110888
  }
110332
110889
  );
110333
110890
  } catch (error) {
110334
- if (nestedPostgresSqlState14(error) === "42501")
110335
- throw new HTTPException102(404, { message: "Artifact not found" });
110891
+ if (nestedPostgresSqlState15(error) === "42501")
110892
+ throw new HTTPException103(404, { message: "Artifact not found" });
110336
110893
  throw error;
110337
110894
  }
110338
110895
  return context.json(
@@ -110346,23 +110903,23 @@ function registerArtifactCatalogRoutes(app, deps) {
110346
110903
  const grant = authorization.grant;
110347
110904
  const parsed = ArtifactCatalogListQuery.safeParse(context.req.query());
110348
110905
  if (!parsed.success)
110349
- throw new HTTPException102(422, { message: "Invalid artifact catalog query" });
110906
+ throw new HTTPException103(422, { message: "Invalid artifact catalog query" });
110350
110907
  const query = parsed.data;
110351
110908
  const artifactsAllowed = hasPermission27(grant.permissions, "artifacts:read");
110352
110909
  const filesAllowed = hasPermission27(grant.permissions, "files:read");
110353
110910
  if (!artifactsAllowed && !filesAllowed)
110354
- throw new HTTPException102(403, { message: "Artifact catalog read access required" });
110911
+ throw new HTTPException103(403, { message: "Artifact catalog read access required" });
110355
110912
  let kinds = ArtifactCatalogKind2.options.filter(
110356
110913
  (kind) => (kind === "image" || kind === "file" ? filesAllowed : artifactsAllowed) && (!query.kind || kind === query.kind)
110357
110914
  );
110358
110915
  if (!deps.editableArtifacts) {
110359
110916
  if (query.kind && editableKinds.has(query.kind) && artifactsAllowed)
110360
- throw new HTTPException102(503, { message: "Editable artifacts are unavailable" });
110917
+ throw new HTTPException103(503, { message: "Editable artifacts are unavailable" });
110361
110918
  kinds = kinds.filter((kind) => !editableKinds.has(kind));
110362
110919
  }
110363
110920
  const includesFiles = kinds.includes("file") || kinds.includes("image");
110364
110921
  const actor = includesFiles ? await fileOwnerContextForAccess5(deps, authorization, "files:read") : { subjectId: grant.subjectId, privateFileOwnerSubjectId: null };
110365
- if (grantHasAgentAttemptAuthority3(grant) && actor.initiatingHumanSubjectId === void 0) {
110922
+ if (grantHasAgentAttemptAuthority4(grant) && actor.initiatingHumanSubjectId === void 0) {
110366
110923
  const callerSessionId = grant.metadata?.["sessionId"];
110367
110924
  try {
110368
110925
  if (typeof callerSessionId !== "string")
@@ -110371,7 +110928,7 @@ function registerArtifactCatalogRoutes(app, deps) {
110371
110928
  actor.initiatingHumanSubjectId = attempt.initiatingHumanSubjectId;
110372
110929
  } catch (error) {
110373
110930
  if (error instanceof SessionAuthorizationDeniedError14)
110374
- throw new HTTPException102(404, { message: "Session not found", cause: error });
110931
+ throw new HTTPException103(404, { message: "Session not found", cause: error });
110375
110932
  throw error;
110376
110933
  }
110377
110934
  }
@@ -110397,17 +110954,17 @@ function registerArtifactCatalogRoutes(app, deps) {
110397
110954
  return false;
110398
110955
  }
110399
110956
  if (error instanceof SessionAuthorizationUnavailableError12)
110400
- throw new HTTPException102(503, { message: "Session authorization is unavailable" });
110957
+ throw new HTTPException103(503, { message: "Session authorization is unavailable" });
110401
110958
  throw error;
110402
110959
  }
110403
110960
  };
110404
110961
  context.header("cache-control", "private, no-store");
110405
110962
  return withSessionRlsActorContext9(actor, async () => {
110406
110963
  if (query.sourceSessionId && !await canReadSession(query.sourceSessionId))
110407
- throw new HTTPException102(404, { message: "Session not found" });
110964
+ throw new HTTPException103(404, { message: "Session not found" });
110408
110965
  const secret = resolveFirstPartyDelegationSecret6(deps.settings);
110409
110966
  if (!secret)
110410
- throw new HTTPException102(503, { message: "Artifact catalog cursor signing is unavailable" });
110967
+ throw new HTTPException103(503, { message: "Artifact catalog cursor signing is unavailable" });
110411
110968
  const filters = { ...query, cursor: void 0 };
110412
110969
  const codec = artifactCatalogCursorCodec(
110413
110970
  secret,
@@ -110536,18 +111093,18 @@ import {
110536
111093
  workspaceInsightsPhaseMetricObserver,
110537
111094
  normalizeWorkspaceInsightsFilter,
110538
111095
  normalizeWorkspaceInsightsSessionScope,
110539
- requireAccessGrant as requireAccessGrant46,
111096
+ requireAccessGrant as requireAccessGrant47,
110540
111097
  WorkspaceInsightsFilterValidationError
110541
111098
  } from "@opengeni/core";
110542
111099
  import { currentSessionRlsActorIdentityKey } from "@opengeni/db";
110543
111100
  import { workspaceInsightsMetricObserver } from "@opengeni/observability";
110544
- import { HTTPException as HTTPException103 } from "hono/http-exception";
111101
+ import { HTTPException as HTTPException104 } from "hono/http-exception";
110545
111102
  function normalizeWorkspaceInsightsQueryFilter(value, field) {
110546
111103
  try {
110547
111104
  return field === "rootSessionId" || field === "sessionId" ? normalizeWorkspaceInsightsSessionScope(value, field) : normalizeWorkspaceInsightsFilter(value, field);
110548
111105
  } catch (error) {
110549
111106
  if (error instanceof WorkspaceInsightsFilterValidationError) {
110550
- throw new HTTPException103(400, { message: error.message });
111107
+ throw new HTTPException104(400, { message: error.message });
110551
111108
  }
110552
111109
  throw error;
110553
111110
  }
@@ -110599,11 +111156,11 @@ function registerInsightsRoutes(app, deps) {
110599
111156
  await measureInsightsPhase(
110600
111157
  observePhase,
110601
111158
  "auth",
110602
- () => requireAccessGrant46(c, deps, workspaceId, "workspace:admin")
111159
+ () => requireAccessGrant47(c, deps, workspaceId, "workspace:admin")
110603
111160
  );
110604
111161
  const rangeParsed = InsightsRange.safeParse(rangeRaw);
110605
111162
  if (!rangeParsed.success) {
110606
- throw new HTTPException103(400, {
111163
+ throw new HTTPException104(400, {
110607
111164
  message: "range must be one of today|week|month|ytd"
110608
111165
  });
110609
111166
  }
@@ -110669,7 +111226,7 @@ import {
110669
111226
  hasPermission as hasPermission28,
110670
111227
  listInsightsCalls,
110671
111228
  requireAccessContext as requireAccessContext11,
110672
- requireAccessGrant as requireAccessGrant47
111229
+ requireAccessGrant as requireAccessGrant48
110673
111230
  } from "@opengeni/core";
110674
111231
  import {
110675
111232
  currentSessionRlsActorIdentityKey as currentSessionRlsActorIdentityKey2,
@@ -110677,11 +111234,11 @@ import {
110677
111234
  readInsightsResponseCacheFence
110678
111235
  } from "@opengeni/db";
110679
111236
  import { createHash as createHash30 } from "crypto";
110680
- import { HTTPException as HTTPException105 } from "hono/http-exception";
111237
+ import { HTTPException as HTTPException106 } from "hono/http-exception";
110681
111238
 
110682
111239
  // src/routes/insights-response-cache.ts
110683
111240
  import { createHmac as createHmac13, randomBytes as randomBytes13 } from "crypto";
110684
- import { HTTPException as HTTPException104 } from "hono/http-exception";
111241
+ import { HTTPException as HTTPException105 } from "hono/http-exception";
110685
111242
  var INSIGHTS_RESPONSE_TTL_MS = 6e4;
110686
111243
  var MAX_ENTRIES = 128;
110687
111244
  var MAX_BYTES = 8 * 1024 * 1024;
@@ -110736,7 +111293,7 @@ async function insightsWithFriendlyTimeout(read) {
110736
111293
  for (let depth = 0; depth < 4 && current && typeof current === "object"; depth++) {
110737
111294
  const candidate = current;
110738
111295
  if (candidate.code === "57014" || typeof candidate.message === "string" && candidate.message.toLowerCase().includes("statement timeout")) {
110739
- throw new HTTPException104(408, {
111296
+ throw new HTTPException105(408, {
110740
111297
  message: "This range has too much data right now. Try a shorter range."
110741
111298
  });
110742
111299
  }
@@ -110837,7 +111394,7 @@ async function callsWithValidation(read) {
110837
111394
  return await read();
110838
111395
  } catch (error) {
110839
111396
  if (error instanceof InvalidInsightsCallsCursorError)
110840
- throw new HTTPException105(400, { message: "invalid Insights calls cursor" });
111397
+ throw new HTTPException106(400, { message: "invalid Insights calls cursor" });
110841
111398
  throw error;
110842
111399
  }
110843
111400
  }
@@ -110873,12 +111430,12 @@ function registerInsightsUsageRoutes(app, deps) {
110873
111430
  }
110874
111431
  app.get("/v1/workspaces/:workspaceId/insights/usage", async (c) => {
110875
111432
  const workspaceId = c.req.param("workspaceId");
110876
- const grant = await requireAccessGrant47(c, deps, workspaceId, "workspace:admin");
111433
+ const grant = await requireAccessGrant48(c, deps, workspaceId, "workspace:admin");
110877
111434
  const query = insightsQueryParameters(c.req.queries());
110878
111435
  if (!insightsRawQuerySupported(query, false, false))
110879
- throw new HTTPException105(400, { message: "unsupported interim Insights query" });
111436
+ throw new HTTPException106(400, { message: "unsupported interim Insights query" });
110880
111437
  const parsed = WorkspaceInsightsUsageQuery.safeParse(query);
110881
- if (!parsed.success) throw new HTTPException105(400, { message: "invalid Insights usage query" });
111438
+ if (!parsed.success) throw new HTTPException106(400, { message: "invalid Insights usage query" });
110882
111439
  const scope = {
110883
111440
  accountId: grant.accountId,
110884
111441
  workspaceId,
@@ -110899,12 +111456,12 @@ function registerInsightsUsageRoutes(app, deps) {
110899
111456
  });
110900
111457
  app.get("/v1/workspaces/:workspaceId/insights/calls", async (c) => {
110901
111458
  const workspaceId = c.req.param("workspaceId");
110902
- const grant = await requireAccessGrant47(c, deps, workspaceId, "workspace:admin");
111459
+ const grant = await requireAccessGrant48(c, deps, workspaceId, "workspace:admin");
110903
111460
  const query = insightsQueryParameters(c.req.queries());
110904
111461
  if (!insightsRawQuerySupported(query, false, true))
110905
- throw new HTTPException105(400, { message: "unsupported interim Insights query" });
111462
+ throw new HTTPException106(400, { message: "unsupported interim Insights query" });
110906
111463
  const parsed = WorkspaceInsightsCallsQuery.safeParse(query);
110907
- if (!parsed.success) throw new HTTPException105(400, { message: "invalid Insights calls query" });
111464
+ if (!parsed.success) throw new HTTPException106(400, { message: "invalid Insights calls query" });
110908
111465
  const scope = {
110909
111466
  accountId: grant.accountId,
110910
111467
  workspaceId,
@@ -110932,9 +111489,9 @@ function registerInsightsUsageRoutes(app, deps) {
110932
111489
  const accountId = requireSelectedAccount(context, c.req.param("accountId"), "billing:read");
110933
111490
  const query = insightsQueryParameters(c.req.queries());
110934
111491
  if (!insightsRawQuerySupported(query, true, false))
110935
- throw new HTTPException105(400, { message: "unsupported interim Insights query" });
111492
+ throw new HTTPException106(400, { message: "unsupported interim Insights query" });
110936
111493
  const parsed = OrganizationInsightsUsageQuery.safeParse(query);
110937
- if (!parsed.success) throw new HTTPException105(400, { message: "invalid Insights usage query" });
111494
+ if (!parsed.success) throw new HTTPException106(400, { message: "invalid Insights usage query" });
110938
111495
  const scope = organizationInsightsScope(context, accountId);
110939
111496
  const response = await withBillingUsageActor(
110940
111497
  deps,
@@ -110959,9 +111516,9 @@ function registerInsightsUsageRoutes(app, deps) {
110959
111516
  const accountId = requireSelectedAccount(context, c.req.param("accountId"), "billing:read");
110960
111517
  const query = insightsQueryParameters(c.req.queries());
110961
111518
  if (!insightsRawQuerySupported(query, true, true))
110962
- throw new HTTPException105(400, { message: "unsupported interim Insights query" });
111519
+ throw new HTTPException106(400, { message: "unsupported interim Insights query" });
110963
111520
  const parsed = OrganizationInsightsCallsQuery.safeParse(query);
110964
- if (!parsed.success) throw new HTTPException105(400, { message: "invalid Insights calls query" });
111521
+ if (!parsed.success) throw new HTTPException106(400, { message: "invalid Insights calls query" });
110965
111522
  const scope = organizationInsightsScope(context, accountId);
110966
111523
  const response = await withBillingUsageActor(
110967
111524
  deps,
@@ -110992,7 +111549,7 @@ import {
110992
111549
  resolveWorkspaceVoiceInputEnabled as resolveWorkspaceVoiceInputEnabled2,
110993
111550
  WorkspaceVoiceInputSettings as WorkspaceVoiceInputSettings2
110994
111551
  } from "@opengeni/contracts";
110995
- import { requireAccessGrant as requireAccessGrant49, TranscriptionServiceError as TranscriptionServiceError2 } from "@opengeni/core";
111552
+ import { requireAccessGrant as requireAccessGrant50, TranscriptionServiceError as TranscriptionServiceError2 } from "@opengeni/core";
110996
111553
  import { getWorkspace as getWorkspace10 } from "@opengeni/db";
110997
111554
 
110998
111555
  // src/routes/transcription-recordings.ts
@@ -111008,7 +111565,7 @@ import {
111008
111565
  import {
111009
111566
  isAcceptedMimeType,
111010
111567
  normalizeMimeType,
111011
- requireAccessGrant as requireAccessGrant48,
111568
+ requireAccessGrant as requireAccessGrant49,
111012
111569
  TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS,
111013
111570
  TranscriptionServiceError
111014
111571
  } from "@opengeni/core";
@@ -111700,7 +112257,7 @@ async function requireRecordingAuthority(c, deps, requirePolicy) {
111700
112257
  if (!workspaceId) {
111701
112258
  throw new TranscriptionRecordingNotFoundError("Workspace not found");
111702
112259
  }
111703
- const grant = await requireAccessGrant48(c, deps, workspaceId, "sessions:create");
112260
+ const grant = await requireAccessGrant49(c, deps, workspaceId, "sessions:create");
111704
112261
  const workspace = await getWorkspace9(deps.db, workspaceId);
111705
112262
  if (requirePolicy) {
111706
112263
  if (!workspace) throw new TranscriptionRecordingNotFoundError("Workspace not found");
@@ -111913,7 +112470,7 @@ function registerTranscriptionRoutes(app, deps) {
111913
112470
  registerResumableTranscriptionRoutes(app, deps);
111914
112471
  app.post("/v1/workspaces/:workspaceId/transcriptions", async (c) => {
111915
112472
  const workspaceId = c.req.param("workspaceId");
111916
- const grant = await requireAccessGrant49(c, deps, workspaceId, "sessions:create");
112473
+ const grant = await requireAccessGrant50(c, deps, workspaceId, "sessions:create");
111917
112474
  const workspace = await getWorkspace10(deps.db, workspaceId);
111918
112475
  if (!workspace) return c.json({ code: "not_found" }, 404);
111919
112476
  if (resolveWorkspaceVoiceInputEnabled2(workspace.settings) === false) {
@@ -112052,7 +112609,7 @@ function isAbort3(error) {
112052
112609
 
112053
112610
  // src/routes/session-artifact-associations.ts
112054
112611
  import {
112055
- requireAccessGrant as requireAccessGrant50,
112612
+ requireAccessGrant as requireAccessGrant51,
112056
112613
  requirePermission as requirePermission12,
112057
112614
  requireSessionAuthorization as requireSessionAuthorization15,
112058
112615
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError15,
@@ -112064,23 +112621,23 @@ import {
112064
112621
  transactionallyAuthorizeEditableArtifactActor,
112065
112622
  withRlsContext as withRlsContext3
112066
112623
  } from "@opengeni/db";
112067
- import { HTTPException as HTTPException106 } from "hono/http-exception";
112068
- import { z as z54 } from "zod";
112069
- var SessionId2 = z54.string().uuid();
112070
- var EditableArtifactId = z54.string().regex(/^(?!0{32}$)[0-9a-f]{32}$/u);
112624
+ import { HTTPException as HTTPException107 } from "hono/http-exception";
112625
+ import { z as z55 } from "zod";
112626
+ var SessionId2 = z55.string().uuid();
112627
+ var EditableArtifactId = z55.string().regex(/^(?!0{32}$)[0-9a-f]{32}$/u);
112071
112628
  function registerSessionArtifactAssociationRoutes(app, deps) {
112072
112629
  app.get(
112073
112630
  "/v1/workspaces/:workspaceId/sessions/:sessionId/artifact-associations/:artifactId",
112074
112631
  async (context) => {
112075
112632
  const workspaceId = context.req.param("workspaceId");
112076
- const grant = await requireAccessGrant50(context, deps, workspaceId, "artifacts:read");
112633
+ const grant = await requireAccessGrant51(context, deps, workspaceId, "artifacts:read");
112077
112634
  requirePermission12(grant, "sessions:read");
112078
112635
  const sessionId = context.req.param("sessionId");
112079
112636
  const artifactId2 = context.req.param("artifactId");
112080
112637
  const kind = EditableArtifactId.safeParse(artifactId2).success ? "editable" : SessionId2.safeParse(artifactId2).success ? "site" : null;
112081
112638
  const requestedKind = context.req.query("kind");
112082
112639
  if (!SessionId2.safeParse(sessionId).success || !kind || requestedKind !== void 0 && requestedKind !== kind) {
112083
- throw new HTTPException106(404, { message: "Artifact association not found" });
112640
+ throw new HTTPException107(404, { message: "Artifact association not found" });
112084
112641
  }
112085
112642
  try {
112086
112643
  return await withAccessGrantSessionRlsContext(deps, grant, async () => {
@@ -112091,7 +112648,7 @@ function registerSessionArtifactAssociationRoutes(app, deps) {
112091
112648
  });
112092
112649
  const scope = { accountId: grant.accountId, workspaceId };
112093
112650
  const linked = kind === "editable" ? await hasEditableArtifactSessionLink2(deps.db, scope, sessionId, artifactId2) : await hasWorkspaceArtifactSessionLink(deps.db, workspaceId, sessionId, artifactId2);
112094
- if (!linked) throw new HTTPException106(404, { message: "Artifact association not found" });
112651
+ if (!linked) throw new HTTPException107(404, { message: "Artifact association not found" });
112095
112652
  if (kind === "editable") {
112096
112653
  const decision = await withRlsContext3(
112097
112654
  deps.db,
@@ -112104,7 +112661,7 @@ function registerSessionArtifactAssociationRoutes(app, deps) {
112104
112661
  })
112105
112662
  );
112106
112663
  if (!decision.allowed) {
112107
- throw new HTTPException106(404, { message: "Artifact association not found" });
112664
+ throw new HTTPException107(404, { message: "Artifact association not found" });
112108
112665
  }
112109
112666
  }
112110
112667
  context.header("cache-control", "private, no-store");
@@ -112112,10 +112669,10 @@ function registerSessionArtifactAssociationRoutes(app, deps) {
112112
112669
  });
112113
112670
  } catch (error) {
112114
112671
  if (error instanceof SessionAuthorizationDeniedError15) {
112115
- throw new HTTPException106(404, { message: "Artifact association not found" });
112672
+ throw new HTTPException107(404, { message: "Artifact association not found" });
112116
112673
  }
112117
112674
  if (error instanceof SessionAuthorizationUnavailableError13) {
112118
- throw new HTTPException106(503, { message: "Session authorization is unavailable" });
112675
+ throw new HTTPException107(503, { message: "Session authorization is unavailable" });
112119
112676
  }
112120
112677
  throw error;
112121
112678
  }
@@ -112140,7 +112697,7 @@ import {
112140
112697
  workspaceXaiSubscriptionActive as workspaceXaiSubscriptionActive2
112141
112698
  } from "@opengeni/db";
112142
112699
  import {
112143
- requireAccessGrant as requireAccessGrant51,
112700
+ requireAccessGrant as requireAccessGrant52,
112144
112701
  requireAccessGrantAuthorization as requireAccessGrantAuthorization37,
112145
112702
  fileOwnerContextForAccess as fileOwnerContextForAccess6,
112146
112703
  requireWorkspaceSettingsGrant as requireWorkspaceSettingsGrant6,
@@ -112148,11 +112705,11 @@ import {
112148
112705
  videoGenerationModelSupportsFundingSource,
112149
112706
  videoGenerationCapabilitiesForPolicy
112150
112707
  } from "@opengeni/core";
112151
- import { HTTPException as HTTPException107 } from "hono/http-exception";
112708
+ import { HTTPException as HTTPException108 } from "hono/http-exception";
112152
112709
  function registerVideoGenerationRoutes(app, deps) {
112153
112710
  app.get("/v1/workspaces/:workspaceId/video-generation", async (c) => {
112154
112711
  const workspaceId = c.req.param("workspaceId");
112155
- const grant = await requireAccessGrant51(c, deps, workspaceId, "workspace:read");
112712
+ const grant = await requireAccessGrant52(c, deps, workspaceId, "workspace:read");
112156
112713
  const [policy, connection, supergrokConfigured] = await Promise.all([
112157
112714
  getWorkspaceVideoGenerationPolicy(deps.db, workspaceId),
112158
112715
  getWorkspaceVercelAiGatewayConnectionMetadata(deps.db, workspaceId),
@@ -112195,14 +112752,14 @@ function registerVideoGenerationRoutes(app, deps) {
112195
112752
  (option) => option.source === payload.fundingSource
112196
112753
  );
112197
112754
  if (payload.enabledModelIds.length > 0 && !selectedFunding?.available) {
112198
- throw new HTTPException107(422, {
112755
+ throw new HTTPException108(422, {
112199
112756
  message: selectedFunding?.unavailableReason ?? "Video generation funding is unavailable"
112200
112757
  });
112201
112758
  }
112202
112759
  if (payload.enabledModelIds.some(
112203
112760
  (modelId) => !videoGenerationModelSupportsFundingSource(modelId, payload.fundingSource)
112204
112761
  )) {
112205
- throw new HTTPException107(422, {
112762
+ throw new HTTPException108(422, {
112206
112763
  message: "The selected video model is unavailable for this funding source"
112207
112764
  });
112208
112765
  }
@@ -112216,10 +112773,10 @@ function registerVideoGenerationRoutes(app, deps) {
112216
112773
  return c.json(VideoGenerationPolicy.parse(policy));
112217
112774
  } catch (error) {
112218
112775
  if (error instanceof VideoGenerationConflictError) {
112219
- throw new HTTPException107(409, { message: error.message });
112776
+ throw new HTTPException108(409, { message: error.message });
112220
112777
  }
112221
112778
  if (error instanceof Error && error.message.startsWith("Unknown video generation model:")) {
112222
- throw new HTTPException107(422, { message: error.message });
112779
+ throw new HTTPException108(422, { message: error.message });
112223
112780
  }
112224
112781
  throw error;
112225
112782
  }
@@ -112233,7 +112790,7 @@ function registerVideoGenerationRoutes(app, deps) {
112233
112790
  () => getVideoGenerationOperationSummary(deps.db, workspaceId, c.req.param("operationId"))
112234
112791
  );
112235
112792
  if (!summary)
112236
- throw new HTTPException107(404, {
112793
+ throw new HTTPException108(404, {
112237
112794
  message: "video generation operation not found"
112238
112795
  });
112239
112796
  return c.json(VideoGenerationOperationSummary.parse(summary));
@@ -112290,8 +112847,8 @@ import {
112290
112847
  CanonicalHumanIdentityMutationInFlightError,
112291
112848
  getCanonicalHumanIdentityProjection as getCanonicalHumanIdentityProjection3
112292
112849
  } from "@opengeni/db/canonical-human-identities";
112293
- import { HTTPException as HTTPException110 } from "hono/http-exception";
112294
- import { z as z57 } from "zod";
112850
+ import { HTTPException as HTTPException111 } from "hono/http-exception";
112851
+ import { z as z58 } from "zod";
112295
112852
 
112296
112853
  // src/routes/managed-sign-in-methods.ts
112297
112854
  import {
@@ -112314,11 +112871,15 @@ import {
112314
112871
  import { getManagedAuthSessionSetSnapshot as getManagedAuthSessionSetSnapshot2 } from "@opengeni/db/managed-auth-session-sets";
112315
112872
  import { getCanonicalHumanIdentityProjection as getCanonicalHumanIdentityProjection2 } from "@opengeni/db/canonical-human-identities";
112316
112873
  import { getCookie as getCookie2 } from "hono/cookie";
112317
- import { HTTPException as HTTPException109 } from "hono/http-exception";
112318
- import { z as z56 } from "zod";
112874
+ import { HTTPException as HTTPException110 } from "hono/http-exception";
112875
+ import { z as z57 } from "zod";
112319
112876
 
112320
112877
  // src/routes/managed-auth-session-sets.ts
112321
- import { assertManagedUserAdmission as assertManagedUserAdmission2, getManagedSession as getManagedSession3 } from "@opengeni/core";
112878
+ import {
112879
+ assertManagedUserAdmission as assertManagedUserAdmission2,
112880
+ getManagedSession as getManagedSession3,
112881
+ withManagedAuthSessionLookup as withManagedAuthSessionLookup2
112882
+ } from "@opengeni/core";
112322
112883
  import { randomUUID as randomUUID16 } from "crypto";
112323
112884
  import {
112324
112885
  BeginManagedAuthLoginTransactionRequest,
@@ -112380,13 +112941,13 @@ import {
112380
112941
  } from "@opengeni/db/managed-auth-session-sets";
112381
112942
  import { ensureManagedAccessForUser as ensureManagedAccessForUser2, getSession as getSession10 } from "@opengeni/db";
112382
112943
  import { deleteCookie as deleteCookie3, getCookie, setCookie as setCookie3 } from "hono/cookie";
112383
- import { HTTPException as HTTPException108 } from "hono/http-exception";
112384
- import { z as z55 } from "zod";
112385
- var ManagedAuthSocialStartReceipt = z55.object({
112386
- version: z55.literal(1),
112387
- requestDigest: z55.string().regex(/^[0-9a-f]{64}$/u),
112388
- url: z55.string().url().max(4096),
112389
- stateCookie: z55.string().min(1).max(4096)
112944
+ import { HTTPException as HTTPException109 } from "hono/http-exception";
112945
+ import { z as z56 } from "zod";
112946
+ var ManagedAuthSocialStartReceipt = z56.object({
112947
+ version: z56.literal(1),
112948
+ requestDigest: z56.string().regex(/^[0-9a-f]{64}$/u),
112949
+ url: z56.string().url().max(4096),
112950
+ stateCookie: z56.string().min(1).max(4096)
112390
112951
  }).strict();
112391
112952
  function registerManagedAuthSessionSetRoutes(app, deps) {
112392
112953
  const noStore = async (context, next) => {
@@ -112407,7 +112968,7 @@ function registerManagedAuthSessionSetRoutes(app, deps) {
112407
112968
  app.use("/v1/auth/session-set/*", noStore);
112408
112969
  app.get("/v1/auth/get-session", async (context) => {
112409
112970
  if (deps.settings.managedAuthSessionSetMode === "legacy") {
112410
- if (!deps.managedAuth) throw new HTTPException108(404);
112971
+ if (!deps.managedAuth) throw new HTTPException109(404);
112411
112972
  if (deps.settings.allowedUserEmails !== void 0) {
112412
112973
  const session = await getManagedSession3(context, deps.managedAuth, { db: deps.db });
112413
112974
  context.header("cache-control", "no-store");
@@ -112491,10 +113052,12 @@ function registerManagedAuthSessionSetRoutes(app, deps) {
112491
113052
  app.post("/v1/auth/session-set/bootstrap", async (context) => {
112492
113053
  const body3 = await bodyAs(context, BootstrapManagedAuthSessionSetRequest);
112493
113054
  const { authority, actorEpoch } = await requireMutation(context, deps, body3.expectedGeneration);
112494
- const ambient = await requireAvailable(deps).managedAuth.api.getSession({
112495
- headers: context.req.raw.headers,
112496
- returnHeaders: true
112497
- });
113055
+ const ambient = await withManagedAuthSessionLookup2(
113056
+ () => requireAvailable(deps).managedAuth.api.getSession({
113057
+ headers: context.req.raw.headers,
113058
+ returnHeaders: true
113059
+ })
113060
+ );
112498
113061
  assertManagedUserAdmission2(deps.managedAuth, ambient.response?.user);
112499
113062
  const authSessionId = ambient.response?.session?.id;
112500
113063
  if (typeof authSessionId !== "string") {
@@ -113253,7 +113816,7 @@ function appendCookies(context, cookies) {
113253
113816
  for (const cookie of cookies) context.header("set-cookie", cookie, { append: true });
113254
113817
  }
113255
113818
  function throwHttp(error, context) {
113256
- if (error instanceof HTTPException108) throw error;
113819
+ if (error instanceof HTTPException109) throw error;
113257
113820
  if (error instanceof ManagedAuthSessionSetGenerationConflictError) {
113258
113821
  throw managedAuthApiError(409, "generation_conflict", { cause: error, retryable: true });
113259
113822
  }
@@ -113322,23 +113885,23 @@ function setCookieHeaders(headers) {
113322
113885
  }
113323
113886
 
113324
113887
  // src/routes/managed-sign-in-methods.ts
113325
- var ConnectState = z56.object({
113326
- opengeniSignInMethod: z56.object({
113327
- intentId: z56.string().uuid(),
113328
- provider: z56.enum(["google", "github"])
113888
+ var ConnectState = z57.object({
113889
+ opengeniSignInMethod: z57.object({
113890
+ intentId: z57.string().uuid(),
113891
+ provider: z57.enum(["google", "github"])
113329
113892
  }),
113330
- link: z56.object({ userId: z56.string().min(1), email: z56.string().email() }),
113331
- callbackURL: z56.string().url(),
113332
- errorURL: z56.string().url()
113893
+ link: z57.object({ userId: z57.string().min(1), email: z57.string().email() }),
113894
+ callbackURL: z57.string().url(),
113895
+ errorURL: z57.string().url()
113333
113896
  });
113334
- var ConnectIntent = z56.object({
113335
- authUserId: z56.string().min(1),
113336
- authSessionId: z56.string().min(1),
113337
- expectedIdentityId: z56.string().uuid(),
113338
- actorFence: z56.object({
113339
- authorityHash: z56.string().regex(/^[0-9a-f]{64}$/),
113340
- actorEpoch: z56.string().regex(/^[1-9][0-9]*$/),
113341
- requestId: z56.string().uuid()
113897
+ var ConnectIntent = z57.object({
113898
+ authUserId: z57.string().min(1),
113899
+ authSessionId: z57.string().min(1),
113900
+ expectedIdentityId: z57.string().uuid(),
113901
+ actorFence: z57.object({
113902
+ authorityHash: z57.string().regex(/^[0-9a-f]{64}$/),
113903
+ actorEpoch: z57.string().regex(/^[1-9][0-9]*$/),
113904
+ requestId: z57.string().uuid()
113342
113905
  }).optional()
113343
113906
  });
113344
113907
  async function rows(deps, query) {
@@ -113356,7 +113919,7 @@ async function identity(context, deps) {
113356
113919
  async function admitManagedSignInMutation(context, deps) {
113357
113920
  const origin = deps.settings.publicBaseUrl ? new URL(deps.settings.publicBaseUrl).origin : null;
113358
113921
  if (!origin || context.req.header("origin") !== origin || context.req.header("sec-fetch-site") !== "same-origin" || context.req.header("content-type")?.split(";", 1)[0] !== "application/json") {
113359
- throw new HTTPException109(403, { message: "Same-origin browser mutation required" });
113922
+ throw new HTTPException110(403, { message: "Same-origin browser mutation required" });
113360
113923
  }
113361
113924
  const authority = getCookie2(context, MANAGED_AUTH_SESSION_SET_COOKIE2);
113362
113925
  if (deps.settings.managedAuthSessionSetMode !== "legacy" && authority) {
@@ -113365,7 +113928,7 @@ async function admitManagedSignInMutation(context, deps) {
113365
113928
  mode: deps.settings.managedAuthSessionSetMode,
113366
113929
  readOnly: true
113367
113930
  });
113368
- if (!snapshot) throw new HTTPException109(401);
113931
+ if (!snapshot) throw new HTTPException110(401);
113369
113932
  try {
113370
113933
  requireManagedAuthMutationAdmission2({
113371
113934
  request: context.req.raw,
@@ -113375,7 +113938,7 @@ async function admitManagedSignInMutation(context, deps) {
113375
113938
  expectedGeneration: snapshot.projection.generation
113376
113939
  });
113377
113940
  } catch {
113378
- throw new HTTPException109(403, { message: "Browser session-set CSRF validation failed" });
113941
+ throw new HTTPException110(403, { message: "Browser session-set CSRF validation failed" });
113379
113942
  }
113380
113943
  }
113381
113944
  }
@@ -113413,7 +113976,7 @@ function registerManagedSignInMethodRoutes(app, deps) {
113413
113976
  select u.email, u.email_verified as "emailVerified", s.created_at > clock_timestamp()-interval '5 minutes' as fresh
113414
113977
  from auth_users u join auth_sessions s on s.user_id=u.id where u.id=${actor.authUserId} and s.id=${actor.authSessionId}`
113415
113978
  );
113416
- if (!user) throw new HTTPException109(401);
113979
+ if (!user) throw new HTTPException110(401);
113417
113980
  const accounts = await rows(
113418
113981
  deps,
113419
113982
  sql`
@@ -113447,11 +114010,11 @@ function registerManagedSignInMethodRoutes(app, deps) {
113447
114010
  const actor = await identity(context, deps);
113448
114011
  const actorFence = getManagedAuthRequestActorLeaseStamp(context.req.raw);
113449
114012
  if (deps.settings.managedAuthSessionSetMode !== "legacy" && !actorFence)
113450
- throw new HTTPException109(409, { message: "Managed actor fence required" });
114013
+ throw new HTTPException110(409, { message: "Managed actor fence required" });
113451
114014
  const schema = kind === "password" ? ManagedSignInPasswordMutation : ManagedSignInProviderMutation;
113452
114015
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
113453
114016
  if (!parsed.success)
113454
- throw new HTTPException109(422, { message: "Invalid sign-in method request" });
114017
+ throw new HTTPException110(422, { message: "Invalid sign-in method request" });
113455
114018
  const body3 = parsed.data;
113456
114019
  const currentIdentity = await getCanonicalHumanIdentityProjection2(deps.db, actor.authUserId);
113457
114020
  if (currentIdentity.activeIdentity.id !== body3.expectedIdentityId)
@@ -113466,7 +114029,7 @@ function registerManagedSignInMethodRoutes(app, deps) {
113466
114029
  deps,
113467
114030
  sql`select u.email,s.token from auth_users u join auth_sessions s on s.user_id=u.id where u.id=${actor.authUserId} and s.id=${actor.authSessionId}`
113468
114031
  );
113469
- if (!user) throw new HTTPException109(401);
114032
+ if (!user) throw new HTTPException110(401);
113470
114033
  const request = {
113471
114034
  kind,
113472
114035
  operationId: body3.operationId,
@@ -113519,7 +114082,7 @@ function registerManagedSignInMethodRoutes(app, deps) {
113519
114082
  if ("provider" in body3) {
113520
114083
  request.provider = body3.provider;
113521
114084
  if (kind === "connect" && !configured(deps, body3.provider))
113522
- throw new HTTPException109(409, { message: "Sign-in provider is not configured" });
114085
+ throw new HTTPException110(409, { message: "Sign-in provider is not configured" });
113523
114086
  } else {
113524
114087
  const [credential] = await rows(
113525
114088
  deps,
@@ -113600,18 +114163,18 @@ async function handleManagedSignInConnectCallback(context, deps, provider) {
113600
114163
  return null;
113601
114164
  const parsed = ConnectState.safeParse(rawState);
113602
114165
  if (!parsed.success)
113603
- throw new HTTPException109(403, { message: "Invalid sign-in method OAuth proof" });
114166
+ throw new HTTPException110(403, { message: "Invalid sign-in method OAuth proof" });
113604
114167
  const value = parsed.data, proof = value.opengeniSignInMethod;
113605
114168
  if (proof.provider !== provider || value.callbackURL !== new URL(
113606
114169
  "/settings/security?signInMethod=connected",
113607
114170
  deps.settings.publicBaseUrl
113608
114171
  ).toString() || value.errorURL !== new URL("/settings/security?signInMethod=error", deps.settings.publicBaseUrl).toString())
113609
- throw new HTTPException109(403);
114172
+ throw new HTTPException110(403);
113610
114173
  const [intent] = await rows(
113611
114174
  deps,
113612
114175
  sql`select value from auth_verifications where identifier=${`managed-sign-in:${proof.intentId}`} and expires_at>clock_timestamp()`
113613
114176
  );
113614
- if (!intent) throw new HTTPException109(403, { message: "Sign-in method connection expired" });
114177
+ if (!intent) throw new HTTPException110(403, { message: "Sign-in method connection expired" });
113615
114178
  const request = ConnectIntent.parse(JSON.parse(intent.value));
113616
114179
  const authority = getCookie2(context, MANAGED_AUTH_SESSION_SET_COOKIE2);
113617
114180
  if (!request.actorFence) {
@@ -113628,9 +114191,9 @@ async function handleManagedSignInConnectCallback(context, deps, provider) {
113628
114191
  }
113629
114192
  }
113630
114193
  if (request.actorFence && (!authority || managedAuthSha2562(authority) !== request.actorFence.authorityHash))
113631
- throw new HTTPException109(403);
114194
+ throw new HTTPException110(403);
113632
114195
  if (value.link?.userId !== request.authUserId || value.link?.email === void 0)
113633
- throw new HTTPException109(403);
114196
+ throw new HTTPException110(403);
113634
114197
  const isolated = await isolatedManagedAuthOAuthCallbackRequest(deps.managedAuth, context.req.raw);
113635
114198
  const response = await runManagedSignInConnect(
113636
114199
  proof.intentId,
@@ -113658,7 +114221,7 @@ async function handleManagedSignInConnectCallback(context, deps, provider) {
113658
114221
 
113659
114222
  // src/routes/canonical-human-identities.ts
113660
114223
  import { managedAuthSecretRequestDigest as managedAuthSecretRequestDigest3 } from "@opengeni/core/managed-auth-session-sets";
113661
- var BindingId = z57.string().uuid();
114224
+ var BindingId = z58.string().uuid();
113662
114225
  async function requestIdentity(context, deps) {
113663
114226
  return await requireCanonicalHumanRequestIdentity2(context, {
113664
114227
  db: deps.db,
@@ -113671,13 +114234,13 @@ async function requestIdentity(context, deps) {
113671
114234
  async function parseBody5(context, schema) {
113672
114235
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
113673
114236
  if (!parsed.success) {
113674
- throw new HTTPException110(422, { message: "Invalid canonical human identity request" });
114237
+ throw new HTTPException111(422, { message: "Invalid canonical human identity request" });
113675
114238
  }
113676
114239
  return parsed.data;
113677
114240
  }
113678
114241
  function bindingId(context) {
113679
114242
  const parsed = BindingId.safeParse(context.req.param("bindingId"));
113680
- if (!parsed.success) throw new HTTPException110(422, { message: "Invalid login binding id" });
114243
+ if (!parsed.success) throw new HTTPException111(422, { message: "Invalid login binding id" });
113681
114244
  return parsed.data;
113682
114245
  }
113683
114246
  function identityError(context, error) {
@@ -113708,7 +114271,7 @@ async function mutate(context, deps, input) {
113708
114271
  const identity2 = await requestIdentity(context, deps);
113709
114272
  const actorFence = getManagedAuthRequestActorLeaseStamp2(context.req.raw);
113710
114273
  if (deps.settings.managedAuthSessionSetMode !== "legacy" && !actorFence) {
113711
- throw new HTTPException110(409, { message: "Managed actor mutation fence is unavailable" });
114274
+ throw new HTTPException111(409, { message: "Managed actor mutation fence is unavailable" });
113712
114275
  }
113713
114276
  try {
113714
114277
  const response = CanonicalHumanIdentityMutationResponse.parse(
@@ -113762,7 +114325,7 @@ function registerCanonicalHumanIdentityRoutes(app, deps) {
113762
114325
  app.post(`${base}/login-bindings`, async (context) => {
113763
114326
  const request = await parseBody5(context, LinkCanonicalHumanLoginBindingRequest);
113764
114327
  if (["google", "github", "credential"].includes(request.providerId.trim().toLowerCase())) {
113765
- throw new HTTPException110(403, {
114328
+ throw new HTTPException111(403, {
113766
114329
  message: "Use personal sign-in method settings with fresh provider proof"
113767
114330
  });
113768
114331
  }
@@ -113779,7 +114342,7 @@ function registerCanonicalHumanIdentityRoutes(app, deps) {
113779
114342
  if (projection.loginBindings.some(
113780
114343
  (binding) => binding.id === bindingId(context) && ["google", "github", "credential"].includes(binding.providerId)
113781
114344
  )) {
113782
- throw new HTTPException110(403, {
114345
+ throw new HTTPException111(403, {
113783
114346
  message: "Use personal sign-in method settings with last-method protection"
113784
114347
  });
113785
114348
  }
@@ -113797,7 +114360,7 @@ function registerCanonicalHumanIdentityRoutes(app, deps) {
113797
114360
  if (projection.loginBindings.some(
113798
114361
  (binding) => binding.id === bindingId(context) && ["google", "github", "credential"].includes(binding.providerId)
113799
114362
  )) {
113800
- throw new HTTPException110(403, {
114363
+ throw new HTTPException111(403, {
113801
114364
  message: "Managed-provider recovery cannot be initiated through raw binding management"
113802
114365
  });
113803
114366
  }
@@ -113895,7 +114458,7 @@ import {
113895
114458
  listOrganizationAdministrationMembers,
113896
114459
  listOrganizationInvitations,
113897
114460
  listSelfOrganizationInvitations,
113898
- nestedPostgresSqlState as nestedPostgresSqlState15,
114461
+ nestedPostgresSqlState as nestedPostgresSqlState16,
113899
114462
  prepareOrganizationUserSetupDelivery,
113900
114463
  revokeOrganizationInvitation,
113901
114464
  settleOrganizationUserSetupDelivery,
@@ -113908,12 +114471,12 @@ import {
113908
114471
  updateOrganizationPrivateSessionSettings,
113909
114472
  updateOrganizationRetentionPolicy
113910
114473
  } from "@opengeni/db";
113911
- import { HTTPException as HTTPException111 } from "hono/http-exception";
113912
- import { z as z58 } from "zod";
113913
- var OrganizationId2 = z58.string().uuid();
113914
- var WorkspaceId = z58.string().uuid();
113915
- var MembershipId = z58.string().uuid();
113916
- var InvitationId = z58.string().uuid();
114474
+ import { HTTPException as HTTPException112 } from "hono/http-exception";
114475
+ import { z as z59 } from "zod";
114476
+ var OrganizationId2 = z59.string().uuid();
114477
+ var WorkspaceId = z59.string().uuid();
114478
+ var MembershipId = z59.string().uuid();
114479
+ var InvitationId = z59.string().uuid();
113917
114480
  async function requirePrivateSessionAdministrator(context, deps, organizationId2) {
113918
114481
  const agent = agentActingAsPerson(
113919
114482
  context,
@@ -113925,14 +114488,14 @@ async function requirePrivateSessionAdministrator(context, deps, organizationId2
113925
114488
  const access = await requireAccessContext12(context, deps);
113926
114489
  const key = accountScopedApiKeyWorkspaceAuthority8(access);
113927
114490
  if (!key || key.accountId !== organizationId2 || !key.permissions.includes("workspace:admin")) {
113928
- throw new HTTPException111(403, {
114491
+ throw new HTTPException112(403, {
113929
114492
  message: "organization administration required"
113930
114493
  });
113931
114494
  }
113932
114495
  if (key.permissionMode === "explicit" && !access.accountGrants.some(
113933
114496
  (grant) => grant.accountId === organizationId2 && grant.permissions.includes("account:admin")
113934
114497
  )) {
113935
- throw new HTTPException111(403, { message: "organization administration requires account:admin" });
114498
+ throw new HTTPException112(403, { message: "organization administration requires account:admin" });
113936
114499
  }
113937
114500
  return { subjectId: access.subjectId };
113938
114501
  }
@@ -113940,25 +114503,25 @@ async function requireOrganizationKeyWorkspaceDeletion(context, deps, organizati
113940
114503
  const access = await requireAccessContext12(context, deps);
113941
114504
  const key = accountScopedApiKeyWorkspaceAuthority8(access);
113942
114505
  if (!key || key.accountId !== organizationId2 || !hasPermission30(key.permissions, "workspace:admin")) {
113943
- throw new HTTPException111(403, {
114506
+ throw new HTTPException112(403, {
113944
114507
  message: "deleting an organization workspace requires an organization owner session or an organization API key with workspace:admin"
113945
114508
  });
113946
114509
  }
113947
114510
  const workspace = await requireWorkspace4(deps.db, workspaceId).catch(() => null);
113948
114511
  if (!workspace || workspace.accountId !== organizationId2) {
113949
- throw new HTTPException111(404, { message: "workspace not found" });
114512
+ throw new HTTPException112(404, { message: "workspace not found" });
113950
114513
  }
113951
114514
  if (workspace.kind !== "shared") {
113952
- throw new HTTPException111(403, {
114515
+ throw new HTTPException112(403, {
113953
114516
  message: "organization API keys cannot delete a Personal workspace"
113954
114517
  });
113955
114518
  }
113956
114519
  if (!organizationWorkspaceInScope3(key.workspaceScope, workspaceId))
113957
- throw new HTTPException111(403, { message: "workspace is outside organization key scope" });
114520
+ throw new HTTPException112(403, { message: "workspace is outside organization key scope" });
113958
114521
  }
113959
114522
  async function requireManagedHuman(context, deps) {
113960
114523
  if (deps.settings.productAccessMode !== "managed" || !deps.managedAuth || !context.req.header("cookie") || context.req.header("authorization")) {
113961
- throw new HTTPException111(401, { message: "managed human session required" });
114524
+ throw new HTTPException112(401, { message: "managed human session required" });
113962
114525
  }
113963
114526
  const session = await getManagedSession4(context, deps.managedAuth, {
113964
114527
  db: deps.db,
@@ -113966,7 +114529,7 @@ async function requireManagedHuman(context, deps) {
113966
114529
  sessionSetMode: deps.settings.managedAuthSessionSetMode
113967
114530
  });
113968
114531
  if (!session?.user) {
113969
- throw new HTTPException111(401, { message: "managed human session required" });
114532
+ throw new HTTPException112(401, { message: "managed human session required" });
113970
114533
  }
113971
114534
  return { session, subjectId: `user:${session.user.id}` };
113972
114535
  }
@@ -113993,14 +114556,14 @@ async function requireOrganizationAdministrator(context, deps, organizationId2)
113993
114556
  );
113994
114557
  return { subjectId };
113995
114558
  }
113996
- throw new HTTPException111(401, {
114559
+ throw new HTTPException112(401, {
113997
114560
  message: "organization administrator session required"
113998
114561
  });
113999
114562
  }
114000
114563
  async function parseBody6(context, schema) {
114001
114564
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
114002
114565
  if (!parsed.success) {
114003
- throw new HTTPException111(422, {
114566
+ throw new HTTPException112(422, {
114004
114567
  message: "invalid organization membership request"
114005
114568
  });
114006
114569
  }
@@ -114008,19 +114571,19 @@ async function parseBody6(context, schema) {
114008
114571
  }
114009
114572
  function parseId(schema, value, label) {
114010
114573
  const parsed = schema.safeParse(value);
114011
- if (!parsed.success) throw new HTTPException111(422, { message: `invalid ${label}` });
114574
+ if (!parsed.success) throw new HTTPException112(422, { message: `invalid ${label}` });
114012
114575
  return parsed.data;
114013
114576
  }
114014
114577
  function rethrowMembershipError(error, resourceLimitMessage) {
114015
- const sqlState = nestedPostgresSqlState15(error);
114578
+ const sqlState = nestedPostgresSqlState16(error);
114016
114579
  if (sqlState === "54000" && resourceLimitMessage) {
114017
- throw new HTTPException111(409, {
114580
+ throw new HTTPException112(409, {
114018
114581
  message: resourceLimitMessage
114019
114582
  });
114020
114583
  }
114021
114584
  const status = organizationMembershipHttpStatus(sqlState);
114022
114585
  if (status !== null) {
114023
- throw new HTTPException111(status, {
114586
+ throw new HTTPException112(status, {
114024
114587
  message: status === 403 ? "organization administration is not authorized" : status === 404 ? "organization resource not found" : status === 409 ? "organization membership state changed; refresh and retry" : "invalid organization membership operation"
114025
114588
  });
114026
114589
  }
@@ -114060,7 +114623,7 @@ function registerOrganizationMembershipRoutes(app, deps) {
114060
114623
  )
114061
114624
  );
114062
114625
  } catch (error) {
114063
- if (error instanceof HTTPException111) throw error;
114626
+ if (error instanceof HTTPException112) throw error;
114064
114627
  rethrowMembershipError(error);
114065
114628
  }
114066
114629
  });
@@ -114118,8 +114681,8 @@ function registerOrganizationMembershipRoutes(app, deps) {
114118
114681
  })
114119
114682
  );
114120
114683
  } catch (error) {
114121
- if (nestedPostgresSqlState15(error) === "42501") {
114122
- throw new HTTPException111(403, {
114684
+ if (nestedPostgresSqlState16(error) === "42501") {
114685
+ throw new HTTPException112(403, {
114123
114686
  message: "organization membership is not active"
114124
114687
  });
114125
114688
  }
@@ -114130,7 +114693,7 @@ function registerOrganizationMembershipRoutes(app, deps) {
114130
114693
  const { session, subjectId } = await requireManagedHuman(context, deps);
114131
114694
  const query = ListOrganizationInvitationsPageQuery.safeParse(context.req.query());
114132
114695
  if (!query.success) {
114133
- throw new HTTPException111(422, {
114696
+ throw new HTTPException112(422, {
114134
114697
  message: "invalid organization invitation page"
114135
114698
  });
114136
114699
  }
@@ -114440,7 +115003,7 @@ function registerOrganizationMembershipRoutes(app, deps) {
114440
115003
  try {
114441
115004
  assertOrganizationUserSetupDeliveryConfigured(deps.settings, deps.managedEmailTransport);
114442
115005
  } catch {
114443
- throw new HTTPException111(503, {
115006
+ throw new HTTPException112(503, {
114444
115007
  message: "invited-user account setup delivery is not configured on this deployment"
114445
115008
  });
114446
115009
  }
@@ -114476,7 +115039,7 @@ function registerOrganizationMembershipRoutes(app, deps) {
114476
115039
  201
114477
115040
  );
114478
115041
  } catch (error) {
114479
- if (error instanceof HTTPException111) throw error;
115042
+ if (error instanceof HTTPException112) throw error;
114480
115043
  rethrowMembershipError(error);
114481
115044
  }
114482
115045
  });
@@ -114489,7 +115052,7 @@ function registerOrganizationMembershipRoutes(app, deps) {
114489
115052
  );
114490
115053
  const query = ListOrganizationInvitationsPageQuery.safeParse(context.req.query());
114491
115054
  if (!query.success) {
114492
- throw new HTTPException111(422, {
115055
+ throw new HTTPException112(422, {
114493
115056
  message: "invalid organization invitation page"
114494
115057
  });
114495
115058
  }
@@ -114526,7 +115089,7 @@ function registerOrganizationMembershipRoutes(app, deps) {
114526
115089
  try {
114527
115090
  assertOrganizationUserSetupDeliveryConfigured(deps.settings, deps.managedEmailTransport);
114528
115091
  } catch {
114529
- throw new HTTPException111(503, {
115092
+ throw new HTTPException112(503, {
114530
115093
  message: "invited-user account setup delivery is not configured on this deployment"
114531
115094
  });
114532
115095
  }
@@ -114573,7 +115136,7 @@ function registerOrganizationMembershipRoutes(app, deps) {
114573
115136
  membership: OrganizationMember.parse(result.membership)
114574
115137
  });
114575
115138
  } catch (error) {
114576
- if (error instanceof HTTPException111) throw error;
115139
+ if (error instanceof HTTPException112) throw error;
114577
115140
  rethrowMembershipError(error);
114578
115141
  }
114579
115142
  });
@@ -114772,14 +115335,14 @@ import {
114772
115335
  SessionListCursorError as SessionListCursorError2,
114773
115336
  SessionListCursorExpiredError as SessionListCursorExpiredError2
114774
115337
  } from "@opengeni/db";
114775
- import { HTTPException as HTTPException112 } from "hono/http-exception";
114776
- import { z as z59 } from "zod";
115338
+ import { HTTPException as HTTPException113 } from "hono/http-exception";
115339
+ import { z as z60 } from "zod";
114777
115340
  var ORGANIZATION_SESSION_LIST_MAX_WORKSPACE_READS = 25;
114778
- var OrganizationId3 = z59.string().uuid();
114779
- var OrganizationSessionListCursorEnvelope = z59.object({
114780
- workspaceId: z59.string().uuid(),
114781
- cursor: z59.string().min(1).nullable(),
114782
- pinnedOffset: z59.number().int().nonnegative().max(1e4).optional()
115341
+ var OrganizationId3 = z60.string().uuid();
115342
+ var OrganizationSessionListCursorEnvelope = z60.object({
115343
+ workspaceId: z60.string().uuid(),
115344
+ cursor: z60.string().min(1).nullable(),
115345
+ pinnedOffset: z60.number().int().nonnegative().max(1e4).optional()
114783
115346
  }).strict();
114784
115347
  function encodeOrganizationSessionListCursor(cursor) {
114785
115348
  return Buffer.from(JSON.stringify(cursor)).toString("base64url");
@@ -114804,7 +115367,7 @@ function requireOrganizationSessionReadAuthority(context, organizationId2) {
114804
115367
  if (authority.accountId === organizationId2 && hasPermission31(authority.permissions, "sessions:read")) {
114805
115368
  return "organization_api_key";
114806
115369
  }
114807
- throw new HTTPException112(403, { message: "organization session read authority required" });
115370
+ throw new HTTPException113(403, { message: "organization session read authority required" });
114808
115371
  }
114809
115372
  const accountGrant = context.accountGrants.find(
114810
115373
  (candidate) => candidate.accountId === organizationId2
@@ -114812,16 +115375,16 @@ function requireOrganizationSessionReadAuthority(context, organizationId2) {
114812
115375
  if (accountGrant?.permissions.includes("account:admin")) {
114813
115376
  return "account_admin";
114814
115377
  }
114815
- throw new HTTPException112(403, { message: "organization session read authority required" });
115378
+ throw new HTTPException113(403, { message: "organization session read authority required" });
114816
115379
  }
114817
115380
  function sessionAuthorizationHttpError2(error) {
114818
115381
  if (error instanceof SessionAuthorizationDeniedError16) {
114819
- return new HTTPException112(404, { message: "session not found" });
115382
+ return new HTTPException113(404, { message: "session not found" });
114820
115383
  }
114821
115384
  if (error instanceof SessionAuthorizationUnavailableError14) {
114822
- return new HTTPException112(503, { message: "session authorization is unavailable" });
115385
+ return new HTTPException113(503, { message: "session authorization is unavailable" });
114823
115386
  }
114824
- if (error instanceof HTTPException112) return error;
115387
+ if (error instanceof HTTPException113) return error;
114825
115388
  throw error;
114826
115389
  }
114827
115390
  function compareWorkspaceIds(left, right) {
@@ -114831,7 +115394,7 @@ function registerOrganizationSessionRoutes(app, deps) {
114831
115394
  app.get("/v1/organizations/:organizationId/sessions", async (c) => {
114832
115395
  const organizationIdResult = OrganizationId3.safeParse(c.req.param("organizationId"));
114833
115396
  if (!organizationIdResult.success) {
114834
- throw new HTTPException112(422, { message: "invalid organization id" });
115397
+ throw new HTTPException113(422, { message: "invalid organization id" });
114835
115398
  }
114836
115399
  const organizationId2 = organizationIdResult.data;
114837
115400
  const context = await requireAccessContext13(c, deps);
@@ -114839,14 +115402,14 @@ function registerOrganizationSessionRoutes(app, deps) {
114839
115402
  const queryResult = ListOrganizationSessionsQuery.safeParse(c.req.query());
114840
115403
  if (!queryResult.success) {
114841
115404
  const issue = queryResult.error.issues[0];
114842
- throw new HTTPException112(400, {
115405
+ throw new HTTPException113(400, {
114843
115406
  message: issue ? `${issue.path.length > 0 ? `${issue.path.join(".")}: ` : ""}${issue.message}` : "invalid organization session list query"
114844
115407
  });
114845
115408
  }
114846
115409
  const query = queryResult.data;
114847
115410
  const cursor = query.cursor ? decodeOrganizationSessionListCursor(query.cursor) : null;
114848
115411
  if (query.cursor && !cursor) {
114849
- throw new HTTPException112(400, { message: "cursor is invalid" });
115412
+ throw new HTTPException113(400, { message: "cursor is invalid" });
114850
115413
  }
114851
115414
  const authority = accountScopedApiKeyWorkspaceAuthority9(context);
114852
115415
  const workspaces = (await listSharedWorkspacesForAccount3(deps.db, organizationId2)).filter(
@@ -114918,7 +115481,7 @@ async function listWorkspaceSessionPage(c, deps, input) {
114918
115481
  "sessions:read"
114919
115482
  );
114920
115483
  } catch (error) {
114921
- if (input.readAuthority === "account_admin" && error instanceof HTTPException112 && error.status === 403) {
115484
+ if (input.readAuthority === "account_admin" && error instanceof HTTPException113 && error.status === 403) {
114922
115485
  return null;
114923
115486
  }
114924
115487
  throw error;
@@ -114932,7 +115495,7 @@ async function listWorkspaceSessionPage(c, deps, input) {
114932
115495
  }
114933
115496
  const cursor = input.innerCursor ? decodeSessionListCursor2(input.innerCursor) : null;
114934
115497
  if (input.innerCursor && !cursor) {
114935
- throw new HTTPException112(400, { message: "cursor is invalid" });
115498
+ throw new HTTPException113(400, { message: "cursor is invalid" });
114936
115499
  }
114937
115500
  try {
114938
115501
  const list = async (limit) => await listSessionsForSubject2(deps.db, input.workspace.id, {
@@ -114965,13 +115528,13 @@ async function listWorkspaceSessionPage(c, deps, input) {
114965
115528
  return { sessions: withStatus(rows2), nextCursor: page2.nextCursor, nextPinnedOffset };
114966
115529
  } catch (error) {
114967
115530
  if (error instanceof SessionListAccessError2) {
114968
- throw new HTTPException112(403, { message: error.message });
115531
+ throw new HTTPException113(403, { message: error.message });
114969
115532
  }
114970
115533
  if (error instanceof SessionListCursorExpiredError2) {
114971
- throw new HTTPException112(410, { message: error.message });
115534
+ throw new HTTPException113(410, { message: error.message });
114972
115535
  }
114973
115536
  if (error instanceof SessionListCursorError2) {
114974
- throw new HTTPException112(400, { message: error.message });
115537
+ throw new HTTPException113(400, { message: error.message });
114975
115538
  }
114976
115539
  throw error;
114977
115540
  }
@@ -115006,10 +115569,10 @@ import {
115006
115569
  OrganizationRecoveryUnavailableError,
115007
115570
  startOrganizationRecoveryOperation
115008
115571
  } from "@opengeni/db";
115009
- import { HTTPException as HTTPException113 } from "hono/http-exception";
115010
- import { z as z60 } from "zod";
115011
- var OrganizationId4 = z60.string().uuid();
115012
- var RecoveryOperationId = z60.string().uuid();
115572
+ import { HTTPException as HTTPException114 } from "hono/http-exception";
115573
+ import { z as z61 } from "zod";
115574
+ var OrganizationId4 = z61.string().uuid();
115575
+ var RecoveryOperationId = z61.string().uuid();
115013
115576
  var productionServices = {
115014
115577
  requireCanonicalHumanRequestIdentity: requireCanonicalHumanRequestIdentity3,
115015
115578
  getManagedAuthRequestActorAdmissionStamp,
@@ -115025,7 +115588,7 @@ var productionServices = {
115025
115588
  };
115026
115589
  async function requireRecoveryIdentity(context, deps, services) {
115027
115590
  if (deps.settings.productAccessMode !== "managed" || !deps.managedAuth || !context.req.header("cookie") || context.req.header("authorization")) {
115028
- throw new HTTPException113(401, {
115591
+ throw new HTTPException114(401, {
115029
115592
  message: "Managed human authentication required"
115030
115593
  });
115031
115594
  }
@@ -115041,7 +115604,7 @@ async function requireRecoveryIdentity(context, deps, services) {
115041
115604
  async function parseBody7(context, schema) {
115042
115605
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
115043
115606
  if (!parsed.success) {
115044
- throw new HTTPException113(422, {
115607
+ throw new HTTPException114(422, {
115045
115608
  message: "Invalid organization recovery request"
115046
115609
  });
115047
115610
  }
@@ -115049,7 +115612,7 @@ async function parseBody7(context, schema) {
115049
115612
  }
115050
115613
  function parseId2(schema, value, label) {
115051
115614
  const parsed = schema.safeParse(value);
115052
- if (!parsed.success) throw new HTTPException113(422, { message: `Invalid ${label}` });
115615
+ if (!parsed.success) throw new HTTPException114(422, { message: `Invalid ${label}` });
115053
115616
  return parsed.data;
115054
115617
  }
115055
115618
  function organizationId(context) {
@@ -115104,12 +115667,12 @@ function organizationRecoveryHttpError(error) {
115104
115667
  function requireActorFence(context, deps, services) {
115105
115668
  const actorFence = services.getManagedAuthRequestActorLeaseStamp(context.req.raw);
115106
115669
  if (deps.settings.managedAuthSessionSetMode !== "legacy" && !actorFence) {
115107
- throw new HTTPException113(409, {
115670
+ throw new HTTPException114(409, {
115108
115671
  message: "Managed actor mutation fence is unavailable"
115109
115672
  });
115110
115673
  }
115111
115674
  if (!actorFence) {
115112
- throw new HTTPException113(409, {
115675
+ throw new HTTPException114(409, {
115113
115676
  message: "Organization recovery requires provider-neutral browser login slots"
115114
115677
  });
115115
115678
  }
@@ -115261,12 +115824,12 @@ import {
115261
115824
  completeSelfServiceOrganizationSetup,
115262
115825
  completeOrganizationUserSetup,
115263
115826
  getSelfServiceOrganizationOnboardingState,
115264
- nestedPostgresSqlState as nestedPostgresSqlState16,
115827
+ nestedPostgresSqlState as nestedPostgresSqlState17,
115265
115828
  preflightOrganizationUserSetup,
115266
115829
  previewOrganizationUserSetup,
115267
115830
  recordOrganizationSignupUseCase
115268
115831
  } from "@opengeni/db";
115269
- import { HTTPException as HTTPException114 } from "hono/http-exception";
115832
+ import { HTTPException as HTTPException115 } from "hono/http-exception";
115270
115833
  var completedSetupReplayPasswordHash = `completed-replay:${"0".repeat(64)}`;
115271
115834
  function registerManagedOnboardingRoutes(app, deps, options = {}) {
115272
115835
  const accountSetupLimiter = options.accountSetupLimiter ?? new PublicSetupRateLimiter();
@@ -115290,7 +115853,7 @@ function registerManagedOnboardingRoutes(app, deps, options = {}) {
115290
115853
  );
115291
115854
  if (!parsed.success) {
115292
115855
  recordOrganizationSetupOutcome(deps.observability, "failed");
115293
- throw new HTTPException114(422, {
115856
+ throw new HTTPException115(422, {
115294
115857
  message: "invalid organization setup request"
115295
115858
  });
115296
115859
  }
@@ -115319,26 +115882,26 @@ function registerManagedOnboardingRoutes(app, deps, options = {}) {
115319
115882
  useCase: parsed.data.useCase
115320
115883
  }).catch((error) => {
115321
115884
  console.warn("[api] could not record the signup use case", {
115322
- sqlState: nestedPostgresSqlState16(error) ?? null
115885
+ sqlState: nestedPostgresSqlState17(error) ?? null
115323
115886
  });
115324
115887
  });
115325
115888
  }
115326
115889
  return context.json(completed);
115327
115890
  } catch (error) {
115328
115891
  recordOrganizationSetupOutcome(deps.observability, "failed");
115329
- const sqlState = nestedPostgresSqlState16(error);
115892
+ const sqlState = nestedPostgresSqlState17(error);
115330
115893
  if (sqlState === "22023") {
115331
- throw new HTTPException114(422, {
115894
+ throw new HTTPException115(422, {
115332
115895
  message: "invalid organization setup request"
115333
115896
  });
115334
115897
  }
115335
115898
  if (sqlState === "42501") {
115336
- throw new HTTPException114(403, {
115899
+ throw new HTTPException115(403, {
115337
115900
  message: "verified managed user required"
115338
115901
  });
115339
115902
  }
115340
115903
  if (sqlState === "23505" || sqlState === "55000") {
115341
- throw new HTTPException114(409, {
115904
+ throw new HTTPException115(409, {
115342
115905
  message: "organization setup is no longer available; refresh to continue"
115343
115906
  });
115344
115907
  }
@@ -115347,14 +115910,14 @@ function registerManagedOnboardingRoutes(app, deps, options = {}) {
115347
115910
  });
115348
115911
  app.post("/v1/auth/organization-setup/preview", async (context) => {
115349
115912
  if (deps.settings.productAccessMode !== "managed" || !deps.managedAuth) {
115350
- throw new HTTPException114(404, { message: "account setup is unavailable" });
115913
+ throw new HTTPException115(404, { message: "account setup is unavailable" });
115351
115914
  }
115352
115915
  enforceAccountSetupRateLimit(context, deps, accountSetupLimiter);
115353
115916
  const parsed = PreviewOrganizationUserSetupRequest.safeParse(
115354
115917
  await context.req.json().catch(() => null)
115355
115918
  );
115356
115919
  if (!parsed.success) {
115357
- throw new HTTPException114(422, { message: "invalid account setup preview request" });
115920
+ throw new HTTPException115(422, { message: "invalid account setup preview request" });
115358
115921
  }
115359
115922
  return context.json(
115360
115923
  OrganizationUserSetupPreview.parse(
@@ -115367,14 +115930,14 @@ function registerManagedOnboardingRoutes(app, deps, options = {}) {
115367
115930
  });
115368
115931
  app.post("/v1/auth/organization-setup", async (context) => {
115369
115932
  if (deps.settings.productAccessMode !== "managed" || !deps.managedAuth) {
115370
- throw new HTTPException114(404, { message: "account setup is unavailable" });
115933
+ throw new HTTPException115(404, { message: "account setup is unavailable" });
115371
115934
  }
115372
115935
  enforceAccountSetupRateLimit(context, deps, accountSetupLimiter);
115373
115936
  const parsed = CompleteOrganizationUserSetupRequest.safeParse(
115374
115937
  await context.req.json().catch(() => null)
115375
115938
  );
115376
115939
  if (!parsed.success) {
115377
- throw new HTTPException114(422, {
115940
+ throw new HTTPException115(422, {
115378
115941
  message: "invalid account setup request"
115379
115942
  });
115380
115943
  }
@@ -115382,7 +115945,7 @@ function registerManagedOnboardingRoutes(app, deps, options = {}) {
115382
115945
  const tokenDigest = await organizationUserSetupTokenDigest(parsed.data.token);
115383
115946
  const preflight = await preflightOrganizationUserSetup(deps.db, tokenDigest);
115384
115947
  if (preflight === "unavailable") {
115385
- throw new HTTPException114(404, {
115948
+ throw new HTTPException115(404, {
115386
115949
  message: "account setup link is invalid, expired, or already belongs to an account"
115387
115950
  });
115388
115951
  }
@@ -115406,19 +115969,19 @@ function registerManagedOnboardingRoutes(app, deps, options = {}) {
115406
115969
  )
115407
115970
  );
115408
115971
  } catch (error) {
115409
- const sqlState = nestedPostgresSqlState16(error);
115972
+ const sqlState = nestedPostgresSqlState17(error);
115410
115973
  if (sqlState === "22023") {
115411
- throw new HTTPException114(422, {
115974
+ throw new HTTPException115(422, {
115412
115975
  message: "invalid account setup request"
115413
115976
  });
115414
115977
  }
115415
115978
  if (sqlState === "23505") {
115416
- throw new HTTPException114(409, {
115979
+ throw new HTTPException115(409, {
115417
115980
  message: "account setup request changed; reopen the original link and try again"
115418
115981
  });
115419
115982
  }
115420
115983
  if (sqlState === "P0002" || sqlState === "42501" || sqlState === "55000") {
115421
- throw new HTTPException114(404, {
115984
+ throw new HTTPException115(404, {
115422
115985
  message: "account setup link is invalid, expired, or already belongs to an account"
115423
115986
  });
115424
115987
  }
@@ -115433,7 +115996,7 @@ function enforceAccountSetupRateLimit(context, deps, limiter) {
115433
115996
  } catch {
115434
115997
  }
115435
115998
  if (!allowed2) {
115436
- throw new HTTPException114(429, { message: "too many account setup requests; slow down" });
115999
+ throw new HTTPException115(429, { message: "too many account setup requests; slow down" });
115437
116000
  }
115438
116001
  }
115439
116002
  var PublicSetupRateLimiter = class {
@@ -115496,7 +116059,7 @@ function refillSetupRateLimitBucket(bucket, capacity, refillPerSecond, now) {
115496
116059
  }
115497
116060
  async function requireManagedHuman2(context, deps) {
115498
116061
  if (deps.settings.productAccessMode !== "managed" || !deps.managedAuth || !context.req.header("cookie") || context.req.header("authorization")) {
115499
- throw new HTTPException114(401, { message: "managed human session required" });
116062
+ throw new HTTPException115(401, { message: "managed human session required" });
115500
116063
  }
115501
116064
  const session = await getManagedSession5(context, deps.managedAuth, {
115502
116065
  db: deps.db,
@@ -115504,7 +116067,7 @@ async function requireManagedHuman2(context, deps) {
115504
116067
  sessionSetMode: deps.settings.managedAuthSessionSetMode
115505
116068
  });
115506
116069
  if (!session?.user) {
115507
- throw new HTTPException114(401, { message: "managed human session required" });
116070
+ throw new HTTPException115(401, { message: "managed human session required" });
115508
116071
  }
115509
116072
  return session;
115510
116073
  }
@@ -115527,26 +116090,26 @@ import {
115527
116090
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError15,
115528
116091
  SessionTenancyManagedHumanRequiredError as SessionTenancyManagedHumanRequiredError3
115529
116092
  } from "@opengeni/core";
115530
- import { nestedPostgresSqlState as nestedPostgresSqlState17, SessionTenancyNotActivatedError as SessionTenancyNotActivatedError3 } from "@opengeni/db";
115531
- import { HTTPException as HTTPException115 } from "hono/http-exception";
115532
- import { z as z61 } from "zod";
116093
+ import { nestedPostgresSqlState as nestedPostgresSqlState18, SessionTenancyNotActivatedError as SessionTenancyNotActivatedError3 } from "@opengeni/db";
116094
+ import { HTTPException as HTTPException116 } from "hono/http-exception";
116095
+ import { z as z62 } from "zod";
115533
116096
  async function body2(context, schema) {
115534
116097
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
115535
- if (!parsed.success) throw new HTTPException115(422, { message: "invalid user-resource request" });
116098
+ if (!parsed.success) throw new HTTPException116(422, { message: "invalid user-resource request" });
115536
116099
  return parsed.data;
115537
116100
  }
115538
116101
  function lifecycleError(error) {
115539
116102
  if (error instanceof SessionAuthorizationDeniedError17 || error instanceof SessionTenancyManagedHumanRequiredError3 || error instanceof SessionTenancyNotActivatedError3) {
115540
- throw new HTTPException115(403, { message: "user-resource authority denied" });
116103
+ throw new HTTPException116(403, { message: "user-resource authority denied" });
115541
116104
  }
115542
116105
  if (error instanceof SessionAuthorizationUnavailableError15) {
115543
- throw new HTTPException115(503, { message: "session authorization unavailable" });
116106
+ throw new HTTPException116(503, { message: "session authorization unavailable" });
115544
116107
  }
115545
- if (nestedPostgresSqlState17(error) === "42501") {
115546
- throw new HTTPException115(403, { message: "user-resource authority denied" });
116108
+ if (nestedPostgresSqlState18(error) === "42501") {
116109
+ throw new HTTPException116(403, { message: "user-resource authority denied" });
115547
116110
  }
115548
- if (nestedPostgresSqlState17(error) === "22023") {
115549
- throw new HTTPException115(422, { message: "invalid user-resource request" });
116111
+ if (nestedPostgresSqlState18(error) === "22023") {
116112
+ throw new HTTPException116(422, { message: "invalid user-resource request" });
115550
116113
  }
115551
116114
  throw error;
115552
116115
  }
@@ -115559,7 +116122,7 @@ function registerUserResourceAuthorityRoutes(app, deps) {
115559
116122
  cursor: context.req.query("cursor"),
115560
116123
  limit: context.req.query("limit")
115561
116124
  });
115562
- if (!query.success) throw new HTTPException115(422, { message: "explicit scope=user required" });
116125
+ if (!query.success) throw new HTTPException116(422, { message: "explicit scope=user required" });
115563
116126
  const workspaceId = context.req.param("workspaceId");
115564
116127
  const access = await requireAccessGrantAuthorization39(
115565
116128
  context,
@@ -115586,8 +116149,8 @@ function registerUserResourceAuthorityRoutes(app, deps) {
115586
116149
  });
115587
116150
  app.post(`${base}/:authorityId/grants`, async (context) => {
115588
116151
  const workspaceId = context.req.param("workspaceId");
115589
- const authorityId = z61.string().uuid().safeParse(context.req.param("authorityId"));
115590
- if (!authorityId.success) throw new HTTPException115(422, { message: "invalid authority id" });
116152
+ const authorityId = z62.string().uuid().safeParse(context.req.param("authorityId"));
116153
+ if (!authorityId.success) throw new HTTPException116(422, { message: "invalid authority id" });
115591
116154
  const request = await body2(context, IssueUserResourceGrantRequest);
115592
116155
  const access = await requireAccessGrantAuthorization39(
115593
116156
  context,
@@ -115615,9 +116178,9 @@ function registerUserResourceAuthorityRoutes(app, deps) {
115615
116178
  });
115616
116179
  app.delete(`${base}/grants/:grantId`, async (context) => {
115617
116180
  const query = RevokeUserResourceGrantQuery.safeParse({ scope: context.req.query("scope") });
115618
- if (!query.success) throw new HTTPException115(422, { message: "explicit scope=user required" });
115619
- const grantId = z61.string().uuid().safeParse(context.req.param("grantId"));
115620
- if (!grantId.success) throw new HTTPException115(422, { message: "invalid grant id" });
116181
+ if (!query.success) throw new HTTPException116(422, { message: "explicit scope=user required" });
116182
+ const grantId = z62.string().uuid().safeParse(context.req.param("grantId"));
116183
+ if (!grantId.success) throw new HTTPException116(422, { message: "invalid grant id" });
115621
116184
  const workspaceId = context.req.param("workspaceId");
115622
116185
  const access = await requireAccessGrantAuthorization39(
115623
116186
  context,
@@ -116205,6 +116768,58 @@ async function exactAvailable(providers, providerId, context) {
116205
116768
  return provider && await provider.available(context) ? provider : null;
116206
116769
  }
116207
116770
 
116771
+ // src/integrations/slack-tool-review.ts
116772
+ function slackToolReviewCanDecide(review) {
116773
+ return review.detailsAvailable && reviewLines(review).every((line) => line.length <= 2800) && JSON.stringify({
116774
+ title: review.title,
116775
+ account: review.accountLabel,
116776
+ consequence: review.consequence,
116777
+ effects: review.effects,
116778
+ samples: review.samples,
116779
+ fields: review.fields,
116780
+ moreFields: review.moreFields,
116781
+ reason: review.reason
116782
+ }).length <= 6e3;
116783
+ }
116784
+ function slackToolReviewBlocks(review, privateAudience) {
116785
+ if (!privateAudience || !slackToolReviewCanDecide(review))
116786
+ return [
116787
+ {
116788
+ type: "section",
116789
+ text: {
116790
+ type: "plain_text",
116791
+ text: "An action needs review. Open the task to review its private details."
116792
+ }
116793
+ }
116794
+ ];
116795
+ const lines = reviewLines(review);
116796
+ const blocks = [];
116797
+ let text = "";
116798
+ for (const line of lines) {
116799
+ if (text && text.length + line.length + 1 > 2800) {
116800
+ blocks.push({ type: "section", text: { type: "plain_text", text } });
116801
+ text = "";
116802
+ }
116803
+ text += `${text ? "\n" : ""}${line}`;
116804
+ }
116805
+ if (text) blocks.push({ type: "section", text: { type: "plain_text", text } });
116806
+ return blocks;
116807
+ }
116808
+ function reviewLines(review) {
116809
+ return [
116810
+ review.title,
116811
+ review.accountLabel,
116812
+ review.consequence,
116813
+ ...review.effects,
116814
+ ...(review.samples ?? []).map(
116815
+ (sample) => [sample.title, sample.subtitle].filter(Boolean).join(" \u2014 ")
116816
+ ),
116817
+ ...review.fields.map((field) => `${field.label}: ${field.preview}`),
116818
+ review.moreFields ? `${review.moreFields} more fields in full details.` : null,
116819
+ review.reason
116820
+ ].filter((line) => Boolean(line));
116821
+ }
116822
+
116208
116823
  // src/integrations/slack-interactions.ts
116209
116824
  import { createHash as createHash33, createHmac as createHmac14, timingSafeEqual as timingSafeEqual4 } from "crypto";
116210
116825
  import {
@@ -116254,6 +116869,7 @@ import {
116254
116869
  enqueueSlackAppHomeRefresh,
116255
116870
  enqueueSlackInteractionInbox,
116256
116871
  getConnectionMetadata as getConnectionMetadata14,
116872
+ getToolActionReview as getToolActionReview2,
116257
116873
  getOrCreateSlackInteraction,
116258
116874
  getLatestSessionModelForSubject,
116259
116875
  getSession as getSession11,
@@ -116320,7 +116936,7 @@ import {
116320
116936
  getActorNewSessionModelChoice,
116321
116937
  hasPermission as hasPermission33,
116322
116938
  requireAccessContext as requireAccessContext14,
116323
- requireAccessGrant as requireAccessGrant52,
116939
+ requireAccessGrant as requireAccessGrant53,
116324
116940
  requireSessionAuthorizationListScope as requireSessionAuthorizationListScope4,
116325
116941
  resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings9
116326
116942
  } from "@opengeni/core";
@@ -116462,7 +117078,7 @@ function otherDeploymentLinkContext(text, webBaseUrl) {
116462
117078
  }
116463
117079
 
116464
117080
  // src/integrations/slack-interactions.ts
116465
- import { HTTPException as HTTPException116 } from "hono/http-exception";
117081
+ import { HTTPException as HTTPException117 } from "hono/http-exception";
116466
117082
 
116467
117083
  // src/integrations/slack-mrkdwn.ts
116468
117084
  var PROVIDER_CITATION_TOKEN = /[ \t]*\u{E200}(?:cite|filecite)(?:\u{E202}[^\u{E200}-\u{E202}]*)*\u{E201}/gu;
@@ -117230,14 +117846,14 @@ function registerSlackInteractionRoutes(app, deps) {
117230
117846
  const payload = parseJsonObject(signed.rawBody);
117231
117847
  if (payload.type === "url_verification") {
117232
117848
  const challenge = boundedString(payload.challenge, 512);
117233
- if (!challenge) throw new HTTPException116(400, { message: "invalid Slack challenge" });
117849
+ if (!challenge) throw new HTTPException117(400, { message: "invalid Slack challenge" });
117234
117850
  return c.json({ challenge });
117235
117851
  }
117236
117852
  const teamId = boundedString(payload.team_id, 64);
117237
- if (!teamId) throw new HTTPException116(400, { message: "invalid Slack event" });
117853
+ if (!teamId) throw new HTTPException117(400, { message: "invalid Slack event" });
117238
117854
  const installation = await resolveSlackInstallationRoute2(deps.db, teamId);
117239
117855
  if (!installation)
117240
- throw new HTTPException116(403, {
117856
+ throw new HTTPException117(403, {
117241
117857
  message: "Slack installation unavailable"
117242
117858
  });
117243
117859
  const event = record5(payload.event);
@@ -117280,12 +117896,12 @@ function registerSlackInteractionRoutes(app, deps) {
117280
117896
  const signed = await readSignedSlackRequest(c, deps);
117281
117897
  const form = new URLSearchParams(signed.rawBody);
117282
117898
  if (form.get("command") !== deps.settings.slackCommand) {
117283
- throw new HTTPException116(400, { message: "invalid Slack command" });
117899
+ throw new HTTPException117(400, { message: "invalid Slack command" });
117284
117900
  }
117285
117901
  const entry2 = normalizedFormInteraction(form, "slash_command");
117286
117902
  const installation = await resolveSlackInstallationRoute2(deps.db, entry2.slackTeamId);
117287
117903
  if (!installation)
117288
- throw new HTTPException116(403, {
117904
+ throw new HTTPException117(403, {
117289
117905
  message: "Slack installation unavailable"
117290
117906
  });
117291
117907
  if (isSlackInfoCommand(entry2.text)) {
@@ -117322,13 +117938,13 @@ function registerSlackInteractionRoutes(app, deps) {
117322
117938
  const entry2 = normalizedBlockActionInteraction(payload);
117323
117939
  const installation2 = await resolveSlackInstallationRoute2(deps.db, entry2.slackTeamId);
117324
117940
  if (!installation2) {
117325
- throw new HTTPException116(403, { message: "Slack installation unavailable" });
117941
+ throw new HTTPException117(403, { message: "Slack installation unavailable" });
117326
117942
  }
117327
117943
  await enqueueNormalizedSlackInteraction(deps, installation2, entry2);
117328
117944
  return c.json({ ok: true });
117329
117945
  }
117330
117946
  if (payload.type !== "message_action") {
117331
- throw new HTTPException116(400, {
117947
+ throw new HTTPException117(400, {
117332
117948
  message: "unsupported Slack interaction"
117333
117949
  });
117334
117950
  }
@@ -117344,13 +117960,13 @@ function registerSlackInteractionRoutes(app, deps) {
117344
117960
  const threadTs = boundedString(message?.thread_ts, 64);
117345
117961
  const text = boundedText(message?.text);
117346
117962
  if (!triggerId || !teamId || !userId || !channelId || !messageTs || !text) {
117347
- throw new HTTPException116(400, {
117963
+ throw new HTTPException117(400, {
117348
117964
  message: "invalid Slack message shortcut"
117349
117965
  });
117350
117966
  }
117351
117967
  const installation = await resolveSlackInstallationRoute2(deps.db, teamId);
117352
117968
  if (!installation)
117353
- throw new HTTPException116(403, {
117969
+ throw new HTTPException117(403, {
117354
117970
  message: "Slack installation unavailable"
117355
117971
  });
117356
117972
  await enqueueNormalizedSlackInteraction(deps, installation, {
@@ -117487,7 +118103,7 @@ function registerSlackInteractionRoutes(app, deps) {
117487
118103
  );
117488
118104
  app.get("/v1/workspaces/:workspaceId/members/access-requests/slack", async (c) => {
117489
118105
  const workspaceId = c.req.param("workspaceId");
117490
- await requireAccessGrant52(c, deps, workspaceId, "members:manage");
118106
+ await requireAccessGrant53(c, deps, workspaceId, "members:manage");
117491
118107
  const workspace = await getWorkspace11(deps.db, workspaceId);
117492
118108
  const requests = await listPendingSlackUserLinkAccessRequests(deps.db, workspaceId);
117493
118109
  return c.json(
@@ -117503,7 +118119,7 @@ function registerSlackInteractionRoutes(app, deps) {
117503
118119
  "/v1/workspaces/:workspaceId/members/access-requests/slack/:requestId/approve",
117504
118120
  async (c) => {
117505
118121
  const workspaceId = c.req.param("workspaceId");
117506
- const grant = await requireAccessGrant52(c, deps, workspaceId, "members:manage");
118122
+ const grant = await requireAccessGrant53(c, deps, workspaceId, "members:manage");
117507
118123
  const payload = await parseRequestJson(c, ApproveSlackUserLinkAccessRequest);
117508
118124
  const requestId = c.req.param("requestId");
117509
118125
  const requestTeam = await slackUserLinkAccessRequestTeam(deps.db, {
@@ -117543,7 +118159,7 @@ function registerSlackInteractionRoutes(app, deps) {
117543
118159
  "/v1/workspaces/:workspaceId/members/access-requests/slack/:requestId/deny",
117544
118160
  async (c) => {
117545
118161
  const workspaceId = c.req.param("workspaceId");
117546
- const grant = await requireAccessGrant52(c, deps, workspaceId, "members:manage");
118162
+ const grant = await requireAccessGrant53(c, deps, workspaceId, "members:manage");
117547
118163
  const payload = await parseRequestJson(c, SlackUserLinkAccessMutationRequest);
117548
118164
  try {
117549
118165
  const request = await denySlackUserLinkAccessRequest(deps.db, {
@@ -117566,24 +118182,24 @@ function registerSlackInteractionRoutes(app, deps) {
117566
118182
  );
117567
118183
  app.post("/v1/workspaces/:workspaceId/integrations/slack/user-links", async (c) => {
117568
118184
  const workspaceId = c.req.param("workspaceId");
117569
- const grant = await requireAccessGrant52(c, deps, workspaceId, "sessions:create");
118185
+ const grant = await requireAccessGrant53(c, deps, workspaceId, "sessions:create");
117570
118186
  const body3 = record5(await c.req.json().catch(() => null));
117571
118187
  const linkToken = boundedString(body3?.linkToken, 2048);
117572
118188
  const signingSecret = deps.settings.slackSigningSecret;
117573
118189
  if (!linkToken || !signingSecret) {
117574
- throw new HTTPException116(400, {
118190
+ throw new HTTPException117(400, {
117575
118191
  message: "invalid or expired Slack identity link"
117576
118192
  });
117577
118193
  }
117578
118194
  const link = verifySlackUserLinkToken(signingSecret, linkToken);
117579
118195
  if (!link || link.workspaceId !== workspaceId) {
117580
- throw new HTTPException116(400, {
118196
+ throw new HTTPException117(400, {
117581
118197
  message: "invalid or expired Slack identity link"
117582
118198
  });
117583
118199
  }
117584
118200
  const installation = await resolveSlackLinkTargetInstallation(deps, link, workspaceId);
117585
118201
  if (!installation) {
117586
- throw new HTTPException116(404, {
118202
+ throw new HTTPException117(404, {
117587
118203
  message: "Slack installation not found"
117588
118204
  });
117589
118205
  }
@@ -117622,9 +118238,9 @@ function registerSlackInteractionRoutes(app, deps) {
117622
118238
  "/v1/workspaces/:workspaceId/integrations/slack/user-links/:slackUserId",
117623
118239
  async (c) => {
117624
118240
  const workspaceId = c.req.param("workspaceId");
117625
- await requireAccessGrant52(c, deps, workspaceId, "connections:write");
118241
+ await requireAccessGrant53(c, deps, workspaceId, "connections:write");
117626
118242
  const connectionId = boundedString(c.req.query("connectionId"), 64);
117627
- if (!connectionId) throw new HTTPException116(400, { message: "connectionId is required" });
118243
+ if (!connectionId) throw new HTTPException117(400, { message: "connectionId is required" });
117628
118244
  return c.json({
117629
118245
  deleted: await deleteSlackBotUserLink(
117630
118246
  deps.db,
@@ -117637,9 +118253,9 @@ function registerSlackInteractionRoutes(app, deps) {
117637
118253
  );
117638
118254
  app.get("/v1/workspaces/:workspaceId/integrations/slack/channel-routes", async (c) => {
117639
118255
  const workspaceId = c.req.param("workspaceId");
117640
- const grant = await requireAccessGrant52(c, deps, workspaceId, "workspace:admin");
118256
+ const grant = await requireAccessGrant53(c, deps, workspaceId, "workspace:admin");
117641
118257
  const connectionId = boundedString(c.req.query("connectionId"), 64);
117642
- if (!connectionId) throw new HTTPException116(400, { message: "connectionId is required" });
118258
+ if (!connectionId) throw new HTTPException117(400, { message: "connectionId is required" });
117643
118259
  const routes = await listSlackChannelRoutes(
117644
118260
  deps.db,
117645
118261
  { accountId: grant.accountId, workspaceId },
@@ -117668,7 +118284,7 @@ function registerSlackInteractionRoutes(app, deps) {
117668
118284
  });
117669
118285
  app.put("/v1/workspaces/:workspaceId/integrations/slack/channel-routes", async (c) => {
117670
118286
  const workspaceId = c.req.param("workspaceId");
117671
- const grant = await requireAccessGrant52(c, deps, workspaceId, "workspace:admin");
118287
+ const grant = await requireAccessGrant53(c, deps, workspaceId, "workspace:admin");
117672
118288
  const payload = await parseRequestJson(c, UpdateSlackChannelRoutesRequest);
117673
118289
  const home = { accountId: grant.accountId, workspaceId };
117674
118290
  const installation = await listSlackInstallationBindings2(deps.db, home);
@@ -117676,7 +118292,7 @@ function registerSlackInteractionRoutes(app, deps) {
117676
118292
  (candidate) => candidate.connectionId === payload.connectionId
117677
118293
  );
117678
118294
  if (!binding) {
117679
- throw new HTTPException116(404, { message: "Slack installation not found" });
118295
+ throw new HTTPException117(404, { message: "Slack installation not found" });
117680
118296
  }
117681
118297
  const callerPersonalWorkspaceId = await namedSubjectPersonalWorkspaceId2(deps.db, {
117682
118298
  accountId: grant.accountId,
@@ -117690,12 +118306,12 @@ function registerSlackInteractionRoutes(app, deps) {
117690
118306
  targetWorkspaceId: route2.targetWorkspaceId
117691
118307
  });
117692
118308
  if (!authorized || !hasPermission33(authorized.permissions, "sessions:create")) {
117693
- throw new HTTPException116(403, {
118309
+ throw new HTTPException117(403, {
117694
118310
  message: `you cannot start work in the workspace chosen for ${route2.slackChannelId}`
117695
118311
  });
117696
118312
  }
117697
118313
  if (callerPersonalWorkspaceId !== null && route2.targetWorkspaceId === callerPersonalWorkspaceId) {
117698
- throw new HTTPException116(422, {
118314
+ throw new HTTPException117(422, {
117699
118315
  message: `a channel cannot be routed to a personal workspace: work started in ${route2.slackChannelId} would be invisible to everyone else in it`
117700
118316
  });
117701
118317
  }
@@ -117723,9 +118339,9 @@ function registerSlackInteractionRoutes(app, deps) {
117723
118339
  });
117724
118340
  app.get("/v1/workspaces/:workspaceId/integrations/slack/reaction-channels", async (c) => {
117725
118341
  const workspaceId = c.req.param("workspaceId");
117726
- const grant = await requireAccessGrant52(c, deps, workspaceId, "workspace:admin");
118342
+ const grant = await requireAccessGrant53(c, deps, workspaceId, "workspace:admin");
117727
118343
  const connectionId = boundedString(c.req.query("connectionId"), 64);
117728
- if (!connectionId) throw new HTTPException116(400, { message: "connectionId is required" });
118344
+ if (!connectionId) throw new HTTPException117(400, { message: "connectionId is required" });
117729
118345
  const cursor = boundedString(c.req.query("cursor"), 1024);
117730
118346
  const client = await createOpenGeniSlackBotInteractionClient(deps, {
117731
118347
  accountId: grant.accountId,
@@ -118070,6 +118686,15 @@ function startSlackInteractionPump(deps, options = {}) {
118070
118686
  for (let index = 0; index < maxPerTick; index += 1) {
118071
118687
  if (!await drainSlackInteractionsOnce(deps)) break;
118072
118688
  }
118689
+ } catch (error) {
118690
+ try {
118691
+ deps.observability?.error("Slack interaction delivery claim failed", {
118692
+ errorClass: error instanceof Error ? error.name : "SlackInteractionPumpError",
118693
+ errorCode: safeErrorCode(error),
118694
+ origin: "api"
118695
+ });
118696
+ } catch {
118697
+ }
118073
118698
  } finally {
118074
118699
  running = false;
118075
118700
  }
@@ -118684,7 +119309,7 @@ async function processSlackInboxEntry(deps, entry2) {
118684
119309
  { surface: "slack" }
118685
119310
  );
118686
119311
  } catch (error) {
118687
- if (error instanceof HTTPException116) {
119312
+ if (error instanceof HTTPException117) {
118688
119313
  await client.postMessage({
118689
119314
  operationId: deterministicUuid2(`slack-admission-failed:${interaction.id}`),
118690
119315
  ...routePolicy.directMessageShortcut ? { userId: entry2.slackUserId } : {
@@ -119220,7 +119845,7 @@ async function processSlackReactionInboxEntry(deps, entry2) {
119220
119845
  );
119221
119846
  await acceptSlackReactionTask(deps, grant, session.id, preparedEntry, preparedTask.resources);
119222
119847
  } catch (error) {
119223
- if (error instanceof HTTPException116) {
119848
+ if (error instanceof HTTPException117) {
119224
119849
  await client.postMessage({
119225
119850
  operationId: deterministicUuid2(`slack-reaction-admission-failed:${interaction.id}`),
119226
119851
  channelId: entry2.slackChannelId,
@@ -119671,7 +120296,7 @@ async function withSlackAllowanceAdmissionNotice(deps, entry2, destination, admi
119671
120296
  try {
119672
120297
  await admit();
119673
120298
  } catch (error) {
119674
- if (error instanceof HTTPException116 && (parseAllowanceExhaustedRefusal2(error.cause) ?? parseAllowanceExhaustedRefusal2(error))) {
120299
+ if (error instanceof HTTPException117 && (parseAllowanceExhaustedRefusal2(error.cause) ?? parseAllowanceExhaustedRefusal2(error))) {
119675
120300
  const client = await createOpenGeniSlackBotInteractionClient(deps, {
119676
120301
  accountId: entry2.accountId,
119677
120302
  workspaceId: entry2.workspaceId,
@@ -120010,7 +120635,7 @@ async function executeSlackAction(deps, grant, interaction, handle) {
120010
120635
  });
120011
120636
  return {
120012
120637
  result: decision === "approve" ? "approved" : "rejected",
120013
- text: `${mention}${decision === "approve" ? "Approved once" : "Rejected"}. OpenGeni will continue from the durable task state.`
120638
+ text: `${mention}${decision === "approve" ? "Approved. Waiting to run." : "Declined. This action will not run."} ${openSessionText(deps, interaction.workspaceId, handle.sessionId)}`
120014
120639
  };
120015
120640
  }
120016
120641
  if (handle.actionKind === "human_input_select" || handle.actionKind === "human_input_skip") {
@@ -120307,7 +120932,7 @@ function slackApprovalSummaries(payload) {
120307
120932
  return id ? { id, name: name ?? "OpenGeni action" } : null;
120308
120933
  }).filter((approval) => approval !== null).slice(0, MAX_SLACK_APPROVALS_PER_CARD);
120309
120934
  }
120310
- async function slackApprovalCard(deps, interaction, event, mention, requesterAuthorized) {
120935
+ async function legacySlackApprovalCard(deps, interaction, event, mention, requesterAuthorized) {
120311
120936
  const operationId = deterministicUuid2(
120312
120937
  slackPostSeed(interaction, `slack-delivery:${interaction.id}:${event.sequence}:approval`)
120313
120938
  );
@@ -120380,6 +121005,94 @@ ${approval.name}` }
120380
121005
  operationId
120381
121006
  };
120382
121007
  }
121008
+ async function slackApprovalCard(deps, interaction, event, mention, requesterAuthorized) {
121009
+ const operationId = deterministicUuid2(
121010
+ slackPostSeed(interaction, `slack-delivery:${interaction.id}:${event.sequence}:approval`)
121011
+ );
121012
+ const approvals = slackApprovalSummaries(event.payload);
121013
+ const link = interaction.sessionId ? slackSessionUrl(deps, interaction.workspaceId, interaction.sessionId) : null;
121014
+ const fallback = `${mention}An action needs review.${link ? ` <${link}|Open the task to review>` : " Open the task to review it."}`;
121015
+ const privateAudience = requesterAuthorized && interaction.visibility === "private" && interaction.slackChannelId.startsWith("D");
121016
+ if (!privateAudience || !interaction.sessionId || !interaction.initiatingSlackUserId || approvals.length === 0)
121017
+ return { text: fallback, operationId };
121018
+ const reviews = await Promise.all(
121019
+ approvals.map(
121020
+ (approval) => getToolActionReview2(deps.db, {
121021
+ accountId: interaction.accountId,
121022
+ workspaceId: interaction.workspaceId,
121023
+ sessionId: interaction.sessionId,
121024
+ approvalId: approval.id
121025
+ })
121026
+ )
121027
+ );
121028
+ const reviewById = new Map(
121029
+ reviews.flatMap((review) => review ? [[review.id, review]] : [])
121030
+ );
121031
+ const actionable = approvals.filter((approval) => {
121032
+ const review = reviewById.get(approval.id);
121033
+ return review && slackToolReviewCanDecide(review);
121034
+ });
121035
+ const specs = actionable.flatMap((approval) => [
121036
+ {
121037
+ actionKind: "approval_approve",
121038
+ actionKey: `approval:${approval.id}:approve`,
121039
+ targetId: approval.id
121040
+ },
121041
+ {
121042
+ actionKind: "approval_reject",
121043
+ actionKey: `approval:${approval.id}:reject`,
121044
+ targetId: approval.id
121045
+ }
121046
+ ]).slice(0, MAX_SLACK_ACTIONS_PER_CARD);
121047
+ const handles = specs.length ? await reserveSlackInteractionActionHandles(deps.db, {
121048
+ interaction,
121049
+ sessionEventSequence: event.sequence,
121050
+ messageOperationId: operationId,
121051
+ expiresAt: new Date(Date.now() + SLACK_ACTION_TTL_MS),
121052
+ actions: specs
121053
+ }) : [];
121054
+ const byKey = new Map(handles.map((handle) => [handle.actionKey, handle]));
121055
+ const blocks = [];
121056
+ for (const approval of approvals) {
121057
+ const approve = byKey.get(`approval:${approval.id}:approve`);
121058
+ const reject = byKey.get(`approval:${approval.id}:reject`);
121059
+ const review = reviewById.get(approval.id);
121060
+ if (!review) continue;
121061
+ blocks.push(...slackToolReviewBlocks(review, privateAudience));
121062
+ if (approve && reject) {
121063
+ blocks.push({
121064
+ type: "actions",
121065
+ block_id: `opengeni_approval_${event.sequence}_${blocks.length}`,
121066
+ elements: [
121067
+ {
121068
+ type: "button",
121069
+ action_id: SLACK_ACTION_ID_BY_KIND.approval_approve,
121070
+ value: approve.id,
121071
+ text: { type: "plain_text", text: review.approveLabel.slice(0, 75), emoji: true },
121072
+ style: "primary"
121073
+ },
121074
+ {
121075
+ type: "button",
121076
+ action_id: SLACK_ACTION_ID_BY_KIND.approval_reject,
121077
+ value: reject.id,
121078
+ text: { type: "plain_text", text: "Decline", emoji: true }
121079
+ }
121080
+ ]
121081
+ });
121082
+ }
121083
+ }
121084
+ return {
121085
+ text: fallback,
121086
+ blocks: [
121087
+ {
121088
+ type: "section",
121089
+ text: { type: "mrkdwn", text: fallback }
121090
+ },
121091
+ ...blocks
121092
+ ],
121093
+ operationId
121094
+ };
121095
+ }
120383
121096
  async function slackSharedResultPublicationBlocks(deps, interaction, event, requesterAuthorized, messageOperationId) {
120384
121097
  if (!safePayloadText(event.payload, "output").trim()) return [];
120385
121098
  if (!requesterAuthorized || !interaction.sessionId || !interaction.initiatingSlackUserId) {
@@ -120677,15 +121390,42 @@ async function deliverSlackSessionEvents(deps, interaction, claimHolderId) {
120677
121390
  requester.mention,
120678
121391
  requester.authorized
120679
121392
  );
120680
- await postDelivery(
120681
- client,
120682
- interaction,
120683
- event,
120684
- card.text,
120685
- "approval",
120686
- card.operationId,
120687
- card.blocks
120688
- );
121393
+ try {
121394
+ await postDelivery(
121395
+ client,
121396
+ interaction,
121397
+ event,
121398
+ card.text,
121399
+ "approval",
121400
+ card.operationId,
121401
+ card.blocks
121402
+ );
121403
+ } catch (error) {
121404
+ if (!(error instanceof SlackBotOperationConflictError)) throw error;
121405
+ const prior = await getSlackBotPostOperation2(
121406
+ deps.db,
121407
+ interaction.workspaceId,
121408
+ interaction.connectionId,
121409
+ card.operationId
121410
+ );
121411
+ if (prior?.status === "completed") continue;
121412
+ const legacy = await legacySlackApprovalCard(
121413
+ deps,
121414
+ interaction,
121415
+ event,
121416
+ requester.mention,
121417
+ requester.authorized
121418
+ );
121419
+ await postDelivery(
121420
+ client,
121421
+ interaction,
121422
+ event,
121423
+ legacy.text,
121424
+ "approval",
121425
+ legacy.operationId,
121426
+ legacy.blocks
121427
+ );
121428
+ }
120689
121429
  } else if (event.type === "session.humanInput.requested") {
120690
121430
  const card = await slackHumanInputCard(
120691
121431
  deps,
@@ -120926,12 +121666,12 @@ async function enqueueNormalizedSlackInteraction(deps, route2, entry2) {
120926
121666
  async function readSignedSlackRequest(c, deps) {
120927
121667
  const signingSecret = deps.settings.slackSigningSecret;
120928
121668
  if (!signingSecret)
120929
- throw new HTTPException116(503, {
121669
+ throw new HTTPException117(503, {
120930
121670
  message: "Slack interactions are disabled"
120931
121671
  });
120932
121672
  const rawBytes = new Uint8Array(await c.req.raw.arrayBuffer());
120933
121673
  if (rawBytes.byteLength === 0 || rawBytes.byteLength > SLACK_INTERACTION_MAX_BODY_BYTES) {
120934
- throw new HTTPException116(rawBytes.byteLength > SLACK_INTERACTION_MAX_BODY_BYTES ? 413 : 400, {
121674
+ throw new HTTPException117(rawBytes.byteLength > SLACK_INTERACTION_MAX_BODY_BYTES ? 413 : 400, {
120935
121675
  message: "invalid Slack request body"
120936
121676
  });
120937
121677
  }
@@ -120944,7 +121684,7 @@ async function readSignedSlackRequest(c, deps) {
120944
121684
  },
120945
121685
  signingSecret
120946
121686
  )) {
120947
- throw new HTTPException116(401, { message: "invalid Slack signature" });
121687
+ throw new HTTPException117(401, { message: "invalid Slack signature" });
120948
121688
  }
120949
121689
  return { rawBody };
120950
121690
  }
@@ -120955,7 +121695,7 @@ function normalizedFormInteraction(form, triggerKind) {
120955
121695
  const triggerId = boundedString(form.get("trigger_id"), 256);
120956
121696
  const text = boundedText(form.get("text"));
120957
121697
  if (!teamId || !userId || !channelId || !triggerId || !text) {
120958
- throw new HTTPException116(400, { message: "invalid Slack command payload" });
121698
+ throw new HTTPException117(400, { message: "invalid Slack command payload" });
120959
121699
  }
120960
121700
  return {
120961
121701
  providerEventId: `command:${triggerId}`,
@@ -120998,7 +121738,7 @@ function normalizedBlockActionInteraction(payload) {
120998
121738
  const actionId = boundedString(action?.action_id, 128);
120999
121739
  const handleId = boundedString(action?.value, 64);
121000
121740
  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) {
121001
- throw new HTTPException116(400, { message: "invalid Slack block action" });
121741
+ throw new HTTPException117(400, { message: "invalid Slack block action" });
121002
121742
  }
121003
121743
  const identity2 = createHash33("sha256").update([teamId, userId, channelId, messageTs, actionTs, actionId, handleId].join("\n")).digest("hex");
121004
121744
  return {
@@ -121199,29 +121939,29 @@ function linkUrl(deps, entry2, mintedAtMs) {
121199
121939
  }
121200
121940
  async function requireManagedSlackLinkHuman(c, deps) {
121201
121941
  if (c.req.header("authorization")) {
121202
- throw new HTTPException116(401, { message: "managed browser sign-in required" });
121942
+ throw new HTTPException117(401, { message: "managed browser sign-in required" });
121203
121943
  }
121204
121944
  const context = await requireAccessContext14(c, deps);
121205
121945
  if (context.mode !== "managed" || !context.subjectId.startsWith("user:")) {
121206
- throw new HTTPException116(403, { message: "managed browser sign-in required" });
121946
+ throw new HTTPException117(403, { message: "managed browser sign-in required" });
121207
121947
  }
121208
121948
  return context;
121209
121949
  }
121210
121950
  function freshSlackLinkRequired() {
121211
- return new HTTPException116(400, {
121951
+ return new HTTPException117(400, {
121212
121952
  message: "This Slack link is invalid or expired. Request a fresh link from Slack."
121213
121953
  });
121214
121954
  }
121215
121955
  function slackLinkAccessHttpError(error) {
121216
- if (error instanceof HTTPException116) return error;
121956
+ if (error instanceof HTTPException117) return error;
121217
121957
  if (!(error instanceof SlackUserLinkAccessPersistenceError)) throw error;
121218
121958
  if (error.code === "version_conflict" || error.code === "idempotency_conflict") {
121219
- return new HTTPException116(409, {
121959
+ return new HTTPException117(409, {
121220
121960
  message: "The Slack access request changed. Refresh and try again."
121221
121961
  });
121222
121962
  }
121223
121963
  if (error.code === "state_conflict") {
121224
- return new HTTPException116(409, {
121964
+ return new HTTPException117(409, {
121225
121965
  message: "This Slack link is no longer pending. Request a fresh link from Slack."
121226
121966
  });
121227
121967
  }
@@ -121289,7 +122029,7 @@ function parseJsonObject(value) {
121289
122029
  if (result) return result;
121290
122030
  } catch {
121291
122031
  }
121292
- throw new HTTPException116(400, { message: "invalid Slack JSON payload" });
122032
+ throw new HTTPException117(400, { message: "invalid Slack JSON payload" });
121293
122033
  }
121294
122034
  function record5(value) {
121295
122035
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -121319,7 +122059,7 @@ function safeErrorCode(error) {
121319
122059
  if (error instanceof SlackBotProviderError) return error.code.slice(0, 128);
121320
122060
  if (error instanceof SlackInteractionPermanentError) return error.code.slice(0, 128);
121321
122061
  if (error instanceof SlackInteractionRetryableError) return error.code.slice(0, 128);
121322
- if (error instanceof HTTPException116) return `http_${error.status}`;
122062
+ if (error instanceof HTTPException117) return `http_${error.status}`;
121323
122063
  const raw = error instanceof Error ? error.name : "slack_interaction_error";
121324
122064
  return raw.toLowerCase().replace(/[^a-z0-9_-]/g, "_").slice(0, 128) || "error";
121325
122065
  }
@@ -121351,7 +122091,7 @@ var SlackInteractionRetryableError = class extends Error {
121351
122091
  }
121352
122092
  };
121353
122093
  function permanentSlackInteractionError(error) {
121354
- return error instanceof SlackInteractionPermanentError || error instanceof HTTPException116;
122094
+ return error instanceof SlackInteractionPermanentError || error instanceof HTTPException117;
121355
122095
  }
121356
122096
  var PERMANENT_SLACK_DELIVERY_CODES = /* @__PURE__ */ new Set([
121357
122097
  "account_inactive",
@@ -121462,7 +122202,7 @@ function createAppComposition(deps) {
121462
122202
  authoritySubjectId
121463
122203
  }) => {
121464
122204
  if (!objectStorage) {
121465
- throw new HTTPException117(503, {
122205
+ throw new HTTPException118(503, {
121466
122206
  message: "object storage is not configured"
121467
122207
  });
121468
122208
  }
@@ -121828,7 +122568,7 @@ function createAppComposition(deps) {
121828
122568
  } catch (error) {
121829
122569
  if (error instanceof ManagedAuthActorChangeError2) {
121830
122570
  c.header("x-opengeni-actor-state", "changed");
121831
- throw new HTTPException117(409, { message: error.code, cause: error });
122571
+ throw new HTTPException118(409, { message: error.code, cause: error });
121832
122572
  }
121833
122573
  if (error instanceof ManagedAuthActorLeaseOutcomeUnknownError) {
121834
122574
  throw new ApiHttpError(503, {
@@ -121918,7 +122658,7 @@ function createAppComposition(deps) {
121918
122658
  deps.settings.publicBaseUrl
121919
122659
  );
121920
122660
  if (!attempt || !authority || attempt.authorityHash !== managedAuthSha2563(authority)) {
121921
- throw new HTTPException117(409, { message: "provider_route_blocked" });
122661
+ throw new HTTPException118(409, { message: "provider_route_blocked" });
121922
122662
  }
121923
122663
  const isolated = await isolatedManagedAuthOAuthCallbackRequest(managedAuth, c.req.raw);
121924
122664
  preserveCookieNames = [isolated.stateCookieName];
@@ -122065,7 +122805,7 @@ function createAppComposition(deps) {
122065
122805
  let modelSelectionForbidden = false;
122066
122806
  const requestedWorkspaceId = c.req.query("workspaceId");
122067
122807
  if (requestedWorkspaceId !== void 0 && requestedWorkspaceId.trim() === "") {
122068
- throw new HTTPException117(422, { message: "workspaceId must not be empty" });
122808
+ throw new HTTPException118(422, { message: "workspaceId must not be empty" });
122069
122809
  }
122070
122810
  const callerScoped = requestedWorkspaceId !== void 0 || deps.settings.productAccessMode !== "managed" || c.req.header("authorization") !== void 0 || // Browser bootstrap precedes session-set reconciliation. A cookie alone
122071
122811
  // cannot scope this public read (broker/changed actors need an epoch).
@@ -122075,13 +122815,13 @@ function createAppComposition(deps) {
122075
122815
  try {
122076
122816
  context = await requireAccessContext15(c, routeDeps);
122077
122817
  } catch (error) {
122078
- if (!(error instanceof HTTPException117 && error.status === 401) || requestedWorkspaceId !== void 0 || c.req.header("authorization") !== void 0 || c.req.header("x-opengeni-actor-epoch") !== void 0 || c.req.header("x-opengeni-external-actor") !== void 0) {
122818
+ if (!(error instanceof HTTPException118 && error.status === 401) || requestedWorkspaceId !== void 0 || c.req.header("authorization") !== void 0 || c.req.header("x-opengeni-actor-epoch") !== void 0 || c.req.header("x-opengeni-external-actor") !== void 0) {
122079
122819
  throw error;
122080
122820
  }
122081
122821
  }
122082
122822
  const workspaceId = requestedWorkspaceId ?? context?.defaultWorkspaceId;
122083
122823
  if (context && workspaceId) {
122084
- const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
122824
+ const grant = await requireAccessGrant54(c, routeDeps, workspaceId);
122085
122825
  await withAccessGrantSessionRlsContext(routeDeps, grant, async () => {
122086
122826
  catalogSettings = (await resolveWorkspaceCatalogSettings10(deps.db, deps.settings, {
122087
122827
  accountId: grant.accountId,
@@ -122196,7 +122936,7 @@ function createAppComposition(deps) {
122196
122936
  boundedRequest = await boundedMcpRequest(c.req.raw);
122197
122937
  } catch (error) {
122198
122938
  if (error instanceof McpPayloadTooLargeError) {
122199
- throw new HTTPException117(413, { message: "MCP request body exceeds the safety limit" });
122939
+ throw new HTTPException118(413, { message: "MCP request body exceeds the safety limit" });
122200
122940
  }
122201
122941
  throw error;
122202
122942
  }
@@ -122219,7 +122959,7 @@ function createAppComposition(deps) {
122219
122959
  const context = await requireAccessContext15(c, routeDeps);
122220
122960
  const authorization = c.req.header("authorization");
122221
122961
  if (!accountScopedApiKeyWorkspaceAuthority10(context) || !authorization) {
122222
- throw new HTTPException117(403, {
122962
+ throw new HTTPException118(403, {
122223
122963
  message: "Connect with Opengeni sign-in or an organization API key."
122224
122964
  });
122225
122965
  }
@@ -122230,13 +122970,32 @@ function createAppComposition(deps) {
122230
122970
  };
122231
122971
  }
122232
122972
  } catch (error) {
122233
- if (error instanceof HTTPException117 && error.status === 401) challenge();
122973
+ if (error instanceof HTTPException118 && error.status === 401) challenge();
122234
122974
  throw error;
122235
122975
  }
122976
+ if (c.req.method === "GET") {
122977
+ const version2 = c.req.header("mcp-protocol-version");
122978
+ if (version2 && !SUPPORTED_PROTOCOL_VERSIONS.includes(version2)) {
122979
+ return c.json(
122980
+ {
122981
+ jsonrpc: "2.0",
122982
+ id: null,
122983
+ error: { code: -32e3, message: "Unsupported protocol version." }
122984
+ },
122985
+ 400
122986
+ );
122987
+ }
122988
+ return c.json(
122989
+ { jsonrpc: "2.0", id: null, error: { code: -32e3, message: "Method not allowed." } },
122990
+ 405,
122991
+ { allow: "POST" }
122992
+ );
122993
+ }
122236
122994
  const transport = new WebStandardStreamableHTTPServerTransport3({ enableJsonResponse: true });
122237
122995
  const mcp = buildOrganizationMcpServer({
122238
122996
  caller,
122239
122997
  origin: new URL(c.req.url).origin,
122998
+ publicOrigin: canonicalPublicOrigin3(deps.settings.publicBaseUrl),
122240
122999
  dispatch: async (request) => await app.fetch(request, c.env),
122241
123000
  signal: c.req.raw.signal
122242
123001
  });
@@ -122250,7 +123009,7 @@ function createAppComposition(deps) {
122250
123009
  app.use("/v1/workspaces/:workspaceId/*", async (c, next) => {
122251
123010
  const workspaceId = c.req.param("workspaceId");
122252
123011
  if (workspaceRequestRequiresCodexAccountPrevalidation(c.req.raw)) {
122253
- const grant2 = await requireAccessGrant53(c, routeDeps, workspaceId);
123012
+ const grant2 = await requireAccessGrant54(c, routeDeps, workspaceId);
122254
123013
  await validateCodexAccountTarget(c.req.raw);
122255
123014
  await withAccessGrantSessionRlsContext(routeDeps, grant2, next);
122256
123015
  return;
@@ -122260,7 +123019,7 @@ function createAppComposition(deps) {
122260
123019
  return;
122261
123020
  }
122262
123021
  const permission = c.req.method === "PUT" && /^\/v1\/workspaces\/[^/]+\/artifact-catalog\/[^/]+\/[^/]+\/pin$/.test(c.req.path) ? "artifacts:publish" : void 0;
122263
- const grant = await requireAccessGrant53(c, routeDeps, workspaceId, permission);
123022
+ const grant = await requireAccessGrant54(c, routeDeps, workspaceId, permission);
122264
123023
  await withAccessGrantSessionRlsContext(routeDeps, grant, next);
122265
123024
  });
122266
123025
  app.all("/v1/workspaces/:workspaceId/mcp", async (c) => {
@@ -122288,7 +123047,7 @@ function createAppComposition(deps) {
122288
123047
  boundedRequest = await boundedMcpRequest(c.req.raw);
122289
123048
  } catch (error) {
122290
123049
  if (error instanceof McpPayloadTooLargeError) {
122291
- throw new HTTPException117(413, {
123050
+ throw new HTTPException118(413, {
122292
123051
  message: "MCP request body exceeds the safety limit"
122293
123052
  });
122294
123053
  }
@@ -122298,7 +123057,7 @@ function createAppComposition(deps) {
122298
123057
  try {
122299
123058
  oauthAccess = await resolveMcpOAuthRouteAccess(routeDeps, c.req.raw, workspaceId);
122300
123059
  } catch (error) {
122301
- if (error instanceof HTTPException117 && error.status === 401) {
123060
+ if (error instanceof HTTPException118 && error.status === 401) {
122302
123061
  c.header(
122303
123062
  "www-authenticate",
122304
123063
  mcpOAuthAuthenticateHeader(routeDeps, new URL(c.req.url).pathname)
@@ -122334,7 +123093,7 @@ function createAppComposition(deps) {
122334
123093
  try {
122335
123094
  authorization = await requireMcpAccessGrantAuthorization(c, routeDeps, workspaceId);
122336
123095
  } catch (error) {
122337
- if (deps.settings.mcpOauthEnabled && error instanceof HTTPException117 && error.status === 401) {
123096
+ if (deps.settings.mcpOauthEnabled && error instanceof HTTPException118 && error.status === 401) {
122338
123097
  c.header(
122339
123098
  "www-authenticate",
122340
123099
  mcpOAuthAuthenticateHeader(routeDeps, new URL(c.req.url).pathname)
@@ -122355,10 +123114,10 @@ function createAppComposition(deps) {
122355
123114
  });
122356
123115
  } catch (error) {
122357
123116
  if (error instanceof SessionAuthorizationDeniedError18) {
122358
- throw new HTTPException117(404, { message: "session not found" });
123117
+ throw new HTTPException118(404, { message: "session not found" });
122359
123118
  }
122360
123119
  if (error instanceof SessionAuthorizationUnavailableError16) {
122361
- throw new HTTPException117(503, {
123120
+ throw new HTTPException118(503, {
122362
123121
  message: "session authorization is unavailable"
122363
123122
  });
122364
123123
  }
@@ -122440,7 +123199,7 @@ function createAppComposition(deps) {
122440
123199
  const grant = authorization.grant;
122441
123200
  const parsed = ToolGatewayCallRequest2.safeParse(await c.req.json().catch(() => null));
122442
123201
  if (!parsed.success) {
122443
- throw new HTTPException117(400, { message: "Invalid tool gateway call" });
123202
+ throw new HTTPException118(400, { message: "Invalid tool gateway call" });
122444
123203
  }
122445
123204
  const prepared = await prepareWorkspaceToolGateway(routeDeps, authorization);
122446
123205
  try {
@@ -122468,7 +123227,7 @@ function createAppComposition(deps) {
122468
123227
  );
122469
123228
  const parsed = ToolGatewayApprovalRequest2.safeParse(await c.req.json().catch(() => null));
122470
123229
  if (!parsed.success) {
122471
- throw new HTTPException117(400, { message: "Invalid tool gateway approval" });
123230
+ throw new HTTPException118(400, { message: "Invalid tool gateway approval" });
122472
123231
  }
122473
123232
  const prepared = await prepareWorkspaceToolGateway(routeDeps, authorization);
122474
123233
  try {
@@ -122505,7 +123264,7 @@ function createAppComposition(deps) {
122505
123264
  });
122506
123265
  app.all("/v1/workspaces/:workspaceId/codemode/sdk/*", async (c) => {
122507
123266
  const workspaceId = c.req.param("workspaceId");
122508
- const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
123267
+ const grant = await requireAccessGrant54(c, routeDeps, workspaceId);
122509
123268
  const url = new URL(c.req.url);
122510
123269
  const prefix2 = `/v1/workspaces/${workspaceId}/codemode/sdk`;
122511
123270
  let forwarded;
@@ -122523,9 +123282,9 @@ function createAppComposition(deps) {
122523
123282
  });
122524
123283
  app.get("/v1/workspaces/:workspaceId/codemode/catalog", async (c) => {
122525
123284
  const workspaceId = c.req.param("workspaceId");
122526
- const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
123285
+ const grant = await requireAccessGrant54(c, routeDeps, workspaceId);
122527
123286
  if (!isCodemodeGrant(grant)) {
122528
- throw new HTTPException117(403, { message: "Codemode access denied" });
123287
+ throw new HTTPException118(403, { message: "Codemode access denied" });
122529
123288
  }
122530
123289
  try {
122531
123290
  return c.json((await requireActiveCodemodeCatalog(routeDeps, grant)).catalog);
@@ -122535,13 +123294,13 @@ function createAppComposition(deps) {
122535
123294
  });
122536
123295
  app.post("/v1/workspaces/:workspaceId/codemode/calls", async (c) => {
122537
123296
  const workspaceId = c.req.param("workspaceId");
122538
- const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
123297
+ const grant = await requireAccessGrant54(c, routeDeps, workspaceId);
122539
123298
  if (!isCodemodeGrant(grant)) {
122540
- throw new HTTPException117(403, { message: "Codemode access denied" });
123299
+ throw new HTTPException118(403, { message: "Codemode access denied" });
122541
123300
  }
122542
123301
  const parsed = CodemodeCallRequest2.safeParse(await c.req.json().catch(() => null));
122543
123302
  if (!parsed.success) {
122544
- throw new HTTPException117(400, { message: "Invalid Codemode call" });
123303
+ throw new HTTPException118(400, { message: "Invalid Codemode call" });
122545
123304
  }
122546
123305
  try {
122547
123306
  const submission = await submitAndDispatchCodemodeCall(routeDeps, grant, parsed.data);
@@ -122555,19 +123314,21 @@ function createAppComposition(deps) {
122555
123314
  });
122556
123315
  app.get("/v1/workspaces/:workspaceId/codemode/calls/:operationId", async (c) => {
122557
123316
  const workspaceId = c.req.param("workspaceId");
122558
- const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
123317
+ const grant = await requireAccessGrant54(c, routeDeps, workspaceId);
122559
123318
  if (!isCodemodeGrant(grant)) {
122560
- throw new HTTPException117(403, { message: "Codemode access denied" });
123319
+ throw new HTTPException118(403, { message: "Codemode access denied" });
122561
123320
  }
122562
123321
  try {
122563
- const operation = await readCodemodeOperation(routeDeps, grant, c.req.param("operationId"));
123322
+ const operation = await readCodemodeOperation(routeDeps, grant, c.req.param("operationId"), {
123323
+ durableApproval: c.req.header("x-opengeni-codemode-capabilities") === "durable-approval-v1"
123324
+ });
122564
123325
  if (!operation)
122565
- throw new HTTPException117(404, {
123326
+ throw new HTTPException118(404, {
122566
123327
  message: "Codemode operation not found"
122567
123328
  });
122568
123329
  return c.json(operation);
122569
123330
  } catch (error) {
122570
- if (error instanceof HTTPException117) throw error;
123331
+ if (error instanceof HTTPException118) throw error;
122571
123332
  throw codemodeHttpError(error);
122572
123333
  }
122573
123334
  });
@@ -122595,6 +123356,7 @@ function createAppComposition(deps) {
122595
123356
  registerCompanyProfileRoutes(app, routeDeps);
122596
123357
  registerCompanyBrainRoutes(app, routeDeps);
122597
123358
  registerSlackTaskPolicyRoutes(app, routeDeps);
123359
+ registerSlackBotAccessRoutes(app, routeDeps);
122598
123360
  registerWorkspaceStateRoutes(app, routeDeps);
122599
123361
  registerMemorySlackPublicationRoutes(app, routeDeps);
122600
123362
  registerWorkspaceArtifactRoutes(app, routeDeps);
@@ -122666,7 +123428,7 @@ function createAppComposition(deps) {
122666
123428
  }
122667
123429
  const importErrorResponse = archivedSessionImportErrorResponse(c, rawError);
122668
123430
  if (importErrorResponse) return importErrorResponse;
122669
- const error = rawError instanceof OrganizationIntegrationDeniedError4 ? new HTTPException117(403, { message: rawError.message }) : allowanceExhaustedHttpError(rawError) ?? workspaceControlBusyHttpError(rawError) ?? scheduledTaskTargetAccessHttpError(rawError) ?? agentConfigHttpError(rawError) ?? modelUnavailableHttpError(rawError) ?? (rawError instanceof UnsupportedLatencyModeError2 ? new ApiHttpError(422, {
123431
+ const error = rawError instanceof OrganizationIntegrationDeniedError4 ? new HTTPException118(403, { message: rawError.message }) : allowanceExhaustedHttpError(rawError) ?? workspaceControlBusyHttpError(rawError) ?? scheduledTaskTargetAccessHttpError(rawError) ?? agentConfigHttpError(rawError) ?? modelUnavailableHttpError(rawError) ?? (rawError instanceof UnsupportedLatencyModeError2 ? new ApiHttpError(422, {
122670
123432
  code: "validation_failed",
122671
123433
  message: rawError.message,
122672
123434
  retryable: false,
@@ -122677,12 +123439,15 @@ function createAppComposition(deps) {
122677
123439
  latencyMode: rawError.latencyMode,
122678
123440
  allowedLatencyModes: [...rawError.allowedLatencyModes]
122679
123441
  }
122680
- }) : null) ?? requestBodyValidationHttpError(rawError) ?? invalidPathIdentifierHttpError(rawError, new URL(c.req.url).pathname) ?? rawError;
123442
+ }) : null) ?? requestBodyValidationHttpError(rawError) ?? invalidPathIdentifierHttpError(rawError, new URL(c.req.url).pathname) ?? databaseUnavailableHttpError(rawError, c.req.method) ?? rawError;
122681
123443
  const compactionLock = codexCompactionV2ProviderLockedError(error);
122682
123444
  const apiError = error instanceof ApiHttpError ? error : null;
122683
123445
  const status = compactionLock ? 422 : httpStatusForError(error);
122684
123446
  const code = compactionLock ? compactionLock.code : apiError?.code ?? errorCodeForStatus(status);
122685
123447
  if (status >= 500) logHttpFailure(c, status, code, rawError);
123448
+ if (apiError?.details?.code === DATABASE_UNAVAILABLE_ERROR_DETAIL_CODE) {
123449
+ c.header("retry-after", "1");
123450
+ }
122686
123451
  const envelope = ErrorEnvelope.parse({
122687
123452
  error: {
122688
123453
  status,
@@ -122704,7 +123469,7 @@ function managedAuthOAuthCallbackProvider(pathname) {
122704
123469
  }
122705
123470
  function mutationOutcomeUnknown(error, method) {
122706
123471
  if (method === "GET" || method === "HEAD" || method === "OPTIONS") return false;
122707
- const cause = error instanceof HTTPException117 ? error.cause : error;
123472
+ const cause = error instanceof HTTPException118 ? error.cause : error;
122708
123473
  return cause instanceof BrowserControlTransportError5 || cause instanceof BrowserControlProtocolError3;
122709
123474
  }
122710
123475
  function appendVary(current, value) {
@@ -122740,7 +123505,7 @@ function workspaceRequestRequiresCodexAccountPrevalidation(request) {
122740
123505
  async function validateCodexAccountTarget(request) {
122741
123506
  const body3 = await request.clone().json().catch(() => null);
122742
123507
  if (typeof body3?.target !== "string" || body3.target.length === 0) {
122743
- throw new HTTPException117(400, {
123508
+ throw new HTTPException118(400, {
122744
123509
  message: 'target is required ("auto" or an account id)'
122745
123510
  });
122746
123511
  }
@@ -122895,23 +123660,23 @@ function assertConfiguredCodemodeSessionProxyPath(session, path, method) {
122895
123660
  }
122896
123661
  }
122897
123662
  if (!allowed2) {
122898
- throw new HTTPException117(403, {
123663
+ throw new HTTPException118(403, {
122899
123664
  message: "Agent configuration does not allow this Codemode SDK operation"
122900
123665
  });
122901
123666
  }
122902
123667
  }
122903
123668
  function codemodeHttpError(error) {
122904
123669
  if (error instanceof SiteSessionPathError) {
122905
- return new HTTPException117(404, { message: error.message, cause: error });
123670
+ return new HTTPException118(404, { message: error.message, cause: error });
122906
123671
  }
122907
123672
  if (error instanceof SessionAuthorizationDeniedError18) {
122908
- return new HTTPException117(404, {
123673
+ return new HTTPException118(404, {
122909
123674
  message: "session not found",
122910
123675
  cause: error
122911
123676
  });
122912
123677
  }
122913
123678
  if (error instanceof SessionAuthorizationUnavailableError16) {
122914
- return new HTTPException117(503, {
123679
+ return new HTTPException118(503, {
122915
123680
  message: "session authorization is unavailable",
122916
123681
  cause: error
122917
123682
  });
@@ -122944,21 +123709,24 @@ function codemodeHttpError(error) {
122944
123709
  });
122945
123710
  }
122946
123711
  if (error instanceof CodemodeOperationNotExecutableError) {
122947
- return new HTTPException117(409, { message: error.message, cause: error });
123712
+ return new HTTPException118(409, { message: error.message, cause: error });
122948
123713
  }
122949
123714
  if (error instanceof CodemodeOperationConflictError) {
122950
- return new HTTPException117(409, { message: error.message, cause: error });
123715
+ return new HTTPException118(409, { message: error.message, cause: error });
122951
123716
  }
122952
123717
  if (error instanceof CodemodeToolNotInCatalogError) {
122953
- return new HTTPException117(404, { message: error.message, cause: error });
123718
+ return new HTTPException118(404, { message: error.message, cause: error });
122954
123719
  }
122955
- if (error instanceof CodemodeToolApprovalRequiredError) {
122956
- return new HTTPException117(409, { message: error.message, cause: error });
123720
+ if (error instanceof CodemodeToolApprovalRequiredError2) {
123721
+ return new HTTPException118(409, { message: error.message, cause: error });
122957
123722
  }
122958
123723
  if (error instanceof CodemodePayloadTooLargeError) {
122959
- return new HTTPException117(413, { message: error.message, cause: error });
123724
+ return new HTTPException118(413, { message: error.message, cause: error });
123725
+ }
123726
+ if (error instanceof CodemodeOperationLimitError) {
123727
+ return new HTTPException118(429, { message: error.message, cause: error });
122960
123728
  }
122961
- return error instanceof HTTPException117 ? error : new HTTPException117(500, {
123729
+ return error instanceof HTTPException118 ? error : new HTTPException118(500, {
122962
123730
  message: "Codemode request failed",
122963
123731
  cause: error
122964
123732
  });
@@ -122993,7 +123761,7 @@ function structuredServicesHint(backend) {
122993
123761
  }
122994
123762
  function codexCompactionV2ProviderLockedError(error) {
122995
123763
  if (error instanceof CodexCompactionV2ProviderLockedError) return error;
122996
- if (error instanceof HTTPException117 && error.cause instanceof CodexCompactionV2ProviderLockedError) {
123764
+ if (error instanceof HTTPException118 && error.cause instanceof CodexCompactionV2ProviderLockedError) {
122997
123765
  return error.cause;
122998
123766
  }
122999
123767
  return null;
@@ -123010,7 +123778,7 @@ function httpStatusForError(error) {
123010
123778
  if (isRequestBodyValidationError(error)) {
123011
123779
  return 400;
123012
123780
  }
123013
- if (error instanceof HTTPException117) {
123781
+ if (error instanceof HTTPException118) {
123014
123782
  return error.status;
123015
123783
  }
123016
123784
  if (error instanceof McpPayloadTooLargeError) {
@@ -123042,7 +123810,7 @@ function publicErrorMessage(error, status) {
123042
123810
  if (status >= 500) {
123043
123811
  return "Opengeni could not complete the request.";
123044
123812
  }
123045
- if (error instanceof HTTPException117) {
123813
+ if (error instanceof HTTPException118) {
123046
123814
  return boundedPublicMessage(error.message) ?? "Request failed.";
123047
123815
  }
123048
123816
  if (error instanceof McpPayloadTooLargeError) {
@@ -124137,4 +124905,4 @@ export {
124137
124905
  withDefaultEnabledCapabilityMcpTools,
124138
124906
  workflowIdForSession4 as workflowIdForSession
124139
124907
  };
124140
- //# sourceMappingURL=chunk-Q77DZ2M4.js.map
124908
+ //# sourceMappingURL=chunk-IZEGLLKZ.js.map