@opengeni/api-router 0.23.1 → 0.26.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.
Files changed (107) hide show
  1. package/dist/app.d.ts +2 -2
  2. package/dist/app.js +1 -1
  3. package/dist/browser-controller-authority.d.ts +43 -0
  4. package/dist/browser-state-authority.d.ts +27 -0
  5. package/dist/{chunk-T4T2PGU4.js → chunk-JIKNR5YL.js} +26181 -14408
  6. package/dist/chunk-JIKNR5YL.js.map +1 -0
  7. package/dist/codemode.d.ts +23 -0
  8. package/dist/editable-artifact-live-hints.d.ts +11 -0
  9. package/dist/editable-artifact-native-kernel.d.ts +37 -0
  10. package/dist/editable-artifact-office-import.d.ts +22 -0
  11. package/dist/editable-artifact-production.d.ts +29 -0
  12. package/dist/editable-artifact-websocket.d.ts +49 -0
  13. package/dist/editable-artifact-workspace-files.d.ts +23 -0
  14. package/dist/github-browser-flow.d.ts +6 -0
  15. package/dist/http/cors.d.ts +1 -0
  16. package/dist/index.d.ts +3 -2
  17. package/dist/index.js +1796 -22
  18. package/dist/index.js.map +1 -1
  19. package/dist/integrations/api-integrations.d.ts +24 -0
  20. package/dist/integrations/atlassian.d.ts +176 -0
  21. package/dist/integrations/github-skill-source.d.ts +5 -0
  22. package/dist/integrations/google-drive.d.ts +85 -0
  23. package/dist/integrations/oauth-client.d.ts +22 -1
  24. package/dist/integrations/provider-oauth.d.ts +19 -0
  25. package/dist/integrations/slack-bot.d.ts +4 -0
  26. package/dist/integrations/slack-interactions.d.ts +8 -2
  27. package/dist/integrations/social-api.d.ts +2 -1
  28. package/dist/mcp/editable-artifact-query-schema.d.ts +4 -0
  29. package/dist/mcp/editable-artifacts.d.ts +13 -0
  30. package/dist/mcp/scheduled-task-view.d.ts +168 -0
  31. package/dist/mcp/server.d.ts +14 -3
  32. package/dist/memory-slack-delivery.d.ts +9 -0
  33. package/dist/routes/api-integrations.d.ts +8 -0
  34. package/dist/routes/browser-identities.d.ts +5 -0
  35. package/dist/routes/browser-sessions.d.ts +6 -0
  36. package/dist/routes/company-profile.d.ts +3 -0
  37. package/dist/routes/computer-sessions.d.ts +6 -0
  38. package/dist/routes/editable-artifacts.d.ts +44 -0
  39. package/dist/routes/integration-features.d.ts +3 -0
  40. package/dist/routes/memory-slack-publications.d.ts +6 -0
  41. package/dist/routes/plugins.d.ts +8 -0
  42. package/dist/routes/sessions.d.ts +17 -2
  43. package/dist/routes/skills.d.ts +6 -0
  44. package/dist/routes/video-generation.d.ts +3 -0
  45. package/dist/sandbox/channel-a.d.ts +55 -2
  46. package/dist/sandbox/metrics-ingestion.d.ts +6 -1
  47. package/dist/sandbox/viewer.d.ts +4 -2
  48. package/dist/temporal-schedule-cleanup.d.ts +26 -0
  49. package/package.json +19 -14
  50. package/src/app.ts +274 -37
  51. package/src/browser-controller-authority.ts +137 -0
  52. package/src/browser-state-authority.ts +236 -0
  53. package/src/codemode.ts +186 -0
  54. package/src/editable-artifact-live-hints.ts +64 -0
  55. package/src/editable-artifact-native-kernel.ts +659 -0
  56. package/src/editable-artifact-office-import.ts +230 -0
  57. package/src/editable-artifact-production.ts +419 -0
  58. package/src/editable-artifact-websocket.ts +311 -0
  59. package/src/editable-artifact-workspace-files.ts +186 -0
  60. package/src/github-browser-flow.ts +35 -6
  61. package/src/http/auth.ts +2 -0
  62. package/src/http/cors.ts +3 -0
  63. package/src/index.ts +119 -20
  64. package/src/integrations/api-integrations.ts +350 -0
  65. package/src/integrations/atlassian.ts +1621 -0
  66. package/src/integrations/github-skill-source.ts +142 -0
  67. package/src/integrations/google-drive.ts +1000 -64
  68. package/src/integrations/oauth-client.ts +123 -33
  69. package/src/integrations/provider-oauth.ts +777 -0
  70. package/src/integrations/slack-bot.ts +31 -2
  71. package/src/integrations/slack-interactions.ts +487 -27
  72. package/src/integrations/social-api.ts +11 -0
  73. package/src/mcp/documents.ts +32 -1
  74. package/src/mcp/editable-artifact-query-schema.ts +236 -0
  75. package/src/mcp/editable-artifacts.ts +448 -0
  76. package/src/mcp/scheduled-task-view.ts +34 -0
  77. package/src/mcp/server.ts +923 -145
  78. package/src/memory-slack-delivery.ts +209 -0
  79. package/src/routes/api-integrations.ts +407 -0
  80. package/src/routes/browser-identities.ts +136 -0
  81. package/src/routes/browser-sessions.ts +2543 -0
  82. package/src/routes/company-profile.ts +255 -0
  83. package/src/routes/computer-sessions.ts +1247 -0
  84. package/src/routes/connections.ts +284 -99
  85. package/src/routes/documents.ts +22 -3
  86. package/src/routes/editable-artifacts.ts +1159 -0
  87. package/src/routes/files.ts +106 -4
  88. package/src/routes/github.ts +18 -2
  89. package/src/routes/install.ts +7 -1
  90. package/src/routes/integration-features.ts +258 -0
  91. package/src/routes/memory-slack-publications.ts +216 -0
  92. package/src/routes/packs.ts +437 -7
  93. package/src/routes/plugins.ts +751 -0
  94. package/src/routes/rigs.ts +77 -20
  95. package/src/routes/scheduled-tasks.ts +67 -40
  96. package/src/routes/sessions.ts +421 -245
  97. package/src/routes/skills.ts +174 -0
  98. package/src/routes/video-generation.ts +132 -0
  99. package/src/routes/workspaces.ts +46 -24
  100. package/src/sandbox/channel-a.ts +723 -116
  101. package/src/sandbox/metrics-ingestion.ts +121 -3
  102. package/src/sandbox/rematerialize.ts +35 -47
  103. package/src/sandbox/viewer.ts +29 -9
  104. package/src/temporal-schedule-cleanup.ts +135 -0
  105. package/dist/chunk-T4T2PGU4.js.map +0 -1
  106. package/dist/mcp/toolspace.d.ts +0 -71
  107. package/src/mcp/toolspace.ts +0 -1190
@@ -7,7 +7,6 @@ import {
7
7
  import {
8
8
  beginRigChangeVerificationAttempt,
9
9
  listRigs,
10
- RigChangeAlreadyVerifyingError,
11
10
  RigChangeTransitionError,
12
11
  } from "@opengeni/db";
13
12
  import type { Hono } from "hono";
@@ -32,38 +31,87 @@ import { boundedLimit } from "../http/common";
32
31
  export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
33
32
  const { db, workflowClient } = deps;
34
33
 
35
- async function startChangeVerification(workspaceId: string, changeId: string): Promise<unknown> {
34
+ async function startChangeVerification(
35
+ workspaceId: string,
36
+ changeId: string,
37
+ ): Promise<{
38
+ change: Awaited<ReturnType<typeof beginRigChangeVerificationAttempt>>;
39
+ started: boolean;
40
+ }> {
36
41
  const startedAt = new Date().toISOString();
37
42
  let change;
38
43
  try {
39
- change = await beginRigChangeVerificationAttempt(db, workspaceId, changeId, { startedAt });
44
+ change = await beginRigChangeVerificationAttempt(db, workspaceId, changeId, {
45
+ startedAt,
46
+ // Reuse the existing attempt after an ambiguous Temporal start. Its
47
+ // deterministic workflow id turns this into an idempotent recovery.
48
+ allowAlreadyVerifying: true,
49
+ });
40
50
  } catch (error) {
41
- if (
42
- error instanceof RigChangeAlreadyVerifyingError ||
43
- error instanceof RigChangeTransitionError
44
- ) {
51
+ if (error instanceof RigChangeTransitionError) {
45
52
  throw new HTTPException(409, { message: error.message });
46
53
  }
47
54
  throw error;
48
55
  }
49
56
  const attempt =
50
57
  typeof change.verification?.attempt === "number" ? change.verification.attempt : Date.now();
51
- await workflowClient.startRigVerification({
52
- workspaceId,
53
- changeId,
54
- workflowId: `rig-verification-change-${changeId}-attempt-${attempt}`,
55
- });
56
- return change;
58
+ try {
59
+ await workflowClient.startRigVerification({
60
+ workspaceId,
61
+ changeId,
62
+ workflowId: `rig-verification-change-${changeId}-attempt-${attempt}`,
63
+ });
64
+ return { change, started: true };
65
+ } catch (error) {
66
+ // The verifying transition already committed. Keep its deterministic
67
+ // attempt identity retryable instead of reporting an opaque 5xx after a
68
+ // durable mutation or inventing a second attempt after an ambiguous start.
69
+ deps.observability?.warn("rig change verification start failed", {
70
+ workspaceId,
71
+ changeId,
72
+ attempt,
73
+ error: error instanceof Error ? error.message : String(error),
74
+ });
75
+ return { change, started: false };
76
+ }
57
77
  }
58
78
 
59
- async function startVersionVerification(workspaceId: string, versionId: string): Promise<void> {
79
+ async function startVersionVerification(
80
+ workspaceId: string,
81
+ versionId: string,
82
+ workflowId = `rig-verification-version-${versionId}-${crypto.randomUUID()}`,
83
+ ): Promise<void> {
60
84
  await workflowClient.startRigVerification({
61
85
  workspaceId,
62
86
  versionId,
63
- workflowId: `rig-verification-version-${versionId}-${crypto.randomUUID()}`,
87
+ workflowId,
64
88
  });
65
89
  }
66
90
 
91
+ async function tryStartInitialVersionVerification(
92
+ workspaceId: string,
93
+ versionId: string,
94
+ ): Promise<boolean> {
95
+ try {
96
+ await startVersionVerification(
97
+ workspaceId,
98
+ versionId,
99
+ `rig-verification-version-${versionId}-initial`,
100
+ );
101
+ return true;
102
+ } catch (error) {
103
+ // Creation already committed. The rig remains fully usable through its
104
+ // runtime setup fallback and can be re-verified explicitly; never turn a
105
+ // successful create into an unretryable 5xx because Temporal was down.
106
+ deps.observability?.warn("initial rig provider-image verification start failed", {
107
+ workspaceId,
108
+ versionId,
109
+ error: error instanceof Error ? error.message : String(error),
110
+ });
111
+ return false;
112
+ }
113
+ }
114
+
67
115
  app.get("/v1/workspaces/:workspaceId/rigs", async (c) => {
68
116
  const workspaceId = c.req.param("workspaceId");
69
117
  await requireAccessGrant(c, deps, workspaceId, "rigs:use");
@@ -75,6 +123,10 @@ export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
75
123
  const grant = await requireAccessGrant(c, deps, workspaceId, "rigs:manage");
76
124
  const payload = CreateRigRequest.parse(await c.req.json());
77
125
  const rig = await createRigForApi({ db }, grant, payload);
126
+ if (rig.activeVersion) {
127
+ const started = await tryStartInitialVersionVerification(workspaceId, rig.activeVersion.id);
128
+ if (!started) c.header("OpenGeni-Rig-Verification", "deferred");
129
+ }
78
130
  return c.json(rig, 201);
79
131
  });
80
132
 
@@ -112,7 +164,10 @@ export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
112
164
  const grant = await requireAccessGrant(c, deps, workspaceId, "rigs:manage");
113
165
  const rig = await requireRigForApi(db, workspaceId, c.req.param("rigId"));
114
166
  const payload = RigDefinitionEditPayload.parse(await c.req.json());
115
- return c.json(await createRigVersionForApi({ db }, grant, rig, payload), 201);
167
+ const version = await createRigVersionForApi({ db }, grant, rig, payload);
168
+ const started = await tryStartInitialVersionVerification(workspaceId, version.id);
169
+ if (!started) c.header("OpenGeni-Rig-Verification", "deferred");
170
+ return c.json(version, 201);
116
171
  });
117
172
 
118
173
  // Rollback / promote-activate: flips which existing version is active.
@@ -142,8 +197,9 @@ export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
142
197
  const rig = await requireRigForApi(db, workspaceId, c.req.param("rigId"));
143
198
  const request = ProposeRigChangeRequest.parse(await c.req.json());
144
199
  const change = await proposeRigChangeForApi({ db }, grant, rig, request);
145
- const verifying = await startChangeVerification(workspaceId, change.id);
146
- return c.json(verifying, 201);
200
+ const verification = await startChangeVerification(workspaceId, change.id);
201
+ if (!verification.started) c.header("OpenGeni-Rig-Verification", "deferred");
202
+ return c.json(verification.change, 201);
147
203
  });
148
204
 
149
205
  app.get("/v1/workspaces/:workspaceId/rigs/:rigId/changes/:changeId", async (c) => {
@@ -163,8 +219,9 @@ export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
163
219
  c.req.param("rigId"),
164
220
  c.req.param("changeId"),
165
221
  );
166
- const verifying = await startChangeVerification(workspaceId, change.id);
167
- return c.json(verifying, 202);
222
+ const verification = await startChangeVerification(workspaceId, change.id);
223
+ if (!verification.started) c.header("OpenGeni-Rig-Verification", "deferred");
224
+ return c.json(verification.change, 202);
168
225
  });
169
226
 
170
227
  app.post("/v1/workspaces/:workspaceId/rigs/:rigId/changes/:changeId/promote", async (c) => {
@@ -10,6 +10,7 @@ import {
10
10
  updateScheduledTask,
11
11
  } from "@opengeni/db";
12
12
  import type { Hono } from "hono";
13
+ import { HTTPException } from "hono/http-exception";
13
14
  import { requireAccessGrant } from "@opengeni/core";
14
15
  import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
15
16
  import type { ApiRouteDeps } from "@opengeni/core";
@@ -29,6 +30,8 @@ import {
29
30
  validatedScheduledTaskUpdate,
30
31
  } from "@opengeni/core";
31
32
  import { boundedLimit } from "../http/common";
33
+ import { revokeKnowledgeSourceScheduleAuthorization } from "../integrations/google-drive";
34
+ import { revokeAtlassianScheduleAuthorization } from "../integrations/atlassian";
32
35
 
33
36
  export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void {
34
37
  const { settings, db, workflowClient, objectStorage } = deps;
@@ -37,7 +40,13 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
37
40
  const workspaceId = c.req.param("workspaceId");
38
41
  const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
39
42
  const rawPayload = await c.req.json();
40
- const payload = CreateScheduledTaskRequest.parse(rawPayload);
43
+ const parsedPayload = CreateScheduledTaskRequest.safeParse(rawPayload);
44
+ if (!parsedPayload.success) {
45
+ throw new HTTPException(400, {
46
+ message: "invalid scheduled task create request",
47
+ });
48
+ }
49
+ const payload = parsedPayload.data;
41
50
  await requireLimit(deps, {
42
51
  accountId: grant.accountId,
43
52
  workspaceId,
@@ -79,7 +88,13 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
79
88
  const existing = await requireScheduledTaskForApi(db, workspaceId, taskId);
80
89
  const previous = await captureScheduledTaskRestoreState(db, existing);
81
90
  const rawPayload = await c.req.json();
82
- const payload = UpdateScheduledTaskRequest.parse(rawPayload);
91
+ const parsedPayload = UpdateScheduledTaskRequest.safeParse(rawPayload);
92
+ if (!parsedPayload.success) {
93
+ throw new HTTPException(400, {
94
+ message: "invalid scheduled task update request",
95
+ });
96
+ }
97
+ const payload = parsedPayload.data;
83
98
  const update = await validatedScheduledTaskUpdate({
84
99
  settings,
85
100
  db,
@@ -101,7 +116,9 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
101
116
  const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
102
117
  const existing = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
103
118
  const previous = await captureScheduledTaskRestoreState(db, existing);
104
- const task = await updateScheduledTask(db, workspaceId, existing.id, { status: "paused" });
119
+ const task = await updateScheduledTask(db, workspaceId, existing.id, {
120
+ status: "paused",
121
+ });
105
122
  await syncUpdatedScheduledTask({ db, workflowClient, previous, task });
106
123
  return c.json(scheduledTaskForGrant(task, grant));
107
124
  });
@@ -111,7 +128,9 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
111
128
  const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
112
129
  const existing = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
113
130
  const previous = await captureScheduledTaskRestoreState(db, existing);
114
- const task = await updateScheduledTask(db, workspaceId, existing.id, { status: "active" });
131
+ const task = await updateScheduledTask(db, workspaceId, existing.id, {
132
+ status: "active",
133
+ });
115
134
  await syncUpdatedScheduledTask({ db, workflowClient, previous, task });
116
135
  return c.json(scheduledTaskForGrant(task, grant));
117
136
  });
@@ -122,35 +141,36 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
122
141
  // Load the task before the gate so a codex-model scheduled task can be
123
142
  // recognised as codex-billed and skip the credit/cost gates at the edge.
124
143
  const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
125
- await validateScheduledTaskTarget({
126
- db,
127
- sessionAuthorization: deps.sessionAuthorization,
128
- authorizationSurface: "http",
129
- grant,
130
- targetSessionId: task.targetSessionId,
131
- runMode: task.runMode,
132
- variableSetId: task.variableSetId,
133
- rigId: task.rigId,
134
- agentConfig: task.agentConfig,
135
- missingTargetStatus: 404,
136
- });
137
- await requireLimit(deps, {
138
- accountId: grant.accountId,
139
- workspaceId,
140
- action: "agent_run:create",
141
- quantity: 1,
142
- model: task.agentConfig.model ?? deps.settings.openaiModel,
143
- });
144
+ if (task.action.kind === "agent_turn") {
145
+ await validateScheduledTaskTarget({
146
+ db,
147
+ sessionAuthorization: deps.sessionAuthorization,
148
+ authorizationSurface: "http",
149
+ grant,
150
+ targetSessionId: task.targetSessionId,
151
+ runMode: task.runMode,
152
+ variableSetId: task.variableSetId,
153
+ rigId: task.rigId,
154
+ agentConfig: task.agentConfig,
155
+ missingTargetStatus: 404,
156
+ });
157
+ await requireLimit(deps, {
158
+ accountId: grant.accountId,
159
+ workspaceId,
160
+ action: "agent_run:create",
161
+ quantity: 1,
162
+ model: task.agentConfig.model ?? deps.settings.openaiModel,
163
+ });
164
+ }
144
165
  // Body is optional (a bare POST is still a valid trigger); only a present,
145
166
  // non-empty body must parse against the contract.
146
167
  const body = await c.req.json().catch(() => ({}));
147
168
  const { triggerId } = TriggerScheduledTaskRequest.parse(body ?? {});
148
169
  const triggerToken = scheduledTaskTriggerToken(triggerId);
149
- const agentRunUsageIdempotencyKey = manualScheduledTaskTriggerUsageKey(
150
- workspaceId,
151
- task.id,
152
- triggerToken,
153
- );
170
+ const agentRunUsageIdempotencyKey =
171
+ task.action.kind === "agent_turn"
172
+ ? manualScheduledTaskTriggerUsageKey(workspaceId, task.id, triggerToken)
173
+ : `knowledge-source-sync:manual:${workspaceId}:${task.id}:${triggerToken}`;
154
174
  const triggerWorkflowId = manualScheduledTaskTriggerWorkflowId(task.id, triggerToken);
155
175
  await workflowClient.triggerScheduledTask({
156
176
  task,
@@ -158,24 +178,31 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
158
178
  triggerWorkflowId,
159
179
  initiator: { kind: "subject", subjectId: grant.subjectId },
160
180
  });
161
- await recordWorkspaceUsage(deps, {
162
- accountId: grant.accountId,
163
- workspaceId,
164
- subjectId: grant.subjectId,
165
- eventType: "agent_run.created",
166
- quantity: 1,
167
- unit: "run",
168
- sourceResourceType: "scheduled_task",
169
- sourceResourceId: task.id,
170
- idempotencyKey: agentRunUsageIdempotencyKey,
171
- });
181
+ if (task.action.kind === "agent_turn") {
182
+ await recordWorkspaceUsage(deps, {
183
+ accountId: grant.accountId,
184
+ workspaceId,
185
+ subjectId: grant.subjectId,
186
+ eventType: "agent_run.created",
187
+ quantity: 1,
188
+ unit: "run",
189
+ sourceResourceType: "scheduled_task",
190
+ sourceResourceId: task.id,
191
+ idempotencyKey: agentRunUsageIdempotencyKey,
192
+ });
193
+ }
172
194
  return c.json(scheduledTaskForGrant(task, grant), 202);
173
195
  });
174
196
 
175
197
  app.delete("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId", async (c) => {
176
198
  const workspaceId = c.req.param("workspaceId");
177
- await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
199
+ const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
178
200
  const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
201
+ if (task.metadata.connectorKind === "atlassian") {
202
+ await revokeAtlassianScheduleAuthorization(deps, { task, subjectId: grant.subjectId });
203
+ } else {
204
+ await revokeKnowledgeSourceScheduleAuthorization(deps, { task, subjectId: grant.subjectId });
205
+ }
179
206
  await workflowClient.deleteScheduledTaskSchedule({
180
207
  temporalScheduleId: task.temporalScheduleId,
181
208
  });