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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  startSlackInteractionPump,
21
21
  startTemporalScheduleCleanupPump,
22
22
  withAccessGrantSessionRlsContext
23
- } from "./chunk-S5VJ3GVR.js";
23
+ } from "./chunk-3KRHXJPM.js";
24
24
  import "./chunk-OGQDXVXY.js";
25
25
 
26
26
  // src/index.ts
@@ -0,0 +1,21 @@
1
+ import { type UserSetupBlocker } from "@opengeni/contracts";
2
+ export { sessionChatUrl } from "@opengeni/contracts";
3
+ type ToolRefusal = {
4
+ code?: string | undefined;
5
+ reason?: string | undefined;
6
+ };
7
+ export type UserSetupCardPost = (blocker: UserSetupBlocker) => Promise<{
8
+ eventId: string | null;
9
+ chatUrl: string | null;
10
+ } | null>;
11
+ /**
12
+ * Post the setup card when a tool was refused for something the person can fix
13
+ * themselves (USER_SETUP_BLOCKERS), and tell the agent it is posted. Every
14
+ * other result is returned unchanged. Posting is best effort: if it cannot be
15
+ * posted, the original refusal still stands.
16
+ */
17
+ export declare function withUserSetupCard<T extends ToolRefusal>(result: T, post: UserSetupCardPost): Promise<T & {
18
+ card?: "shown";
19
+ cardEventId?: string | null;
20
+ chatUrl?: string;
21
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/api-router",
3
- "version": "4.3.3-canary.39786771332001",
3
+ "version": "4.4.1",
4
4
  "description": "Opengeni HTTP surface: the Hono adapter/router (createApp), routes, MCP HTTP transport, and HTTP access adapters over @opengeni/core. An engine-distribution surface — its runtime closure includes engine-internal packages.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -45,29 +45,29 @@
45
45
  "@keeper-security/secrets-manager-core": "17.6.0",
46
46
  "@llamaindex/liteparse": "2.14.2",
47
47
  "@modelcontextprotocol/sdk": "^1.29.0",
48
- "@opengeni/agent-proto": "4.3.3-canary.39786771332001",
49
- "@opengeni/artifact-tool": "4.3.3-canary.39786771332001",
50
- "@opengeni/capabilities": "4.3.3-canary.39786771332001",
51
- "@opengeni/codemode": "4.3.3-canary.39786771332001",
52
- "@opengeni/codex": "4.3.3-canary.39786771332001",
53
- "@opengeni/config": "4.3.3-canary.39786771332001",
54
- "@opengeni/contracts": "4.3.3-canary.39786771332001",
55
- "@opengeni/core": "4.3.3-canary.39786771332001",
56
- "@opengeni/db": "4.3.3-canary.39786771332001",
57
- "@opengeni/documents": "4.3.3-canary.39786771332001",
58
- "@opengeni/events": "4.3.3-canary.39786771332001",
59
- "@opengeni/github": "4.3.3-canary.39786771332001",
60
- "@opengeni/interaction": "4.3.3-canary.39786771332001",
61
- "@opengeni/network": "4.3.3-canary.39786771332001",
62
- "@opengeni/observability": "4.3.3-canary.39786771332001",
63
- "@opengeni/runtime": "4.3.3-canary.39786771332001",
64
- "@opengeni/storage": "4.3.3-canary.39786771332001",
65
- "@opengeni/tool-gateway": "4.3.3-canary.39786771332001",
66
- "@opengeni/vaults": "4.3.3-canary.39786771332001",
67
- "@opengeni/vaults-keeper": "4.3.3-canary.39786771332001",
68
- "@opengeni/vaults-onepassword": "4.3.3-canary.39786771332001",
69
- "@opengeni/vaults-protonpass": "4.3.3-canary.39786771332001",
70
- "@opengeni/xai-subscription": "4.3.3-canary.39786771332001",
48
+ "@opengeni/agent-proto": "4.4.1",
49
+ "@opengeni/artifact-tool": "4.4.1",
50
+ "@opengeni/capabilities": "4.4.1",
51
+ "@opengeni/codemode": "4.4.1",
52
+ "@opengeni/codex": "4.4.1",
53
+ "@opengeni/config": "4.4.1",
54
+ "@opengeni/contracts": "4.4.1",
55
+ "@opengeni/core": "4.4.1",
56
+ "@opengeni/db": "4.4.1",
57
+ "@opengeni/documents": "4.4.1",
58
+ "@opengeni/events": "4.4.1",
59
+ "@opengeni/github": "4.4.1",
60
+ "@opengeni/interaction": "4.4.1",
61
+ "@opengeni/network": "4.4.1",
62
+ "@opengeni/observability": "4.4.1",
63
+ "@opengeni/runtime": "4.4.1",
64
+ "@opengeni/storage": "4.4.1",
65
+ "@opengeni/tool-gateway": "4.4.1",
66
+ "@opengeni/vaults": "4.4.1",
67
+ "@opengeni/vaults-keeper": "4.4.1",
68
+ "@opengeni/vaults-onepassword": "4.4.1",
69
+ "@opengeni/vaults-protonpass": "4.4.1",
70
+ "@opengeni/xai-subscription": "4.4.1",
71
71
  "@temporalio/client": "^1.17.0",
72
72
  "ajv": "^8.20.0",
73
73
  "better-auth": "^1.6.14",
package/src/mcp/server.ts CHANGED
@@ -18,11 +18,7 @@ import { enableMachineScreenControl, enrollmentAccess } from "../sandbox/screen-
18
18
  import { registerNotificationTools } from "./notification-tools";
19
19
  import { excelAddinSetup, registerExcelTools } from "./excel-tools";
20
20
  import { capabilityAccountReadiness } from "./capability-account-readiness";
21
- import {
22
- sessionChatUrl,
23
- withMachinePermissionCard,
24
- type MachinePermissionCardPost,
25
- } from "./machine-permission-card";
21
+ import { sessionChatUrl, withUserSetupCard, type UserSetupCardPost } from "./user-setup-card";
26
22
  import {
27
23
  prepareWorkspaceArtifactUpload,
28
24
  prepareWorkspaceArtifactPublication,
@@ -215,6 +211,7 @@ import {
215
211
  CONNECTED_MACHINE_CAPABILITY_ID,
216
212
  EXCEL_ADDIN_CAPABILITY_ID,
217
213
  nativeConnectionCapabilityRecommendations,
214
+ userSetupCardPayload,
218
215
  OPENGENI_BROWSER_EXTENSION_URL,
219
216
  requireLiveAgentAttemptAuthorization,
220
217
  requireSessionAuthorization,
@@ -4323,23 +4320,23 @@ function registerFleetTools(
4323
4320
  };
4324
4321
  };
4325
4322
 
4326
- // A refusal that only the owner's permission can resolve posts the Connected
4327
- // Machine card right away, in the agent's own turn, so the person can allow
4328
- // the machine without the agent having to find and call the card tool. Only
4329
- // exact agent attempts post it. The card names the machine the agent asked
4330
- // for, so it can lead with allowing that machine. A repeated refusal for the
4331
- // same machine in the same turn reuses that turn's card; the lookup and
4332
- // append run atomically under the attempt fence so parallel refusals cannot
4333
- // stack copies. Cards for every other capability are never limited.
4334
- const machinePermissionCard =
4335
- (toolName: string, target: string): MachinePermissionCardPost =>
4336
- async () => {
4323
+ // A refusal the person can fix themselves (USER_SETUP_BLOCKERS, for example
4324
+ // the owner not having allowed this chat to use their machine yet) posts the
4325
+ // setup card right away, in the agent's own turn, so the person sees the fix
4326
+ // without the agent having to find and call the card tool. Only exact agent
4327
+ // attempts post it. A machine card names the machine the agent asked for, so
4328
+ // it can lead with allowing that machine. A repeated refusal for the same
4329
+ // machine in the same turn reuses that turn's card; the lookup and append run
4330
+ // atomically under the attempt fence so parallel refusals cannot stack
4331
+ // copies. Cards for every other capability are never limited.
4332
+ const userSetupCard =
4333
+ (toolName: string, target: string): UserSetupCardPost =>
4334
+ async (blocker) => {
4337
4335
  const requestedSandboxId = UUID_TARGET.test(target) ? target.toLowerCase() : null;
4338
4336
  if (!exactAgentAttemptClaims(grant)) return null;
4339
4337
  const claims = exactAgentCommandContext(grant, sessionId);
4340
- const item = nativeConnectionCapabilityRecommendations({ connectedMachines: true }).find(
4341
- (candidate) => candidate.id === CONNECTED_MACHINE_CAPABILITY_ID,
4342
- )!;
4338
+ const payload = userSetupCardPayload(blocker, { toolName, requestedSandboxId });
4339
+ if (!payload) return null;
4343
4340
  const appended = await appendTurnCapabilityCardOnce(
4344
4341
  deps.db,
4345
4342
  grant.workspaceId,
@@ -4347,23 +4344,13 @@ function registerFleetTools(
4347
4344
  claims.callerTurnId,
4348
4345
  claims.callerExecutionGeneration,
4349
4346
  claims.callerAttemptId,
4350
- item.id,
4351
- {
4352
- type: "tool.auth_needed",
4353
- payload: {
4354
- ...capabilityCardPayload(item, {
4355
- action: "connect",
4356
- rationale: "Allow this chat to use your computer.",
4357
- toolName,
4358
- }),
4359
- ...(requestedSandboxId ? { requestedSandboxId } : {}),
4360
- },
4361
- },
4347
+ blocker.capabilityId,
4348
+ { type: "tool.auth_needed", payload },
4362
4349
  requestedSandboxId,
4363
4350
  );
4364
4351
  await publishDurableSessionEvents(deps.bus, grant.workspaceId, sessionId, appended.events);
4365
4352
  if (!appended.accepted) {
4366
- throw new Error("The calling turn was replaced before the machine card committed.");
4353
+ throw new Error("The calling turn was replaced before the setup card committed.");
4367
4354
  }
4368
4355
  return {
4369
4356
  eventId: appended.existingEventId ?? appended.events[0]?.id ?? null,
@@ -4398,9 +4385,9 @@ function registerFleetTools(
4398
4385
  },
4399
4386
  async ({ target }) =>
4400
4387
  json(
4401
- await withMachinePermissionCard(
4388
+ await withUserSetupCard(
4402
4389
  await swapActiveSandbox(services, await fleetContext(), target),
4403
- machinePermissionCard("sandbox_attach", target),
4390
+ userSetupCard("sandbox_attach", target),
4404
4391
  ),
4405
4392
  ),
4406
4393
  );
@@ -4414,9 +4401,9 @@ function registerFleetTools(
4414
4401
  },
4415
4402
  async ({ target }) =>
4416
4403
  json(
4417
- await withMachinePermissionCard(
4404
+ await withUserSetupCard(
4418
4405
  await swapActiveSandbox(services, await fleetContext(), target),
4419
- machinePermissionCard("sandbox_swap", target),
4406
+ userSetupCard("sandbox_swap", target),
4420
4407
  ),
4421
4408
  ),
4422
4409
  );
@@ -4458,12 +4445,12 @@ function registerFleetTools(
4458
4445
  )
4459
4446
  : null;
4460
4447
  return json(
4461
- await withMachinePermissionCard(
4448
+ await withUserSetupCard(
4462
4449
  await runOnSandbox(services, await fleetContext(), target, typedOp, {
4463
4450
  ...(transientExecEnvironment ? { transientExecEnvironment } : {}),
4464
4451
  ...(secretResolver ? { secretResolver } : {}),
4465
4452
  }),
4466
- machinePermissionCard("run_on", target),
4453
+ userSetupCard("run_on", target),
4467
4454
  ),
4468
4455
  );
4469
4456
  },
@@ -0,0 +1,42 @@
1
+ import {
2
+ type UserSetupBlocker,
3
+ userSetupBlocker,
4
+ userSetupCardShownMessage,
5
+ } from "@opengeni/contracts";
6
+
7
+ export { sessionChatUrl } from "@opengeni/contracts";
8
+
9
+ type ToolRefusal = { code?: string | undefined; reason?: string | undefined };
10
+
11
+ export type UserSetupCardPost = (blocker: UserSetupBlocker) => Promise<{
12
+ eventId: string | null;
13
+ chatUrl: string | null;
14
+ } | null>;
15
+
16
+ /**
17
+ * Post the setup card when a tool was refused for something the person can fix
18
+ * themselves (USER_SETUP_BLOCKERS), and tell the agent it is posted. Every
19
+ * other result is returned unchanged. Posting is best effort: if it cannot be
20
+ * posted, the original refusal still stands.
21
+ */
22
+ export async function withUserSetupCard<T extends ToolRefusal>(
23
+ result: T,
24
+ post: UserSetupCardPost,
25
+ ): Promise<T & { card?: "shown"; cardEventId?: string | null; chatUrl?: string }> {
26
+ const blocker = userSetupBlocker(result.code);
27
+ if (!blocker) return result;
28
+ let posted: Awaited<ReturnType<UserSetupCardPost>>;
29
+ try {
30
+ posted = await post(blocker);
31
+ } catch {
32
+ return result;
33
+ }
34
+ if (!posted) return result;
35
+ return {
36
+ ...result,
37
+ reason: userSetupCardShownMessage(blocker, posted.chatUrl),
38
+ card: "shown",
39
+ cardEventId: posted.eventId,
40
+ ...(posted.chatUrl ? { chatUrl: posted.chatUrl } : {}),
41
+ };
42
+ }
@@ -461,7 +461,9 @@ export function sandboxArtifactRelativePath(value: string, workspaceRoot = "/wor
461
461
  }
462
462
  if (!relative) {
463
463
  throw new HTTPException(400, {
464
- message: "sandbox artifact path must name a file inside the active workspace root",
464
+ // Say which root: on a connected machine the file may exist outside it,
465
+ // and copying it in is the way forward.
466
+ 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`,
465
467
  });
466
468
  }
467
469
  if (absolute.length > SANDBOX_ARTIFACT_ABSOLUTE_PATH_MAX_CHARS) {
@@ -1,33 +0,0 @@
1
- /** The one refusal a card can resolve: the owner just has not allowed this chat. */
2
- export declare const MACHINE_PERMISSION_REQUIRED_CODE = "personal_machine_permission_required";
3
- /**
4
- * What the agent reads after the card was posted for it. The card is posted in
5
- * this chat's Opengeni timeline; the person may instead be reading the chat in
6
- * Slack, or this may be a worker or scheduled chat, so the text names the chat
7
- * link rather than promising the card is in front of them. Choosing the
8
- * machine on the card sends a message that continues the chat, so the agent
9
- * has nothing to wait for and nothing to retry.
10
- */
11
- export declare function machinePermissionCardShownReason(chatUrl: string | null): string;
12
- type MachineRefusal = {
13
- code?: string | undefined;
14
- reason?: string | undefined;
15
- };
16
- export type MachinePermissionCardPost = () => Promise<{
17
- eventId: string | null;
18
- chatUrl: string | null;
19
- } | null>;
20
- /**
21
- * Post the Connected Machine card when a swap or one-off command was refused
22
- * only because the machine's owner has not allowed this chat yet, and tell the
23
- * agent it is posted. Every other result is returned unchanged. Posting is best
24
- * effort: if it cannot be posted, the original refusal still stands.
25
- */
26
- export declare function withMachinePermissionCard<T extends MachineRefusal>(result: T, post: MachinePermissionCardPost): Promise<T & {
27
- card?: "shown";
28
- cardEventId?: string | null;
29
- chatUrl?: string;
30
- }>;
31
- /** The Opengeni console link for a chat, or null when no safe web base is configured. */
32
- export declare function sessionChatUrl(base: string | null | undefined, workspaceId: string, sessionId: string): string | null;
33
- export {};
@@ -1,78 +0,0 @@
1
- /** The one refusal a card can resolve: the owner just has not allowed this chat. */
2
- export const MACHINE_PERMISSION_REQUIRED_CODE = "personal_machine_permission_required";
3
-
4
- /**
5
- * What the agent reads after the card was posted for it. The card is posted in
6
- * this chat's Opengeni timeline; the person may instead be reading the chat in
7
- * Slack, or this may be a worker or scheduled chat, so the text names the chat
8
- * link rather than promising the card is in front of them. Choosing the
9
- * machine on the card sends a message that continues the chat, so the agent
10
- * has nothing to wait for and nothing to retry.
11
- */
12
- export function machinePermissionCardShownReason(chatUrl: string | null): string {
13
- const elsewhere = chatUrl
14
- ? `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}. `
15
- : "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. ";
16
- return (
17
- "This is a personal (Only me) machine, and its owner has not allowed this chat to use it yet. " +
18
- "A card where the owner can allow it is now posted in this chat in Opengeni. " +
19
- "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. " +
20
- elsewhere +
21
- "Do not wait for it and do not retry: when they allow it, their message continues this chat."
22
- );
23
- }
24
-
25
- type MachineRefusal = { code?: string | undefined; reason?: string | undefined };
26
-
27
- export type MachinePermissionCardPost = () => Promise<{
28
- eventId: string | null;
29
- chatUrl: string | null;
30
- } | null>;
31
-
32
- /**
33
- * Post the Connected Machine card when a swap or one-off command was refused
34
- * only because the machine's owner has not allowed this chat yet, and tell the
35
- * agent it is posted. Every other result is returned unchanged. Posting is best
36
- * effort: if it cannot be posted, the original refusal still stands.
37
- */
38
- export async function withMachinePermissionCard<T extends MachineRefusal>(
39
- result: T,
40
- post: MachinePermissionCardPost,
41
- ): Promise<T & { card?: "shown"; cardEventId?: string | null; chatUrl?: string }> {
42
- if (result.code !== MACHINE_PERMISSION_REQUIRED_CODE) return result;
43
- let posted: Awaited<ReturnType<MachinePermissionCardPost>>;
44
- try {
45
- posted = await post();
46
- } catch {
47
- return result;
48
- }
49
- if (!posted) return result;
50
- return {
51
- ...result,
52
- reason: machinePermissionCardShownReason(posted.chatUrl),
53
- card: "shown",
54
- cardEventId: posted.eventId,
55
- ...(posted.chatUrl ? { chatUrl: posted.chatUrl } : {}),
56
- };
57
- }
58
-
59
- /** The Opengeni console link for a chat, or null when no safe web base is configured. */
60
- export function sessionChatUrl(
61
- base: string | null | undefined,
62
- workspaceId: string,
63
- sessionId: string,
64
- ): string | null {
65
- if (!base) return null;
66
- try {
67
- const url = new URL(
68
- `/workspaces/${encodeURIComponent(workspaceId)}/sessions/${encodeURIComponent(sessionId)}`,
69
- base,
70
- );
71
- if ((url.protocol !== "https:" && url.protocol !== "http:") || url.username || url.password) {
72
- return null;
73
- }
74
- return url.toString();
75
- } catch {
76
- return null;
77
- }
78
- }