@opengeni/api-router 0.30.3 → 2.2.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/api-websocket.d.ts +18 -0
  2. package/dist/app.js +1 -1
  3. package/dist/auth/managed-auth.d.ts +6 -0
  4. package/dist/{chunk-WCXHI3FF.js → chunk-3TP54PPX.js} +26692 -15389
  5. package/dist/chunk-3TP54PPX.js.map +1 -0
  6. package/dist/codemode.d.ts +8 -1
  7. package/dist/company-brain-okf.d.ts +24 -0
  8. package/dist/connected-machine-computer-access.d.ts +11 -0
  9. package/dist/connection-authority-owner.d.ts +59 -0
  10. package/dist/connection-ownership.d.ts +47 -0
  11. package/dist/controller-data-plane.d.ts +16 -0
  12. package/dist/editable-artifact-websocket.d.ts +5 -15
  13. package/dist/editable-artifact-workspace-files.d.ts +1 -1
  14. package/dist/http/api-error.d.ts +10 -0
  15. package/dist/http/cors.d.ts +5 -0
  16. package/dist/http/interaction-control-error.d.ts +13 -0
  17. package/dist/http/sse.d.ts +2 -3
  18. package/dist/index.d.ts +5 -2
  19. package/dist/index.js +326 -82
  20. package/dist/index.js.map +1 -1
  21. package/dist/integrations/atlassian.d.ts +17 -0
  22. package/dist/integrations/fiken.d.ts +7 -2
  23. package/dist/integrations/google-drive.d.ts +18 -0
  24. package/dist/integrations/oauth-client.d.ts +22 -3
  25. package/dist/integrations/oauth-profiles.d.ts +195 -0
  26. package/dist/integrations/personal-github-repositories.d.ts +45 -0
  27. package/dist/integrations/personal-github.d.ts +40 -0
  28. package/dist/integrations/pr-review-provider.d.ts +22 -0
  29. package/dist/integrations/provider-oauth.d.ts +8 -0
  30. package/dist/integrations/slack-app-home.d.ts +24 -0
  31. package/dist/integrations/slack-bot.d.ts +157 -9
  32. package/dist/integrations/slack-interactions.d.ts +39 -5
  33. package/dist/integrations/slack-routing.d.ts +120 -0
  34. package/dist/integrations/social-oauth.d.ts +5 -0
  35. package/dist/interaction-frame-proxy.d.ts +68 -0
  36. package/dist/mcp/company-brain-governed-writes.d.ts +26 -0
  37. package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
  38. package/dist/mcp/documents.d.ts +1 -0
  39. package/dist/mcp/remember.d.ts +28 -0
  40. package/dist/mcp/request-abort.d.ts +19 -0
  41. package/dist/mcp/scheduled-task-view.d.ts +4 -4
  42. package/dist/mcp/server.d.ts +15 -2
  43. package/dist/mcp/session-view.d.ts +4 -0
  44. package/dist/mcp/session-wait.d.ts +137 -0
  45. package/dist/routes/automations.d.ts +4 -0
  46. package/dist/routes/billing.d.ts +5 -0
  47. package/dist/routes/browser-sessions.d.ts +4 -1
  48. package/dist/routes/company-brain.d.ts +3 -0
  49. package/dist/routes/company-profile.d.ts +2 -1
  50. package/dist/routes/connection-authorities.d.ts +3 -0
  51. package/dist/routes/personal-github-git-broker.d.ts +29 -0
  52. package/dist/routes/personal-github.d.ts +3 -0
  53. package/dist/routes/pr-review.d.ts +3 -0
  54. package/dist/routes/sessions.d.ts +1 -0
  55. package/dist/routes/user-resource-authorities.d.ts +3 -0
  56. package/dist/routes/workspace-capture.d.ts +10 -2
  57. package/dist/routes/workspace-learning.d.ts +3 -0
  58. package/dist/routes/workspaces.d.ts +19 -0
  59. package/dist/sandbox/channel-a.d.ts +11 -1
  60. package/dist/sandbox/connection-authority.d.ts +3 -0
  61. package/dist/sandbox/enrollment.d.ts +2 -0
  62. package/dist/sandbox/machines.d.ts +2 -2
  63. package/dist/sandbox/metrics-ingestion.d.ts +10 -20
  64. package/dist/sandbox/viewer.d.ts +24 -1
  65. package/dist/sandbox-file-artifacts.d.ts +11 -0
  66. package/dist/scheduled-task-deletion.d.ts +14 -0
  67. package/dist/slack-reaction-files.d.ts +1 -1
  68. package/dist/temporal-schedule-cleanup.d.ts +1 -0
  69. package/package.json +20 -18
  70. package/src/api-websocket.ts +23 -0
  71. package/src/app.ts +161 -23
  72. package/src/auth/managed-auth.ts +25 -3
  73. package/src/codemode.ts +53 -23
  74. package/src/codex-realtime.ts +8 -2
  75. package/src/company-brain-okf.ts +340 -0
  76. package/src/connected-machine-computer-access.ts +33 -0
  77. package/src/connection-authority-owner.ts +61 -0
  78. package/src/connection-ownership.ts +180 -0
  79. package/src/controller-data-plane.ts +47 -0
  80. package/src/editable-artifact-native-kernel.ts +26 -15
  81. package/src/editable-artifact-office-import.ts +28 -3
  82. package/src/editable-artifact-production.ts +13 -7
  83. package/src/editable-artifact-websocket.ts +11 -18
  84. package/src/editable-artifact-workspace-files.ts +31 -6
  85. package/src/http/api-error.ts +28 -0
  86. package/src/http/auth.ts +4 -0
  87. package/src/http/cors.ts +35 -0
  88. package/src/http/interaction-control-error.ts +164 -0
  89. package/src/http/sse.ts +112 -66
  90. package/src/index.ts +46 -6
  91. package/src/integrations/atlassian.ts +146 -35
  92. package/src/integrations/fiken.ts +102 -22
  93. package/src/integrations/google-drive.ts +301 -92
  94. package/src/integrations/oauth-client.ts +272 -143
  95. package/src/integrations/oauth-profiles.ts +477 -0
  96. package/src/integrations/personal-github-repositories.ts +445 -0
  97. package/src/integrations/personal-github.ts +705 -0
  98. package/src/integrations/pr-review-provider.ts +246 -0
  99. package/src/integrations/provider-oauth.ts +121 -5
  100. package/src/integrations/slack-app-home.ts +300 -0
  101. package/src/integrations/slack-bot.ts +342 -57
  102. package/src/integrations/slack-interactions.ts +1898 -259
  103. package/src/integrations/slack-routing.ts +337 -0
  104. package/src/integrations/social-oauth.ts +25 -0
  105. package/src/interaction-frame-proxy.ts +409 -0
  106. package/src/mcp/company-brain-governed-writes.ts +262 -0
  107. package/src/mcp/company-profile-agent-admin.ts +205 -0
  108. package/src/mcp/documents.ts +37 -6
  109. package/src/mcp/files.ts +16 -1
  110. package/src/mcp/remember.ts +181 -0
  111. package/src/mcp/request-abort.ts +44 -0
  112. package/src/mcp/scheduled-task-view.ts +1 -0
  113. package/src/mcp/server.ts +719 -369
  114. package/src/mcp/session-view.ts +54 -0
  115. package/src/mcp/session-wait.ts +554 -0
  116. package/src/model-catalog.ts +20 -12
  117. package/src/routes/api-integrations.ts +4 -0
  118. package/src/routes/api-keys.ts +1 -0
  119. package/src/routes/automations.ts +534 -0
  120. package/src/routes/billing.ts +57 -1
  121. package/src/routes/browser-sessions.ts +329 -79
  122. package/src/routes/channels.ts +17 -1
  123. package/src/routes/codex.ts +50 -0
  124. package/src/routes/company-brain.ts +367 -0
  125. package/src/routes/company-profile.ts +1 -1
  126. package/src/routes/computer-sessions.ts +320 -93
  127. package/src/routes/connection-authorities.ts +139 -0
  128. package/src/routes/connections.ts +144 -34
  129. package/src/routes/documents.ts +353 -4
  130. package/src/routes/editable-artifacts.ts +11 -3
  131. package/src/routes/enrollments.ts +116 -26
  132. package/src/routes/environments.ts +127 -55
  133. package/src/routes/files.ts +72 -11
  134. package/src/routes/install.ts +82 -28
  135. package/src/routes/integration-facets.ts +29 -0
  136. package/src/routes/interaction-resources.ts +20 -2
  137. package/src/routes/machines.ts +275 -13
  138. package/src/routes/organization-memberships.ts +717 -28
  139. package/src/routes/packs.ts +4 -3
  140. package/src/routes/personal-github-git-broker.ts +785 -0
  141. package/src/routes/personal-github.ts +319 -0
  142. package/src/routes/pr-review.ts +531 -0
  143. package/src/routes/rigs.ts +104 -39
  144. package/src/routes/scheduled-tasks.ts +19 -21
  145. package/src/routes/sessions.ts +775 -38
  146. package/src/routes/social.ts +14 -2
  147. package/src/routes/supergrok.ts +23 -4
  148. package/src/routes/transcription-recordings.ts +8 -2
  149. package/src/routes/user-resource-authorities.ts +138 -0
  150. package/src/routes/workspace-artifacts.ts +4 -1
  151. package/src/routes/workspace-capture.ts +21 -0
  152. package/src/routes/workspace-learning.ts +228 -0
  153. package/src/routes/workspaces.ts +59 -6
  154. package/src/sandbox/auth-callout.ts +38 -12
  155. package/src/sandbox/channel-a.ts +242 -19
  156. package/src/sandbox/connection-authority.ts +3 -0
  157. package/src/sandbox/enrollment.ts +15 -3
  158. package/src/sandbox/machines.ts +186 -64
  159. package/src/sandbox/metrics-ingestion.ts +220 -58
  160. package/src/sandbox/viewer.ts +243 -19
  161. package/src/sandbox-file-artifacts.ts +329 -0
  162. package/src/scheduled-task-deletion.ts +127 -0
  163. package/src/slack-reaction-files.ts +16 -5
  164. package/src/temporal-schedule-cleanup.ts +13 -0
  165. package/dist/chunk-WCXHI3FF.js.map +0 -1
@@ -4,17 +4,31 @@ import {
4
4
  CreateKnowledgeMemoryRequest,
5
5
  CreateDocumentBaseRequest,
6
6
  Document,
7
+ DocumentAuthorityReclassification,
7
8
  DocumentBase,
9
+ DocumentDefaultCollectionBackfill,
10
+ DocumentDefaultCollectionBackfillAudit,
11
+ GetDocumentDefaultCollectionBackfillAuditQuery,
8
12
  DocumentSearchRequest,
9
13
  DocumentSearchResponse,
14
+ FileAsset,
15
+ FileDownloadUrlResponse,
10
16
  KnowledgeMemory,
11
17
  KnowledgeMemorySearchRequest,
18
+ ListDocumentAuthorityReclassificationsQuery,
19
+ ListDocumentAuthorityReclassificationsResponse,
20
+ ListDocumentDefaultCollectionBackfillRunsResponse,
21
+ ListDocumentMigrationAuditQuery,
22
+ ListOrganizationDocumentAuthorityReclassificationsResponse,
12
23
  MoveDocumentRequest,
24
+ ReclassifyDocumentAuthorityRequest,
25
+ RunDocumentDefaultCollectionBackfillRequest,
13
26
  UpdateKnowledgeMemoryRequest,
14
27
  WorkspaceMemorySearchRequest,
15
28
  WorkspaceMemorySearchResponse,
16
29
  } from "@opengeni/contracts";
17
30
  import {
31
+ recordAuditEvent,
18
32
  completeFileUpload,
19
33
  createFileUpload,
20
34
  createKnowledgeMemory,
@@ -29,11 +43,19 @@ import {
29
43
  deleteDocumentFromBase,
30
44
  ensureDefaultBase,
31
45
  getDocument,
46
+ getDocumentOriginalFile,
32
47
  getDocumentBase,
48
+ getDocumentDefaultCollectionBackfillAudit,
49
+ listAccessibleDocuments,
50
+ listDocumentAuthorityReclassifications,
51
+ listDocumentDefaultCollectionBackfillRuns,
33
52
  listDocumentBasesEnsuringDefault,
34
53
  listDocuments,
54
+ listOrganizationDocumentAuthorityReclassifications,
35
55
  moveDocumentToBase,
36
56
  queueDocumentForReindex,
57
+ reclassifyDocumentAuthority,
58
+ runDocumentDefaultCollectionBackfill,
37
59
  searchEffectiveDocuments,
38
60
  } from "@opengeni/documents";
39
61
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
@@ -41,6 +63,7 @@ import type { Context, Hono } from "hono";
41
63
  import { HTTPException } from "hono/http-exception";
42
64
  import {
43
65
  requireAccessGrant,
66
+ requireAccountAdminAuthorizationStamp,
44
67
  requireAccessGrantAuthorization,
45
68
  saveWorkspaceMemoryWithSlackPublication,
46
69
  } from "@opengeni/core";
@@ -87,6 +110,136 @@ export function registerDocumentRoutes(app: Hono, deps: ApiRouteDeps): void {
87
110
  return c.json(DocumentBase.parse(base));
88
111
  });
89
112
 
113
+ app.post("/v1/workspaces/:workspaceId/document-default-collection-backfills", async (c) => {
114
+ const workspaceId = c.req.param("workspaceId");
115
+ const authorization = await requireAccessGrantAuthorization(
116
+ c,
117
+ deps,
118
+ workspaceId,
119
+ "documents:manage",
120
+ );
121
+ if (!hasAccountAdminAuthority(authorization)) {
122
+ throw new HTTPException(403, { message: "missing permission: account:admin" });
123
+ }
124
+ const accountAdminAuthorization = requireAccountAdminAuthorizationStamp(authorization);
125
+ const payload = RunDocumentDefaultCollectionBackfillRequest.parse(await c.req.json());
126
+ try {
127
+ return c.json(
128
+ DocumentDefaultCollectionBackfill.parse(
129
+ await runDocumentDefaultCollectionBackfill(db, {
130
+ ...payload,
131
+ accountId: authorization.grant.accountId,
132
+ workspaceId,
133
+ actorSubjectId: authorization.grant.subjectId,
134
+ accountAdminAuthorization,
135
+ }),
136
+ ),
137
+ );
138
+ } catch (error) {
139
+ throw documentHttpException(error);
140
+ }
141
+ });
142
+
143
+ app.get("/v1/workspaces/:workspaceId/document-default-collection-backfills", async (c) => {
144
+ const workspaceId = c.req.param("workspaceId");
145
+ const authorization = await requireAccessGrantAuthorization(
146
+ c,
147
+ deps,
148
+ workspaceId,
149
+ "documents:manage",
150
+ );
151
+ if (!hasAccountAdminAuthority(authorization)) {
152
+ throw new HTTPException(403, { message: "missing permission: account:admin" });
153
+ }
154
+ const query = ListDocumentMigrationAuditQuery.safeParse(c.req.query());
155
+ if (!query.success) {
156
+ throw new HTTPException(400, { message: "invalid document migration audit query" });
157
+ }
158
+ try {
159
+ return c.json(
160
+ ListDocumentDefaultCollectionBackfillRunsResponse.parse(
161
+ await listDocumentDefaultCollectionBackfillRuns(db, {
162
+ accountId: authorization.grant.accountId,
163
+ workspaceId,
164
+ actorSubjectId: authorization.grant.subjectId,
165
+ accountAdminAuthorization: requireAccountAdminAuthorizationStamp(authorization),
166
+ ...query.data,
167
+ }),
168
+ ),
169
+ );
170
+ } catch (error) {
171
+ throw documentHttpException(error);
172
+ }
173
+ });
174
+
175
+ app.get("/v1/workspaces/:workspaceId/document-default-collection-backfills/:runId", async (c) => {
176
+ const workspaceId = c.req.param("workspaceId");
177
+ const authorization = await requireAccessGrantAuthorization(
178
+ c,
179
+ deps,
180
+ workspaceId,
181
+ "documents:manage",
182
+ );
183
+ if (!hasAccountAdminAuthority(authorization)) {
184
+ throw new HTTPException(403, { message: "missing permission: account:admin" });
185
+ }
186
+ const runId = RunDocumentDefaultCollectionBackfillRequest.shape.runId.safeParse(
187
+ c.req.param("runId"),
188
+ );
189
+ const query = GetDocumentDefaultCollectionBackfillAuditQuery.safeParse(c.req.query());
190
+ if (!runId.success || !query.success) {
191
+ throw new HTTPException(400, { message: "invalid document migration audit query" });
192
+ }
193
+ try {
194
+ return c.json(
195
+ DocumentDefaultCollectionBackfillAudit.parse(
196
+ await getDocumentDefaultCollectionBackfillAudit(db, {
197
+ accountId: authorization.grant.accountId,
198
+ workspaceId,
199
+ actorSubjectId: authorization.grant.subjectId,
200
+ accountAdminAuthorization: requireAccountAdminAuthorizationStamp(authorization),
201
+ runId: runId.data,
202
+ ...query.data,
203
+ }),
204
+ ),
205
+ );
206
+ } catch (error) {
207
+ throw documentHttpException(error);
208
+ }
209
+ });
210
+
211
+ app.get("/v1/workspaces/:workspaceId/document-authority-reclassifications", async (c) => {
212
+ const workspaceId = c.req.param("workspaceId");
213
+ const authorization = await requireAccessGrantAuthorization(
214
+ c,
215
+ deps,
216
+ workspaceId,
217
+ "documents:manage",
218
+ );
219
+ if (!hasAccountAdminAuthority(authorization)) {
220
+ throw new HTTPException(403, { message: "missing permission: account:admin" });
221
+ }
222
+ const query = ListDocumentMigrationAuditQuery.safeParse(c.req.query());
223
+ if (!query.success) {
224
+ throw new HTTPException(400, { message: "invalid document migration audit query" });
225
+ }
226
+ try {
227
+ return c.json(
228
+ ListOrganizationDocumentAuthorityReclassificationsResponse.parse(
229
+ await listOrganizationDocumentAuthorityReclassifications(db, {
230
+ accountId: authorization.grant.accountId,
231
+ workspaceId,
232
+ actorSubjectId: authorization.grant.subjectId,
233
+ accountAdminAuthorization: requireAccountAdminAuthorizationStamp(authorization),
234
+ ...query.data,
235
+ }),
236
+ ),
237
+ );
238
+ } catch (error) {
239
+ throw documentHttpException(error);
240
+ }
241
+ });
242
+
90
243
  app.post("/v1/workspaces/:workspaceId/document-bases/:baseId/documents", async (c) => {
91
244
  const workspaceId = c.req.param("workspaceId");
92
245
  const access = await requireAccessGrantAuthorization(c, deps, workspaceId, "documents:manage");
@@ -161,6 +314,148 @@ export function registerDocumentRoutes(app: Hono, deps: ApiRouteDeps): void {
161
314
  );
162
315
  });
163
316
 
317
+ app.get("/v1/workspaces/:workspaceId/documents", async (c) => {
318
+ const workspaceId = c.req.param("workspaceId");
319
+ const grant = await requireAccessGrant(c, deps, workspaceId, "documents:search");
320
+ return c.json(
321
+ (
322
+ await listAccessibleDocuments(db, workspaceId, {
323
+ viewerSubjectId: grant.subjectId,
324
+ })
325
+ ).map((document) => Document.parse(document)),
326
+ );
327
+ });
328
+
329
+ app.post(
330
+ "/v1/workspaces/:workspaceId/documents/:documentId/authority-reclassifications",
331
+ async (c) => {
332
+ const workspaceId = c.req.param("workspaceId");
333
+ const authorization = await requireAccessGrantAuthorization(
334
+ c,
335
+ deps,
336
+ workspaceId,
337
+ "documents:manage",
338
+ );
339
+ const payload = ReclassifyDocumentAuthorityRequest.parse(await c.req.json());
340
+ const accountAdminAuthorized = hasAccountAdminAuthority(authorization);
341
+ const accountAdminAuthorization = accountAdminAuthorized
342
+ ? requireAccountAdminAuthorizationStamp(authorization)
343
+ : null;
344
+ try {
345
+ const document = await getDocument(db, workspaceId, c.req.param("documentId"), {
346
+ viewerSubjectId: authorization.grant.subjectId,
347
+ });
348
+ if (!document) throw new HTTPException(404, { message: "document not found" });
349
+ if (
350
+ (document.authorityKind === "organization" ||
351
+ payload.targetAuthorityKind === "organization") &&
352
+ !accountAdminAuthorization
353
+ ) {
354
+ throw new HTTPException(403, { message: "missing permission: account:admin" });
355
+ }
356
+ return c.json(
357
+ DocumentAuthorityReclassification.parse(
358
+ await reclassifyDocumentAuthority(db, {
359
+ ...payload,
360
+ accountId: authorization.grant.accountId,
361
+ workspaceId,
362
+ documentId: document.id,
363
+ actorSubjectId: authorization.grant.subjectId,
364
+ accountAdminAuthorization,
365
+ }),
366
+ ),
367
+ );
368
+ } catch (error) {
369
+ if (error instanceof HTTPException) throw error;
370
+ throw documentHttpException(error);
371
+ }
372
+ },
373
+ );
374
+
375
+ app.get(
376
+ "/v1/workspaces/:workspaceId/documents/:documentId/authority-reclassifications",
377
+ async (c) => {
378
+ const workspaceId = c.req.param("workspaceId");
379
+ const grant = await requireAccessGrant(c, deps, workspaceId, "documents:manage");
380
+ try {
381
+ const query = ListDocumentAuthorityReclassificationsQuery.parse(c.req.query());
382
+ return c.json(
383
+ ListDocumentAuthorityReclassificationsResponse.parse(
384
+ await listDocumentAuthorityReclassifications(db, {
385
+ accountId: grant.accountId,
386
+ workspaceId,
387
+ documentId: c.req.param("documentId"),
388
+ actorSubjectId: grant.subjectId,
389
+ ...query,
390
+ }),
391
+ ),
392
+ );
393
+ } catch (error) {
394
+ throw documentHttpException(error);
395
+ }
396
+ },
397
+ );
398
+
399
+ app.get("/v1/workspaces/:workspaceId/documents/:documentId/original-file", async (c) => {
400
+ const workspaceId = c.req.param("workspaceId");
401
+ const grant = await requireAccessGrant(c, deps, workspaceId, "documents:search");
402
+ await requireAccessGrant(c, deps, workspaceId, "files:read");
403
+ const file = await getDocumentOriginalFile(db, {
404
+ accountId: grant.accountId,
405
+ workspaceId,
406
+ documentId: c.req.param("documentId"),
407
+ access: { viewerSubjectId: grant.subjectId },
408
+ });
409
+ if (!file) {
410
+ throw new HTTPException(404, { message: "document file not found" });
411
+ }
412
+ return c.json(FileAsset.parse(file));
413
+ });
414
+
415
+ app.post(
416
+ "/v1/workspaces/:workspaceId/documents/:documentId/original-file/download-url",
417
+ async (c) => {
418
+ const workspaceId = c.req.param("workspaceId");
419
+ const grant = await requireAccessGrant(c, deps, workspaceId, "documents:search");
420
+ await requireAccessGrant(c, deps, workspaceId, "files:read");
421
+ if (!objectStorage) {
422
+ throw new HTTPException(503, { message: "object storage is not configured" });
423
+ }
424
+ const file = await getDocumentOriginalFile(db, {
425
+ accountId: grant.accountId,
426
+ workspaceId,
427
+ documentId: c.req.param("documentId"),
428
+ access: { viewerSubjectId: grant.subjectId },
429
+ });
430
+ if (!file) {
431
+ throw new HTTPException(404, { message: "document file not found" });
432
+ }
433
+ if (file.status !== "ready") {
434
+ throw new HTTPException(409, { message: `file is ${file.status}` });
435
+ }
436
+ const signed = await objectStorage.createGetUrl({ key: file.objectKey });
437
+ await recordAuditEvent(db, {
438
+ accountId: grant.accountId,
439
+ workspaceId,
440
+ subjectId: grant.subjectId,
441
+ action: "file.signed_url.issued",
442
+ targetType: "workspace_document",
443
+ targetId: c.req.param("documentId"),
444
+ metadata: {
445
+ fileId: file.id,
446
+ kind: "document_original",
447
+ expiresAt: signed.expiresAt.toISOString(),
448
+ },
449
+ });
450
+ return c.json(
451
+ FileDownloadUrlResponse.parse({
452
+ url: signed.url,
453
+ expiresAt: signed.expiresAt.toISOString(),
454
+ }),
455
+ );
456
+ },
457
+ );
458
+
164
459
  app.delete(
165
460
  "/v1/workspaces/:workspaceId/document-bases/:baseId/documents/:documentId",
166
461
  async (c) => {
@@ -212,7 +507,9 @@ export function registerDocumentRoutes(app: Hono, deps: ApiRouteDeps): void {
212
507
  const { grant } = authorization;
213
508
  const organizationAuthorityGranted = hasAccountAdminAuthority(authorization);
214
509
  if (!objectStorage) {
215
- throw new HTTPException(503, { message: "object storage is not configured" });
510
+ throw new HTTPException(503, {
511
+ message: "object storage is not configured",
512
+ });
216
513
  }
217
514
  await requireLimit(deps, {
218
515
  accountId: grant.accountId,
@@ -246,7 +543,9 @@ export function registerDocumentRoutes(app: Hono, deps: ApiRouteDeps): void {
246
543
  const indexed =
247
544
  (await documentIndexer.indexDocument({
248
545
  accountId: grant.accountId,
249
- workspaceId,
546
+ // The requested workspace authorizes the human operation; the
547
+ // immutable ingestion workspace remains the indexing authority.
548
+ workspaceId: queued.workspaceId,
250
549
  documentId: document.id,
251
550
  authorityKind: document.authorityKind,
252
551
  authorityWorkspaceId: document.authorityWorkspaceId,
@@ -651,13 +950,19 @@ export function registerDocumentRoutes(app: Hono, deps: ApiRouteDeps): void {
651
950
  const grant = await requireAccessGrant(c, deps, workspaceId, "documents:search");
652
951
  const sessionId =
653
952
  typeof grant.metadata?.sessionId === "string" ? grant.metadata.sessionId : undefined;
953
+ const attemptId =
954
+ typeof grant.metadata?.attemptId === "string" ? grant.metadata.attemptId : undefined;
654
955
  const transport = new WebStandardStreamableHTTPServerTransport({ enableJsonResponse: true });
655
956
  const server = buildDocumentsMcpServer(
656
957
  db,
657
958
  grant.accountId,
658
959
  workspaceId,
659
960
  getDocumentServices(),
660
- { createdBySessionId: sessionId, initiatingSubjectId: grant.subjectId },
961
+ {
962
+ createdBySessionId: sessionId,
963
+ attemptId,
964
+ initiatingSubjectId: grant.subjectId,
965
+ },
661
966
  );
662
967
  await server.connect(transport);
663
968
  return await transport.handleRequest(c.req.raw);
@@ -682,16 +987,47 @@ function dropFilename(preferred: string | undefined): string {
682
987
  }
683
988
 
684
989
  function documentHttpException(error: unknown): HTTPException {
685
- const message = error instanceof Error ? error.message : String(error);
990
+ const message = documentDomainErrorMessage(error);
686
991
  if (message.includes("organization document") && message.includes("exact account authority")) {
687
992
  return new HTTPException(403, { message: "missing permission: account:admin" });
688
993
  }
689
994
  if (message.includes("not found")) {
690
995
  return new HTTPException(404, { message });
691
996
  }
997
+ if (message.includes("document Default collection backfill audit run is unavailable")) {
998
+ return new HTTPException(404, { message });
999
+ }
692
1000
  if (message.includes("already exists")) {
693
1001
  return new HTTPException(409, { message });
694
1002
  }
1003
+ if (
1004
+ message.includes("operation id was reused") ||
1005
+ message.includes("authority changed before reclassification") ||
1006
+ message.includes("run identity mismatch")
1007
+ ) {
1008
+ return new HTTPException(409, { message });
1009
+ }
1010
+ if (
1011
+ message.includes("reclassification requires") ||
1012
+ message.includes("backfill requires organization administration") ||
1013
+ message.includes("document migration audit requires organization administration") ||
1014
+ message.includes("document migration audit scope is invalid")
1015
+ ) {
1016
+ return new HTTPException(403, { message });
1017
+ }
1018
+ if (
1019
+ message.includes("reclassification input is invalid") ||
1020
+ message.includes("reclassification authority is invalid") ||
1021
+ message.includes("invalid document authority receipt cursor") ||
1022
+ message.includes("document authority receipt limit") ||
1023
+ message.includes("invalid document migration audit cursor") ||
1024
+ message.includes("document migration audit cursor is invalid") ||
1025
+ message.includes("document migration audit limit") ||
1026
+ message.includes("document migration audit authority is incomplete") ||
1027
+ message.includes("document Default collection backfill audit run id is required")
1028
+ ) {
1029
+ return new HTTPException(400, { message });
1030
+ }
695
1031
  if (message.includes("no suggested base")) {
696
1032
  return new HTTPException(422, { message });
697
1033
  }
@@ -711,6 +1047,19 @@ function documentHttpException(error: unknown): HTTPException {
711
1047
  return new HTTPException(500, { message });
712
1048
  }
713
1049
 
1050
+ function documentDomainErrorMessage(error: unknown): string {
1051
+ let current: unknown = error;
1052
+ let message = error instanceof Error ? error.message : String(error);
1053
+ const seen = new Set<unknown>();
1054
+ while (current && typeof current === "object" && !seen.has(current) && seen.size < 8) {
1055
+ seen.add(current);
1056
+ const record = current as { cause?: unknown; message?: unknown };
1057
+ if (typeof record.message === "string") message = record.message;
1058
+ current = record.cause;
1059
+ }
1060
+ return message;
1061
+ }
1062
+
714
1063
  function hasAccountAdminAuthority(
715
1064
  authorization: Awaited<ReturnType<typeof requireAccessGrantAuthorization>>,
716
1065
  ): boolean {
@@ -78,9 +78,13 @@ const ListEditableArtifactsQuery = z
78
78
  const MintEditableArtifactLiveTicketRequest = z
79
79
  .object({
80
80
  replicaId: ReplicaId,
81
- protocolVersion: PositiveProtocolVersion,
81
+ modality: z.enum(["document", "spreadsheet", "presentation"]),
82
+ liveProtocolVersion: PositiveProtocolVersion,
82
83
  kernelVersion: VersionName,
83
84
  modelSchemaVersion: PositiveModelSchemaVersion,
85
+ snapshotVersion: PositiveProtocolVersion,
86
+ commandProtocolVersion: PositiveProtocolVersion,
87
+ committedTransactionProtocolVersion: PositiveProtocolVersion,
84
88
  })
85
89
  .strict();
86
90
 
@@ -482,9 +486,13 @@ export function registerEditableArtifactRoutes(
482
486
  scope,
483
487
  actor,
484
488
  artifactId,
485
- protocolVersion: body.protocolVersion,
489
+ modality: body.modality,
490
+ liveProtocolVersion: body.liveProtocolVersion,
486
491
  kernelVersion: body.kernelVersion,
487
492
  modelSchemaVersion: body.modelSchemaVersion,
493
+ snapshotVersion: body.snapshotVersion,
494
+ commandProtocolVersion: body.commandProtocolVersion,
495
+ committedTransactionProtocolVersion: body.committedTransactionProtocolVersion,
488
496
  allowEdit: hasPermission(grant.permissions, "artifacts:publish"),
489
497
  });
490
498
  } catch (error) {
@@ -496,7 +504,7 @@ export function registerEditableArtifactRoutes(
496
504
  !parsedTicket.success ||
497
505
  parsedTicket.data.artifactId !== artifactId ||
498
506
  parsedTicket.data.replicaId !== actor.replicaId ||
499
- parsedTicket.data.protocolVersion !== body.protocolVersion ||
507
+ parsedTicket.data.protocolVersion !== body.liveProtocolVersion ||
500
508
  Date.parse(parsedTicket.data.expiresAt) <= Date.now()
501
509
  ) {
502
510
  throw new Error("Editable artifact application returned an invalid live ticket");