@opengeni/api-router 4.3.3-canary.39786771332001 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/app.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  withDefaultEnabledCapabilityMcpTools,
29
29
  workflowIdForSession,
30
30
  workspaceActorContextExempt
31
- } from "./chunk-S5VJ3GVR.js";
31
+ } from "./chunk-LXUAR3HS.js";
32
32
  import "./chunk-OGQDXVXY.js";
33
33
  export {
34
34
  API_MAX_REQUEST_BODY_BYTES,
@@ -6515,44 +6515,30 @@ function capabilityAccountReadiness(availableRoutes, bindings) {
6515
6515
  return { available, accepted };
6516
6516
  }
6517
6517
 
6518
- // src/mcp/machine-permission-card.ts
6519
- var MACHINE_PERMISSION_REQUIRED_CODE = "personal_machine_permission_required";
6520
- function machinePermissionCardShownReason(chatUrl) {
6521
- const elsewhere = chatUrl ? `If they are reading this chat somewhere else (for example Slack, or this is a worker or scheduled chat), give them this link to allow it: ${chatUrl}. ` : "If they are reading this chat somewhere else (for example Slack, or this is a worker or scheduled chat), tell them to open this chat in Opengeni to allow it. ";
6522
- return "This is a personal (Only me) machine, and its owner has not allowed this chat to use it yet. A card where the owner can allow it is now posted in this chat in Opengeni. If they are reading this chat in Opengeni, tell them in one short sentence that they can allow it there; do not describe buttons or steps. " + elsewhere + "Do not wait for it and do not retry: when they allow it, their message continues this chat.";
6523
- }
6524
- async function withMachinePermissionCard(result2, post) {
6525
- if (result2.code !== MACHINE_PERMISSION_REQUIRED_CODE) return result2;
6518
+ // src/mcp/user-setup-card.ts
6519
+ import {
6520
+ userSetupBlocker,
6521
+ userSetupCardShownMessage
6522
+ } from "@opengeni/contracts";
6523
+ import { sessionChatUrl } from "@opengeni/contracts";
6524
+ async function withUserSetupCard(result2, post) {
6525
+ const blocker = userSetupBlocker(result2.code);
6526
+ if (!blocker) return result2;
6526
6527
  let posted;
6527
6528
  try {
6528
- posted = await post();
6529
+ posted = await post(blocker);
6529
6530
  } catch {
6530
6531
  return result2;
6531
6532
  }
6532
6533
  if (!posted) return result2;
6533
6534
  return {
6534
6535
  ...result2,
6535
- reason: machinePermissionCardShownReason(posted.chatUrl),
6536
+ reason: userSetupCardShownMessage(blocker, posted.chatUrl),
6536
6537
  card: "shown",
6537
6538
  cardEventId: posted.eventId,
6538
6539
  ...posted.chatUrl ? { chatUrl: posted.chatUrl } : {}
6539
6540
  };
6540
6541
  }
6541
- function sessionChatUrl(base, workspaceId, sessionId) {
6542
- if (!base) return null;
6543
- try {
6544
- const url = new URL(
6545
- `/workspaces/${encodeURIComponent(workspaceId)}/sessions/${encodeURIComponent(sessionId)}`,
6546
- base
6547
- );
6548
- if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password) {
6549
- return null;
6550
- }
6551
- return url.toString();
6552
- } catch {
6553
- return null;
6554
- }
6555
- }
6556
6542
 
6557
6543
  // src/site-uploads.ts
6558
6544
  import { WorkspaceArtifactSourceBundle as WorkspaceArtifactSourceBundle2 } from "@opengeni/contracts";
@@ -16607,8 +16593,9 @@ import {
16607
16593
  buildCapabilityCatalog,
16608
16594
  CONNECTED_MACHINE_CAPABILITY_ID,
16609
16595
  EXCEL_ADDIN_CAPABILITY_ID as EXCEL_ADDIN_CAPABILITY_ID2,
16596
+ browserExtension,
16610
16597
  nativeConnectionCapabilityRecommendations,
16611
- OPENGENI_BROWSER_EXTENSION_URL,
16598
+ userSetupCardPayload,
16612
16599
  requireLiveAgentAttemptAuthorization as requireLiveAgentAttemptAuthorization7,
16613
16600
  requireSessionAuthorization as requireSessionAuthorization3,
16614
16601
  requireSessionAuthorizationListScope,
@@ -19560,7 +19547,9 @@ function sandboxArtifactRelativePath(value, workspaceRoot = "/workspace") {
19560
19547
  }
19561
19548
  if (!relative) {
19562
19549
  throw new HTTPException24(400, {
19563
- message: "sandbox artifact path must name a file inside the active workspace root"
19550
+ // Say which root: on a connected machine the file may exist outside it,
19551
+ // and copying it in is the way forward.
19552
+ message: `sandbox artifact path must name a file inside this session's workspace root ${workspaceRoot}; ${path} is outside it, so copy the file into the workspace first and use that path`
19564
19553
  });
19565
19554
  }
19566
19555
  if (absolute.length > SANDBOX_ARTIFACT_ABSOLUTE_PATH_MAX_CHARS) {
@@ -42341,6 +42330,15 @@ var ACTION_CATALOG = [
42341
42330
  "PersonalGitHubOAuthStartResponse"
42342
42331
  ]
42343
42332
  },
42333
+ {
42334
+ "id": "recordSiteOpened",
42335
+ "method": "POST",
42336
+ "path": "/v1/workspaces/:workspaceId/published-artifacts/:artifactId/opened",
42337
+ "request": [],
42338
+ "response": [
42339
+ "SiteOpenedResponse"
42340
+ ]
42341
+ },
42344
42342
  {
42345
42343
  "id": "recoverSandbox",
42346
42344
  "method": "POST",
@@ -46956,7 +46954,7 @@ function registerSandboxFileArtifactTool(server, deps, grant, sessionId, json2)
46956
46954
  server.registerTool(
46957
46955
  "sandbox_file_publish",
46958
46956
  {
46959
- description: "Publish one exact file from this session's /workspace into durable workspace storage. Use this before presenting a video, ZIP, CSV, JSON, Markdown, HTML, PDF, Office file, or other sandbox output as downloadable. Present the returned artifact.artifactId as [Open file](artifact:<artifactId>), or ![Preview](artifact:<artifactId>) for inline image/video/audio/PDF previews. Replace <artifactId> with the exact returned ID and use a descriptive label. Never expose a sandbox: URL as the durable result. The artifactId is also the file's workspace File ID, so tools that take workspace file IDs, such as a browser upload action, accept it. A new file gets this chat's visibility (Only me in an Only me chat, otherwise the workspace) unless you pass visibility; to change an existing file, such as a generated image, pass its artifactId and visibility instead of path. Check the result's access.whoCanOpen before sharing a link with anyone.",
46957
+ description: "Publish one file from this session's workspace so the person can open it. Use it for every file you hand over: document, spreadsheet, PDF, image, video, archive or data. Link the result as [Descriptive label](artifact:<artifactId>), or ![Descriptive label](artifact:<artifactId>) to preview an image, video, audio or PDF inline; never give a sandbox: link instead. The artifactId is also the file's workspace file ID, which tools such as a browser upload accept. A new file gets this chat's visibility unless you pass visibility. To change who can open an existing file, such as a generated image, pass its artifactId and visibility instead of path. Check access.whoCanOpen in the result before sharing the link with anyone else.",
46960
46958
  inputSchema: {
46961
46959
  path: z47.string().min(1).max(4096).optional(),
46962
46960
  artifactId: z47.string().uuid().optional(),
@@ -48581,13 +48579,12 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
48581
48579
  ...deps.settings.ogtoolPackageSpec ? { OPENGENI_OGTOOL_PACKAGE_SPEC: deps.settings.ogtoolPackageSpec } : {}
48582
48580
  };
48583
48581
  };
48584
- const machinePermissionCard = (toolName, target) => async () => {
48582
+ const userSetupCard = (toolName, target) => async (blocker) => {
48585
48583
  const requestedSandboxId = UUID_TARGET.test(target) ? target.toLowerCase() : null;
48586
48584
  if (!exactAgentAttemptClaims(grant)) return null;
48587
48585
  const claims = exactAgentCommandContext(grant, sessionId);
48588
- const item = nativeConnectionCapabilityRecommendations({ connectedMachines: true }).find(
48589
- (candidate) => candidate.id === CONNECTED_MACHINE_CAPABILITY_ID
48590
- );
48586
+ const payload = userSetupCardPayload(blocker, { toolName, requestedSandboxId });
48587
+ if (!payload) return null;
48591
48588
  const appended = await appendTurnCapabilityCardOnce(
48592
48589
  deps.db,
48593
48590
  grant.workspaceId,
@@ -48595,23 +48592,13 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
48595
48592
  claims.callerTurnId,
48596
48593
  claims.callerExecutionGeneration,
48597
48594
  claims.callerAttemptId,
48598
- item.id,
48599
- {
48600
- type: "tool.auth_needed",
48601
- payload: {
48602
- ...capabilityCardPayload(item, {
48603
- action: "connect",
48604
- rationale: "Allow this chat to use your computer.",
48605
- toolName
48606
- }),
48607
- ...requestedSandboxId ? { requestedSandboxId } : {}
48608
- }
48609
- },
48595
+ blocker.capabilityId,
48596
+ { type: "tool.auth_needed", payload },
48610
48597
  requestedSandboxId
48611
48598
  );
48612
48599
  await publishDurableSessionEvents(deps.bus, grant.workspaceId, sessionId, appended.events);
48613
48600
  if (!appended.accepted) {
48614
- throw new Error("The calling turn was replaced before the machine card committed.");
48601
+ throw new Error("The calling turn was replaced before the setup card committed.");
48615
48602
  }
48616
48603
  return {
48617
48604
  eventId: appended.existingEventId ?? appended.events[0]?.id ?? null,
@@ -48640,9 +48627,9 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
48640
48627
  inputSchema: { target: z47.string().min(1) }
48641
48628
  },
48642
48629
  async ({ target }) => json2(
48643
- await withMachinePermissionCard(
48630
+ await withUserSetupCard(
48644
48631
  await swapActiveSandbox(services, await fleetContext(), target),
48645
- machinePermissionCard("sandbox_attach", target)
48632
+ userSetupCard("sandbox_attach", target)
48646
48633
  )
48647
48634
  )
48648
48635
  );
@@ -48653,9 +48640,9 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
48653
48640
  inputSchema: { target: z47.string().min(1) }
48654
48641
  },
48655
48642
  async ({ target }) => json2(
48656
- await withMachinePermissionCard(
48643
+ await withUserSetupCard(
48657
48644
  await swapActiveSandbox(services, await fleetContext(), target),
48658
- machinePermissionCard("sandbox_swap", target)
48645
+ userSetupCard("sandbox_swap", target)
48659
48646
  )
48660
48647
  )
48661
48648
  );
@@ -48690,12 +48677,12 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
48690
48677
  (subjectId) => vaultProviderRegistryFor(deps, { workspaceId: grant.workspaceId, subjectId })
48691
48678
  ) : null;
48692
48679
  return json2(
48693
- await withMachinePermissionCard(
48680
+ await withUserSetupCard(
48694
48681
  await runOnSandbox(services, await fleetContext(), target, typedOp, {
48695
48682
  ...transientExecEnvironment ? { transientExecEnvironment } : {},
48696
48683
  ...secretResolver ? { secretResolver } : {}
48697
48684
  }),
48698
- machinePermissionCard("run_on", target)
48685
+ userSetupCard("run_on", target)
48699
48686
  )
48700
48687
  );
48701
48688
  }
@@ -48712,9 +48699,10 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
48712
48699
  async ({ kind, name }) => {
48713
48700
  if (kind === "selfhosted" && exactAgentAttemptClaims(grant)) {
48714
48701
  await requireLiveAgentAttemptAuthorization7(deps.db, grant, sessionId);
48715
- const item = nativeConnectionCapabilityRecommendations({ connectedMachines: true }).find(
48716
- (candidate) => candidate.id === CONNECTED_MACHINE_CAPABILITY_ID
48717
- );
48702
+ const item = nativeConnectionCapabilityRecommendations({
48703
+ connectedMachines: true,
48704
+ browserExtension: browserExtension(deps.settings)
48705
+ }).find((candidate) => candidate.id === CONNECTED_MACHINE_CAPABILITY_ID);
48718
48706
  const setup = await capabilitySetupProjection(
48719
48707
  deps,
48720
48708
  grant.workspaceId,
@@ -48738,7 +48726,7 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
48738
48726
  kind: "selfhosted",
48739
48727
  status: "card_posted",
48740
48728
  eventId,
48741
- message: `${setup.status === "ready" ? `${setup.detail} The Connected Machine card lists them, so the person can choose one for this chat or connect another.` : "The Connected Machine card was posted. It shows the person what to do, so do not describe its buttons or steps; nothing is connected yet."} Check sandboxes_list after they confirm. Chrome needs the browser extension (${OPENGENI_BROWSER_EXTENSION_URL}) on the connected machine.`
48729
+ message: `${setup.status === "ready" ? `${setup.detail} The Connected Machine card lists them, so the person can choose one for this chat or connect another.` : "The Connected Machine card was posted. It shows the person what to do, so do not describe its buttons or steps; nothing is connected yet."} Check sandboxes_list after they confirm. Chrome needs the browser extension (${browserExtension(deps.settings).installUrl}) on the connected machine.`
48742
48730
  });
48743
48731
  }
48744
48732
  return json2(
@@ -48754,7 +48742,7 @@ function registerConnectedMachineTools(server, deps, grant, sessionId, json2) {
48754
48742
  server.registerTool(
48755
48743
  "connected_machine_enroll_token",
48756
48744
  {
48757
- description: "Create a single-use Connected Machine enrollment token for this workspace (expires in one hour) using existing enrollments:manage authority. Returns the token, expiry and Unix/PowerShell install commands. Use it when you can run commands on the target machine yourself (run_on, SSH, a VM you control): run the matching command there, then verify the machine with sandboxes_list. One token connects one machine; mint another for each additional machine (the same machine may rerun its command). When the person must run it on their own computer, post the Connected Machine card instead (capability_authorization_request with api:connected-machine), which mints the command in their browser; do not paste a token into the chat. Screen control is optional and defaults off; to turn it on for a machine that is already connected, use connected_machine_enable_screen_control instead of reconnecting. The token grants whole-machine access on enrollment; do not put it in public code or unrelated logs. Existing machine connections are preserved. For Chrome, the person installs the Opengeni Browser extension (" + OPENGENI_BROWSER_EXTENSION_URL + ") after the machine is connected.",
48745
+ description: "Create a single-use Connected Machine enrollment token for this workspace (expires in one hour) using existing enrollments:manage authority. Returns the token, expiry and Unix/PowerShell install commands. Use it when you can run commands on the target machine yourself (run_on, SSH, a VM you control): run the matching command there, then verify the machine with sandboxes_list. One token connects one machine; mint another for each additional machine (the same machine may rerun its command). When the person must run it on their own computer, post the Connected Machine card instead (capability_authorization_request with api:connected-machine), which mints the command in their browser; do not paste a token into the chat. Screen control is optional and defaults off; to turn it on for a machine that is already connected, use connected_machine_enable_screen_control instead of reconnecting. The token grants whole-machine access on enrollment; do not put it in public code or unrelated logs. Existing machine connections are preserved. For Chrome, the person installs the Opengeni Browser extension (" + browserExtension(deps.settings).installUrl + ") after the machine is connected.",
48758
48746
  inputSchema: { allowScreenControl: z47.boolean().optional() }
48759
48747
  },
48760
48748
  async ({ allowScreenControl }) => {
@@ -48782,7 +48770,7 @@ function registerConnectedMachineTools(server, deps, grant, sessionId, json2) {
48782
48770
  server.registerTool(
48783
48771
  "connected_machine_enable_screen_control",
48784
48772
  {
48785
- description: "Turn screen control on for a machine that is already connected, in place, so agents can see and use its screen (computer and attached-Chrome tools). `target` is a sandboxes_list id or an enrollment id. Needs no human click, token, terminal or reconnect: the platform records the consent on that machine and its agent refreshes its own credentials over the live connection. status=active means it is on now. status=pending means it is allowed and turns on by itself: reason=offline when the machine next connects; reason=agent_update_required after its agent is updated (update it with the machine update action, POST /v1/workspaces/{workspaceId}/machines/{enrollmentId}/update); reason=renewal_failed: call again shortly; reason=reconnect_required: the machine's connection predates in-place changes, so rerun the connect command on it (connected_machine_enroll_token with allowScreenControl=true). On a Mac the OS must also allow Screen Recording, Accessibility and Input Monitoring for the app that runs the agent (the name macOS lists in those panes): open each pane on the Mac with run_on exec `open 'x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture'` (Privacy_Accessibility, Privacy_ListenEvent; also the privacy-settings machine action) and ask the person to switch that app on there, choosing Quit & Reopen if macOS asks; the machine list reports runtime.macPermissions. Chrome additionally needs the Opengeni Browser extension (" + OPENGENI_BROWSER_EXTENSION_URL + ") on that machine.",
48773
+ description: "Turn screen control on for a machine that is already connected, in place, so agents can see and use its screen (computer and attached-Chrome tools). `target` is a sandboxes_list id or an enrollment id. Needs no human click, token, terminal or reconnect: the platform records the consent on that machine and its agent refreshes its own credentials over the live connection. status=active means it is on now. status=pending means it is allowed and turns on by itself: reason=offline when the machine next connects; reason=agent_update_required after its agent is updated (update it with the machine update action, POST /v1/workspaces/{workspaceId}/machines/{enrollmentId}/update); reason=renewal_failed: call again shortly; reason=reconnect_required: the machine's connection predates in-place changes, so rerun the connect command on it (connected_machine_enroll_token with allowScreenControl=true). On a Mac the OS must also allow Screen Recording, Accessibility and Input Monitoring for the app that runs the agent (the name macOS lists in those panes): open each pane on the Mac with run_on exec `open 'x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture'` (Privacy_Accessibility, Privacy_ListenEvent; also the privacy-settings machine action) and ask the person to switch that app on there, choosing Quit & Reopen if macOS asks; the machine list reports runtime.macPermissions. Chrome additionally needs the Opengeni Browser extension (" + browserExtension(deps.settings).installUrl + ") on that machine.",
48786
48774
  inputSchema: { target: z47.string().min(1) }
48787
48775
  },
48788
48776
  async ({ target }) => {
@@ -50609,7 +50597,8 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json2)
50609
50597
  const candidates = [
50610
50598
  ...current.items,
50611
50599
  ...nativeConnectionCapabilityRecommendations({
50612
- connectedMachines: deps.settings.sandboxSelfhostedEnabled
50600
+ connectedMachines: deps.settings.sandboxSelfhostedEnabled,
50601
+ browserExtension: browserExtension(deps.settings)
50613
50602
  })
50614
50603
  ];
50615
50604
  const ranked = searchCapabilityCatalogItems(candidates, query, limit ?? 8);
@@ -50666,7 +50655,8 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json2)
50666
50655
  const item = [
50667
50656
  ...current.items,
50668
50657
  ...nativeConnectionCapabilityRecommendations({
50669
- connectedMachines: deps.settings.sandboxSelfhostedEnabled
50658
+ connectedMachines: deps.settings.sandboxSelfhostedEnabled,
50659
+ browserExtension: browserExtension(deps.settings)
50670
50660
  })
50671
50661
  ].find((candidate) => candidate.id === capabilityId);
50672
50662
  if (!item || !capabilityCatalogItemIsTrustedForExposure(item)) {
@@ -50700,7 +50690,7 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json2)
50700
50690
  capabilityId: item.id,
50701
50691
  status: "ready",
50702
50692
  eventId,
50703
- message: `${setup.detail} The Connected Machine card is in this chat: the person can choose a machine for this chat or connect another. Chrome needs the browser extension (${OPENGENI_BROWSER_EXTENSION_URL}) on a connected machine.`
50693
+ message: `${setup.detail} The Connected Machine card is in this chat: the person can choose a machine for this chat or connect another. Chrome needs the browser extension (${browserExtension(deps.settings).installUrl}) on a connected machine.`
50704
50694
  });
50705
50695
  }
50706
50696
  if (githubCardWhenReady) {
@@ -58985,11 +58975,12 @@ function subscribeSessionListHints(options) {
58985
58975
  let all = false;
58986
58976
  const ids = /* @__PURE__ */ new Set();
58987
58977
  let tail = Promise.resolve();
58988
- const emit = (available, sessionIds) => options.write({
58978
+ const emit = (available, sessionIds, siteSessionIds = []) => options.write({
58989
58979
  type: "workspace.sessions.changed",
58990
58980
  workspaceId: options.workspaceId,
58991
58981
  available,
58992
- sessionIds
58982
+ sessionIds,
58983
+ ...siteSessionIds.length ? { siteSessionIds } : {}
58993
58984
  });
58994
58985
  const enqueue = (action) => {
58995
58986
  tail = tail.then(async () => {
@@ -59025,10 +59016,12 @@ function subscribeSessionListHints(options) {
59025
59016
  ids.clear();
59026
59017
  enqueue(async () => {
59027
59018
  if (observedGeneration !== generation || !connected) return;
59028
- const visibleIds = reconcileAll ? null : await options.authorizeIds(pendingIds);
59019
+ const authorized = reconcileAll ? null : await options.authorizeIds(pendingIds);
59029
59020
  if (observedGeneration !== generation || !connected) return;
59030
- if (visibleIds !== null && !visibleIds.length) return;
59031
- await emit(true, visibleIds);
59021
+ if (authorized === null) return await emit(true, null);
59022
+ const visible = Array.isArray(authorized) ? { sessionIds: authorized, siteSessionIds: [] } : authorized;
59023
+ if (!visible.sessionIds.length && !visible.siteSessionIds.length) return;
59024
+ await emit(true, visible.sessionIds, visible.siteSessionIds);
59032
59025
  });
59033
59026
  }, options.intervalMs);
59034
59027
  return () => {
@@ -60866,6 +60859,7 @@ function registerSessionRoutes(app, deps) {
60866
60859
  ...query.channelId !== void 0 ? { channelId: query.channelId } : {},
60867
60860
  ...query.originSiteId ? { originSiteId: query.originSiteId } : {},
60868
60861
  ...query.siteAccessId ? { siteAccessId: query.siteAccessId } : {},
60862
+ ...query.siteConversations ? { siteConversations: query.siteConversations } : {},
60869
60863
  ...query.createdBy ? { createdBy: query.createdBy } : {},
60870
60864
  ...query.updatedFrom ? { updatedFrom: query.updatedFrom } : {},
60871
60865
  ...query.updatedBefore ? { updatedBefore: query.updatedBefore } : {},
@@ -65010,6 +65004,25 @@ function sessionListQuery(query, allowCursor = true) {
65010
65004
  });
65011
65005
  if (originSiteId !== void 0 && !z31.string().uuid().safeParse(originSiteId).success)
65012
65006
  throw new HTTPException54(400, { message: "originSiteId must be a Site id" });
65007
+ const siteConversations = query.siteConversations;
65008
+ if (siteConversations !== void 0) {
65009
+ if (siteConversations !== "include" && siteConversations !== "group")
65010
+ throw new HTTPException54(400, { message: 'siteConversations must be "include" or "group"' });
65011
+ if (!allowCursor)
65012
+ throw new HTTPException54(400, { message: 'siteConversations requires view="page"' });
65013
+ if (originSiteId !== void 0 || siteAccessId !== void 0)
65014
+ throw new HTTPException54(400, {
65015
+ message: "siteConversations cannot be combined with a Site filter"
65016
+ });
65017
+ if (siteConversations === "group" && (query.parentSessionId !== "null" || query.pinsOnly === "true"))
65018
+ throw new HTTPException54(400, {
65019
+ message: 'siteConversations="group" requires a root page (parentSessionId=null)'
65020
+ });
65021
+ if (siteConversations === "group" && (query.archiveStatus === "archived" || query.archivedOnly === "true"))
65022
+ throw new HTTPException54(400, {
65023
+ message: 'siteConversations="group" cannot list archived sessions; use "include"'
65024
+ });
65025
+ }
65013
65026
  if (parentSessionId !== void 0 && parentSessionId !== "null" && !z31.string().uuid().safeParse(parentSessionId).success) {
65014
65027
  throw new HTTPException54(400, {
65015
65028
  message: 'parentSessionId must be a session id or the literal "null"'
@@ -65144,6 +65157,7 @@ function sessionListQuery(query, allowCursor = true) {
65144
65157
  limit: query.limit,
65145
65158
  originSiteId,
65146
65159
  siteAccessId,
65160
+ siteConversations,
65147
65161
  parentSessionId: parentSessionId === void 0 ? void 0 : parentSessionId === "null" ? null : parentSessionId,
65148
65162
  cursor,
65149
65163
  search: search || void 0,
@@ -66565,7 +66579,8 @@ import {
66565
66579
  createUserPresenceRecorder,
66566
66580
  registerProductUsageMetricBaselines,
66567
66581
  stampPersonalApiKeyRequest,
66568
- verifiedDelegatedHumanAuthorizationForRequest as verifiedDelegatedHumanAuthorizationForRequest3
66582
+ verifiedDelegatedHumanAuthorizationForRequest as verifiedDelegatedHumanAuthorizationForRequest3,
66583
+ browserExtension as browserExtension2
66569
66584
  } from "@opengeni/core";
66570
66585
 
66571
66586
  // src/auth/new-signups-gate.ts
@@ -117726,6 +117741,16 @@ function parseBrowserScreenshotOptions(query) {
117726
117741
  function browserTabDiagnosisError(error) {
117727
117742
  if (!(error instanceof BrowserControlRequestError6)) return error;
117728
117743
  const code = error.error.code;
117744
+ if (code === "resource_unavailable" && error.status >= 500 && !error.retryable && !legacyInteractionCapacityFailure(error, "browser")) {
117745
+ const failure4 = new ApiHttpError(409, {
117746
+ code: "conflict",
117747
+ message: error.error.message,
117748
+ retryable: false,
117749
+ outcomeUnknown: false
117750
+ });
117751
+ failure4.cause = error;
117752
+ return failure4;
117753
+ }
117729
117754
  if (code !== "tab_not_responding" && code !== "tab_crashed") return error;
117730
117755
  const failure3 = new ApiHttpError(409, {
117731
117756
  code,
@@ -122905,7 +122930,7 @@ import {
122905
122930
  listInteractionInterventions,
122906
122931
  listNetworkRoutes,
122907
122932
  listSiteAuthConnections,
122908
- listVisibleSessionInvalidationIds,
122933
+ listVisibleSessionInvalidationHints,
122909
122934
  sessionListChangesFor,
122910
122935
  resolveInteractionIntervention,
122911
122936
  updateNetworkRoute,
@@ -122966,7 +122991,7 @@ function registerInteractionResourceRoutes(app, deps) {
122966
122991
  freshGrant,
122967
122992
  "http"
122968
122993
  );
122969
- return await listVisibleSessionInvalidationIds(deps.db, workspaceId, ids, {
122994
+ return await listVisibleSessionInvalidationHints(deps.db, workspaceId, ids, {
122970
122995
  subjectId: freshGrant.subjectId,
122971
122996
  ...authorizationScope ? { authorizationScope } : {},
122972
122997
  personalWorkspaceOwnerException: hasVerifiedOwningUserAuthorization2(authorization)
@@ -132329,6 +132354,7 @@ import {
132329
132354
  PublishWorkspaceArtifactVersionRequest as PublishWorkspaceArtifactVersionRequest2,
132330
132355
  RollbackWorkspaceArtifactRequest as RollbackWorkspaceArtifactRequest2,
132331
132356
  SetWorkspaceArtifactStatusRequest as SetWorkspaceArtifactStatusRequest2,
132357
+ SiteOpenedResponse,
132332
132358
  UpdateWorkspaceArtifactVisibilityRequest as UpdateWorkspaceArtifactVisibilityRequest2,
132333
132359
  UpdateWorkspaceArtifactVisibilityResponse,
132334
132360
  WORKSPACE_ARTIFACT_TITLE_MAX_CHARS,
@@ -132347,6 +132373,7 @@ import {
132347
132373
  resolveSiteAudienceChange,
132348
132374
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError16,
132349
132375
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError14,
132376
+ currentSiteSessionOrigin as currentSiteSessionOrigin6,
132350
132377
  siteViewerForGrant as siteViewerForGrant8
132351
132378
  } from "@opengeni/core";
132352
132379
  import {
@@ -132355,6 +132382,7 @@ import {
132355
132382
  getWorkspaceArtifact as getWorkspaceArtifact3,
132356
132383
  getWorkspaceArtifactContentRef as getWorkspaceArtifactContentRef4,
132357
132384
  listWorkspaceArtifacts as listWorkspaceArtifacts2,
132385
+ recordSiteOpened,
132358
132386
  nestedPostgresSqlState as nestedPostgresSqlState18,
132359
132387
  publishWorkspaceArtifactVersion as publishWorkspaceArtifactVersion2,
132360
132388
  rollbackWorkspaceArtifact as rollbackWorkspaceArtifact2,
@@ -132498,6 +132526,30 @@ function registerWorkspaceArtifactRoutes(app, deps) {
132498
132526
  return workspaceArtifactErrorResponse(context, error);
132499
132527
  }
132500
132528
  });
132529
+ app.post(`${base}/:artifactId/opened`, async (context) => {
132530
+ const workspaceId = context.req.param("workspaceId");
132531
+ const grant = await requireAccessGrant57(context, deps, workspaceId, "artifacts:read");
132532
+ if (currentSiteSessionOrigin6())
132533
+ throw new HTTPException121(403, { message: "A Site page cannot record opening a Site" });
132534
+ const siteId3 = artifactId(context);
132535
+ const viewer = await siteViewerForGrant8(deps.db, grant);
132536
+ if (!viewer.subjectId)
132537
+ throw new HTTPException121(403, { message: "A signed-in person is required" });
132538
+ try {
132539
+ return context.json(
132540
+ SiteOpenedResponse.parse(
132541
+ await recordSiteOpened(deps.db, {
132542
+ workspaceId,
132543
+ subjectId: viewer.subjectId,
132544
+ viewer,
132545
+ siteId: siteId3
132546
+ })
132547
+ )
132548
+ );
132549
+ } catch (error) {
132550
+ return workspaceArtifactErrorResponse(context, error);
132551
+ }
132552
+ });
132501
132553
  app.get(base, async (context) => {
132502
132554
  const workspaceId = context.req.param("workspaceId");
132503
132555
  const grant = await requireAccessGrant57(context, deps, workspaceId, "artifacts:read");
@@ -149957,6 +150009,7 @@ function createAppComposition(deps) {
149957
150009
  deploymentRevision: deps.settings.deploymentRevision,
149958
150010
  claudeSubscriptionEnabled: deps.settings.claudeSubscriptionEnabled,
149959
150011
  connectedMachines: deps.settings.sandboxSelfhostedEnabled,
150012
+ browserExtension: browserExtension2(deps.settings),
149960
150013
  apiContractRevision: OPENGENI_API_CONTRACT_REVISION5,
149961
150014
  ...deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {},
149962
150015
  defaultModel,
@@ -152239,4 +152292,4 @@ export {
152239
152292
  withDefaultEnabledCapabilityMcpTools,
152240
152293
  workflowIdForSession4 as workflowIdForSession
152241
152294
  };
152242
- //# sourceMappingURL=chunk-S5VJ3GVR.js.map
152295
+ //# sourceMappingURL=chunk-LXUAR3HS.js.map