@opengeni/api-router 4.4.1 → 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-3KRHXJPM.js";
31
+ } from "./chunk-LXUAR3HS.js";
32
32
  import "./chunk-OGQDXVXY.js";
33
33
  export {
34
34
  API_MAX_REQUEST_BODY_BYTES,
@@ -16593,9 +16593,9 @@ import {
16593
16593
  buildCapabilityCatalog,
16594
16594
  CONNECTED_MACHINE_CAPABILITY_ID,
16595
16595
  EXCEL_ADDIN_CAPABILITY_ID as EXCEL_ADDIN_CAPABILITY_ID2,
16596
+ browserExtension,
16596
16597
  nativeConnectionCapabilityRecommendations,
16597
16598
  userSetupCardPayload,
16598
- OPENGENI_BROWSER_EXTENSION_URL,
16599
16599
  requireLiveAgentAttemptAuthorization as requireLiveAgentAttemptAuthorization7,
16600
16600
  requireSessionAuthorization as requireSessionAuthorization3,
16601
16601
  requireSessionAuthorizationListScope,
@@ -42330,6 +42330,15 @@ var ACTION_CATALOG = [
42330
42330
  "PersonalGitHubOAuthStartResponse"
42331
42331
  ]
42332
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
+ },
42333
42342
  {
42334
42343
  "id": "recoverSandbox",
42335
42344
  "method": "POST",
@@ -46945,7 +46954,7 @@ function registerSandboxFileArtifactTool(server, deps, grant, sessionId, json2)
46945
46954
  server.registerTool(
46946
46955
  "sandbox_file_publish",
46947
46956
  {
46948
- 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.",
46949
46958
  inputSchema: {
46950
46959
  path: z47.string().min(1).max(4096).optional(),
46951
46960
  artifactId: z47.string().uuid().optional(),
@@ -48690,9 +48699,10 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
48690
48699
  async ({ kind, name }) => {
48691
48700
  if (kind === "selfhosted" && exactAgentAttemptClaims(grant)) {
48692
48701
  await requireLiveAgentAttemptAuthorization7(deps.db, grant, sessionId);
48693
- const item = nativeConnectionCapabilityRecommendations({ connectedMachines: true }).find(
48694
- (candidate) => candidate.id === CONNECTED_MACHINE_CAPABILITY_ID
48695
- );
48702
+ const item = nativeConnectionCapabilityRecommendations({
48703
+ connectedMachines: true,
48704
+ browserExtension: browserExtension(deps.settings)
48705
+ }).find((candidate) => candidate.id === CONNECTED_MACHINE_CAPABILITY_ID);
48696
48706
  const setup = await capabilitySetupProjection(
48697
48707
  deps,
48698
48708
  grant.workspaceId,
@@ -48716,7 +48726,7 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
48716
48726
  kind: "selfhosted",
48717
48727
  status: "card_posted",
48718
48728
  eventId,
48719
- 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.`
48720
48730
  });
48721
48731
  }
48722
48732
  return json2(
@@ -48732,7 +48742,7 @@ function registerConnectedMachineTools(server, deps, grant, sessionId, json2) {
48732
48742
  server.registerTool(
48733
48743
  "connected_machine_enroll_token",
48734
48744
  {
48735
- 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.",
48736
48746
  inputSchema: { allowScreenControl: z47.boolean().optional() }
48737
48747
  },
48738
48748
  async ({ allowScreenControl }) => {
@@ -48760,7 +48770,7 @@ function registerConnectedMachineTools(server, deps, grant, sessionId, json2) {
48760
48770
  server.registerTool(
48761
48771
  "connected_machine_enable_screen_control",
48762
48772
  {
48763
- 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.",
48764
48774
  inputSchema: { target: z47.string().min(1) }
48765
48775
  },
48766
48776
  async ({ target }) => {
@@ -50587,7 +50597,8 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json2)
50587
50597
  const candidates = [
50588
50598
  ...current.items,
50589
50599
  ...nativeConnectionCapabilityRecommendations({
50590
- connectedMachines: deps.settings.sandboxSelfhostedEnabled
50600
+ connectedMachines: deps.settings.sandboxSelfhostedEnabled,
50601
+ browserExtension: browserExtension(deps.settings)
50591
50602
  })
50592
50603
  ];
50593
50604
  const ranked = searchCapabilityCatalogItems(candidates, query, limit ?? 8);
@@ -50644,7 +50655,8 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json2)
50644
50655
  const item = [
50645
50656
  ...current.items,
50646
50657
  ...nativeConnectionCapabilityRecommendations({
50647
- connectedMachines: deps.settings.sandboxSelfhostedEnabled
50658
+ connectedMachines: deps.settings.sandboxSelfhostedEnabled,
50659
+ browserExtension: browserExtension(deps.settings)
50648
50660
  })
50649
50661
  ].find((candidate) => candidate.id === capabilityId);
50650
50662
  if (!item || !capabilityCatalogItemIsTrustedForExposure(item)) {
@@ -50678,7 +50690,7 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json2)
50678
50690
  capabilityId: item.id,
50679
50691
  status: "ready",
50680
50692
  eventId,
50681
- 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.`
50682
50694
  });
50683
50695
  }
50684
50696
  if (githubCardWhenReady) {
@@ -58963,11 +58975,12 @@ function subscribeSessionListHints(options) {
58963
58975
  let all = false;
58964
58976
  const ids = /* @__PURE__ */ new Set();
58965
58977
  let tail = Promise.resolve();
58966
- const emit = (available, sessionIds) => options.write({
58978
+ const emit = (available, sessionIds, siteSessionIds = []) => options.write({
58967
58979
  type: "workspace.sessions.changed",
58968
58980
  workspaceId: options.workspaceId,
58969
58981
  available,
58970
- sessionIds
58982
+ sessionIds,
58983
+ ...siteSessionIds.length ? { siteSessionIds } : {}
58971
58984
  });
58972
58985
  const enqueue = (action) => {
58973
58986
  tail = tail.then(async () => {
@@ -59003,10 +59016,12 @@ function subscribeSessionListHints(options) {
59003
59016
  ids.clear();
59004
59017
  enqueue(async () => {
59005
59018
  if (observedGeneration !== generation || !connected) return;
59006
- const visibleIds = reconcileAll ? null : await options.authorizeIds(pendingIds);
59019
+ const authorized = reconcileAll ? null : await options.authorizeIds(pendingIds);
59007
59020
  if (observedGeneration !== generation || !connected) return;
59008
- if (visibleIds !== null && !visibleIds.length) return;
59009
- 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);
59010
59025
  });
59011
59026
  }, options.intervalMs);
59012
59027
  return () => {
@@ -60844,6 +60859,7 @@ function registerSessionRoutes(app, deps) {
60844
60859
  ...query.channelId !== void 0 ? { channelId: query.channelId } : {},
60845
60860
  ...query.originSiteId ? { originSiteId: query.originSiteId } : {},
60846
60861
  ...query.siteAccessId ? { siteAccessId: query.siteAccessId } : {},
60862
+ ...query.siteConversations ? { siteConversations: query.siteConversations } : {},
60847
60863
  ...query.createdBy ? { createdBy: query.createdBy } : {},
60848
60864
  ...query.updatedFrom ? { updatedFrom: query.updatedFrom } : {},
60849
60865
  ...query.updatedBefore ? { updatedBefore: query.updatedBefore } : {},
@@ -64988,6 +65004,25 @@ function sessionListQuery(query, allowCursor = true) {
64988
65004
  });
64989
65005
  if (originSiteId !== void 0 && !z31.string().uuid().safeParse(originSiteId).success)
64990
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
+ }
64991
65026
  if (parentSessionId !== void 0 && parentSessionId !== "null" && !z31.string().uuid().safeParse(parentSessionId).success) {
64992
65027
  throw new HTTPException54(400, {
64993
65028
  message: 'parentSessionId must be a session id or the literal "null"'
@@ -65122,6 +65157,7 @@ function sessionListQuery(query, allowCursor = true) {
65122
65157
  limit: query.limit,
65123
65158
  originSiteId,
65124
65159
  siteAccessId,
65160
+ siteConversations,
65125
65161
  parentSessionId: parentSessionId === void 0 ? void 0 : parentSessionId === "null" ? null : parentSessionId,
65126
65162
  cursor,
65127
65163
  search: search || void 0,
@@ -66543,7 +66579,8 @@ import {
66543
66579
  createUserPresenceRecorder,
66544
66580
  registerProductUsageMetricBaselines,
66545
66581
  stampPersonalApiKeyRequest,
66546
- verifiedDelegatedHumanAuthorizationForRequest as verifiedDelegatedHumanAuthorizationForRequest3
66582
+ verifiedDelegatedHumanAuthorizationForRequest as verifiedDelegatedHumanAuthorizationForRequest3,
66583
+ browserExtension as browserExtension2
66547
66584
  } from "@opengeni/core";
66548
66585
 
66549
66586
  // src/auth/new-signups-gate.ts
@@ -117704,6 +117741,16 @@ function parseBrowserScreenshotOptions(query) {
117704
117741
  function browserTabDiagnosisError(error) {
117705
117742
  if (!(error instanceof BrowserControlRequestError6)) return error;
117706
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
+ }
117707
117754
  if (code !== "tab_not_responding" && code !== "tab_crashed") return error;
117708
117755
  const failure3 = new ApiHttpError(409, {
117709
117756
  code,
@@ -122883,7 +122930,7 @@ import {
122883
122930
  listInteractionInterventions,
122884
122931
  listNetworkRoutes,
122885
122932
  listSiteAuthConnections,
122886
- listVisibleSessionInvalidationIds,
122933
+ listVisibleSessionInvalidationHints,
122887
122934
  sessionListChangesFor,
122888
122935
  resolveInteractionIntervention,
122889
122936
  updateNetworkRoute,
@@ -122944,7 +122991,7 @@ function registerInteractionResourceRoutes(app, deps) {
122944
122991
  freshGrant,
122945
122992
  "http"
122946
122993
  );
122947
- return await listVisibleSessionInvalidationIds(deps.db, workspaceId, ids, {
122994
+ return await listVisibleSessionInvalidationHints(deps.db, workspaceId, ids, {
122948
122995
  subjectId: freshGrant.subjectId,
122949
122996
  ...authorizationScope ? { authorizationScope } : {},
122950
122997
  personalWorkspaceOwnerException: hasVerifiedOwningUserAuthorization2(authorization)
@@ -132307,6 +132354,7 @@ import {
132307
132354
  PublishWorkspaceArtifactVersionRequest as PublishWorkspaceArtifactVersionRequest2,
132308
132355
  RollbackWorkspaceArtifactRequest as RollbackWorkspaceArtifactRequest2,
132309
132356
  SetWorkspaceArtifactStatusRequest as SetWorkspaceArtifactStatusRequest2,
132357
+ SiteOpenedResponse,
132310
132358
  UpdateWorkspaceArtifactVisibilityRequest as UpdateWorkspaceArtifactVisibilityRequest2,
132311
132359
  UpdateWorkspaceArtifactVisibilityResponse,
132312
132360
  WORKSPACE_ARTIFACT_TITLE_MAX_CHARS,
@@ -132325,6 +132373,7 @@ import {
132325
132373
  resolveSiteAudienceChange,
132326
132374
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError16,
132327
132375
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError14,
132376
+ currentSiteSessionOrigin as currentSiteSessionOrigin6,
132328
132377
  siteViewerForGrant as siteViewerForGrant8
132329
132378
  } from "@opengeni/core";
132330
132379
  import {
@@ -132333,6 +132382,7 @@ import {
132333
132382
  getWorkspaceArtifact as getWorkspaceArtifact3,
132334
132383
  getWorkspaceArtifactContentRef as getWorkspaceArtifactContentRef4,
132335
132384
  listWorkspaceArtifacts as listWorkspaceArtifacts2,
132385
+ recordSiteOpened,
132336
132386
  nestedPostgresSqlState as nestedPostgresSqlState18,
132337
132387
  publishWorkspaceArtifactVersion as publishWorkspaceArtifactVersion2,
132338
132388
  rollbackWorkspaceArtifact as rollbackWorkspaceArtifact2,
@@ -132476,6 +132526,30 @@ function registerWorkspaceArtifactRoutes(app, deps) {
132476
132526
  return workspaceArtifactErrorResponse(context, error);
132477
132527
  }
132478
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
+ });
132479
132553
  app.get(base, async (context) => {
132480
132554
  const workspaceId = context.req.param("workspaceId");
132481
132555
  const grant = await requireAccessGrant57(context, deps, workspaceId, "artifacts:read");
@@ -149935,6 +150009,7 @@ function createAppComposition(deps) {
149935
150009
  deploymentRevision: deps.settings.deploymentRevision,
149936
150010
  claudeSubscriptionEnabled: deps.settings.claudeSubscriptionEnabled,
149937
150011
  connectedMachines: deps.settings.sandboxSelfhostedEnabled,
150012
+ browserExtension: browserExtension2(deps.settings),
149938
150013
  apiContractRevision: OPENGENI_API_CONTRACT_REVISION5,
149939
150014
  ...deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {},
149940
150015
  defaultModel,
@@ -152217,4 +152292,4 @@ export {
152217
152292
  withDefaultEnabledCapabilityMcpTools,
152218
152293
  workflowIdForSession4 as workflowIdForSession
152219
152294
  };
152220
- //# sourceMappingURL=chunk-3KRHXJPM.js.map
152295
+ //# sourceMappingURL=chunk-LXUAR3HS.js.map