@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
@@ -11,6 +11,15 @@ type Utf8Projection = {
11
11
  };
12
12
  export type ScheduledTaskUtf8ProjectionFact = Omit<Utf8Projection, "value">;
13
13
  type ScheduledTaskScheduleProjection = {
14
+ schedule: Extract<ScheduledTaskScheduleSpec, {
15
+ type: "manual";
16
+ }>;
17
+ projection: {
18
+ type: "manual";
19
+ truncated: false;
20
+ fields: Record<string, never>;
21
+ };
22
+ } | {
14
23
  schedule: Extract<ScheduledTaskScheduleSpec, {
15
24
  type: "once";
16
25
  }>;
@@ -53,6 +62,8 @@ export declare function scheduledTaskMcpSummary(task: ScheduledTask): {
53
62
  name: string;
54
63
  status: "active" | "paused";
55
64
  schedule: {
65
+ type: "manual";
66
+ } | {
56
67
  type: "once";
57
68
  runAt: string;
58
69
  timeZone: string;
@@ -70,6 +81,53 @@ export declare function scheduledTaskMcpSummary(task: ScheduledTask): {
70
81
  };
71
82
  runMode: "existing_session" | "new_session_per_run" | "reusable_session";
72
83
  overlapPolicy: "allow_concurrent" | "buffer_one" | "skip";
84
+ action: {
85
+ kind: "agent_turn";
86
+ sourceId?: never;
87
+ sourceGeneration?: never;
88
+ sourceLifecycleGeneration?: never;
89
+ destination?: never;
90
+ initiatingSubjectId?: never;
91
+ allDescendants?: never;
92
+ connection?: never;
93
+ limits?: never;
94
+ } | {
95
+ kind: "knowledge_source_sync";
96
+ sourceId: string;
97
+ sourceGeneration: number;
98
+ sourceLifecycleGeneration: number;
99
+ destination: {
100
+ kind: "organization";
101
+ workspaceId: null;
102
+ subjectId: null;
103
+ } | {
104
+ kind: "workspace";
105
+ workspaceId: string;
106
+ subjectId: null;
107
+ } | {
108
+ kind: "personal";
109
+ workspaceId: string | null;
110
+ subjectId: string;
111
+ };
112
+ initiatingSubjectId: string;
113
+ allDescendants: boolean;
114
+ connection: {
115
+ connectionId: string;
116
+ connectionVersion: number;
117
+ providerDomain: string;
118
+ kind: "api_key" | "app_install" | "delegated" | "oauth2";
119
+ ownerSubjectId: string;
120
+ };
121
+ limits: {
122
+ maxItems: number;
123
+ maxBytes: number;
124
+ maxFileBytes: number;
125
+ maxProviderRequests: number;
126
+ maxElapsedSeconds: number;
127
+ maxConcurrency: number;
128
+ maxFailureDetails: number;
129
+ };
130
+ };
73
131
  targetSessionId: string | null;
74
132
  reusableSessionId: string | null;
75
133
  variableSetId: string | null;
@@ -91,6 +149,10 @@ export declare function scheduledTaskMcpSummary(task: ScheduledTask): {
91
149
  bounded: boolean;
92
150
  name: ScheduledTaskUtf8ProjectionFact;
93
151
  schedule: {
152
+ type: "manual";
153
+ truncated: false;
154
+ fields: Record<string, never>;
155
+ } | {
94
156
  type: "once";
95
157
  truncated: boolean;
96
158
  fields: {
@@ -123,6 +185,8 @@ export declare function boundScheduledTaskDetailMcp(task: ScheduledTask): {
123
185
  name: string;
124
186
  status: "active" | "paused";
125
187
  schedule: {
188
+ type: "manual";
189
+ } | {
126
190
  type: "once";
127
191
  runAt: string;
128
192
  timeZone: string;
@@ -140,6 +204,53 @@ export declare function boundScheduledTaskDetailMcp(task: ScheduledTask): {
140
204
  };
141
205
  runMode: "existing_session" | "new_session_per_run" | "reusable_session";
142
206
  overlapPolicy: "allow_concurrent" | "buffer_one" | "skip";
207
+ action: {
208
+ kind: "agent_turn";
209
+ sourceId?: never;
210
+ sourceGeneration?: never;
211
+ sourceLifecycleGeneration?: never;
212
+ destination?: never;
213
+ initiatingSubjectId?: never;
214
+ allDescendants?: never;
215
+ connection?: never;
216
+ limits?: never;
217
+ } | {
218
+ kind: "knowledge_source_sync";
219
+ sourceId: string;
220
+ sourceGeneration: number;
221
+ sourceLifecycleGeneration: number;
222
+ destination: {
223
+ kind: "organization";
224
+ workspaceId: null;
225
+ subjectId: null;
226
+ } | {
227
+ kind: "workspace";
228
+ workspaceId: string;
229
+ subjectId: null;
230
+ } | {
231
+ kind: "personal";
232
+ workspaceId: string | null;
233
+ subjectId: string;
234
+ };
235
+ initiatingSubjectId: string;
236
+ allDescendants: boolean;
237
+ connection: {
238
+ connectionId: string;
239
+ connectionVersion: number;
240
+ providerDomain: string;
241
+ kind: "api_key" | "app_install" | "delegated" | "oauth2";
242
+ ownerSubjectId: string;
243
+ };
244
+ limits: {
245
+ maxItems: number;
246
+ maxBytes: number;
247
+ maxFileBytes: number;
248
+ maxProviderRequests: number;
249
+ maxElapsedSeconds: number;
250
+ maxConcurrency: number;
251
+ maxFailureDetails: number;
252
+ };
253
+ };
143
254
  targetSessionId: string | null;
144
255
  reusableSessionId: string | null;
145
256
  variableSetId: string | null;
@@ -161,6 +272,10 @@ export declare function boundScheduledTaskDetailMcp(task: ScheduledTask): {
161
272
  bounded: boolean;
162
273
  name: ScheduledTaskUtf8ProjectionFact;
163
274
  schedule: {
275
+ type: "manual";
276
+ truncated: false;
277
+ fields: Record<string, never>;
278
+ } | {
164
279
  type: "once";
165
280
  truncated: boolean;
166
281
  fields: {
@@ -254,6 +369,8 @@ export declare function boundScheduledTaskMcpPage(input: {
254
369
  name: string;
255
370
  status: "active" | "paused";
256
371
  schedule: {
372
+ type: "manual";
373
+ } | {
257
374
  type: "once";
258
375
  runAt: string;
259
376
  timeZone: string;
@@ -271,6 +388,53 @@ export declare function boundScheduledTaskMcpPage(input: {
271
388
  };
272
389
  runMode: "existing_session" | "new_session_per_run" | "reusable_session";
273
390
  overlapPolicy: "allow_concurrent" | "buffer_one" | "skip";
391
+ action: {
392
+ kind: "agent_turn";
393
+ sourceId?: never;
394
+ sourceGeneration?: never;
395
+ sourceLifecycleGeneration?: never;
396
+ destination?: never;
397
+ initiatingSubjectId?: never;
398
+ allDescendants?: never;
399
+ connection?: never;
400
+ limits?: never;
401
+ } | {
402
+ kind: "knowledge_source_sync";
403
+ sourceId: string;
404
+ sourceGeneration: number;
405
+ sourceLifecycleGeneration: number;
406
+ destination: {
407
+ kind: "organization";
408
+ workspaceId: null;
409
+ subjectId: null;
410
+ } | {
411
+ kind: "workspace";
412
+ workspaceId: string;
413
+ subjectId: null;
414
+ } | {
415
+ kind: "personal";
416
+ workspaceId: string | null;
417
+ subjectId: string;
418
+ };
419
+ initiatingSubjectId: string;
420
+ allDescendants: boolean;
421
+ connection: {
422
+ connectionId: string;
423
+ connectionVersion: number;
424
+ providerDomain: string;
425
+ kind: "api_key" | "app_install" | "delegated" | "oauth2";
426
+ ownerSubjectId: string;
427
+ };
428
+ limits: {
429
+ maxItems: number;
430
+ maxBytes: number;
431
+ maxFileBytes: number;
432
+ maxProviderRequests: number;
433
+ maxElapsedSeconds: number;
434
+ maxConcurrency: number;
435
+ maxFailureDetails: number;
436
+ };
437
+ };
274
438
  targetSessionId: string | null;
275
439
  reusableSessionId: string | null;
276
440
  variableSetId: string | null;
@@ -292,6 +456,10 @@ export declare function boundScheduledTaskMcpPage(input: {
292
456
  bounded: boolean;
293
457
  name: ScheduledTaskUtf8ProjectionFact;
294
458
  schedule: {
459
+ type: "manual";
460
+ truncated: false;
461
+ fields: Record<string, never>;
462
+ } | {
295
463
  type: "once";
296
464
  truncated: boolean;
297
465
  fields: {
@@ -1,11 +1,9 @@
1
- import { type AccessGrant } from "@opengeni/contracts";
1
+ import { type AccessGrant, type SessionAuthorizationActor } from "@opengeni/contracts";
2
2
  import { listSessionDiscoverySummaries, type SessionDiscoveryCursor, type SessionDiscoveryOrderBy } from "@opengeni/db";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import type { ApiRouteDeps } from "@opengeni/core";
5
- import type { ToolspaceMcpSurface } from "./toolspace";
6
5
  export type McpServerOptions = {
7
6
  requestOrigin?: string | null;
8
- toolspace?: ToolspaceMcpSurface | null;
9
7
  workspaceMemoryEnabled?: boolean | undefined;
10
8
  };
11
9
  export declare function buildOpenGeniMcpServer(deps: ApiRouteDeps, grant: AccessGrant, options?: McpServerOptions): McpServer;
@@ -33,6 +31,19 @@ export declare function isHumanDirectedTurn(turn: {
33
31
  * identified machine child-notification caller is refused).
34
32
  */
35
33
  export declare function assertGoalReactivationAllowed(deps: ApiRouteDeps, workspaceId: string, sessionId: string, callerTurnId: string | null): Promise<void>;
34
+ export declare function memorySlackPublicationActor(actor: Extract<SessionAuthorizationActor, {
35
+ kind: "agent_attempt";
36
+ }>, sessionId: string, fallbackOwnerLabel: string | null): {
37
+ actor: {
38
+ kind: "human" | "service";
39
+ subjectId: string;
40
+ initiatingHumanSubjectId: string | null;
41
+ sessionId: string;
42
+ turnId: string;
43
+ attemptId: string;
44
+ };
45
+ ownerLabel: string | null;
46
+ };
36
47
  export declare function encodeSessionDiscoveryCursor(cursor: SessionDiscoveryCursor): string;
37
48
  export declare function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCursor;
38
49
  export declare function capSessionDiscoveryPage(page: Awaited<ReturnType<typeof listSessionDiscoverySummaries>>, includeLastMessage: boolean): {
@@ -0,0 +1,9 @@
1
+ import { type ClaimedMemorySlackPublication } from "@opengeni/db";
2
+ import { type ApiRouteDeps } from "@opengeni/core";
3
+ export declare function drainMemorySlackPublicationsOnce(deps: ApiRouteDeps): Promise<boolean>;
4
+ export declare function startMemorySlackPublicationPump(deps: ApiRouteDeps, options?: {
5
+ intervalMs?: number;
6
+ maxPerTick?: number;
7
+ }): () => Promise<void>;
8
+ export declare function formatMemorySlackPublicationMessage(deps: Pick<ApiRouteDeps, "settings">, publication: ClaimedMemorySlackPublication): string;
9
+ export declare function memorySlackDestinationDrift(error: unknown): boolean;
@@ -0,0 +1,8 @@
1
+ import { type ApiRouteDeps } from "@opengeni/core";
2
+ import type { FetchLike } from "@opengeni/network";
3
+ import type { Hono } from "hono";
4
+ export type ApiIntegrationRouteOverrides = Readonly<{
5
+ fetchImpl?: FetchLike;
6
+ }>;
7
+ export declare function apiIntegrationRequiresConnection(authScheme: Record<string, unknown>): boolean;
8
+ export declare function registerApiIntegrationRoutes(app: Hono, deps: ApiRouteDeps, overrides?: ApiIntegrationRouteOverrides): void;
@@ -0,0 +1,5 @@
1
+ import { type ApiRouteDeps } from "@opengeni/core";
2
+ import type { Hono } from "hono";
3
+ import { HTTPException } from "hono/http-exception";
4
+ export declare function registerBrowserIdentityRoutes(app: Hono, deps: ApiRouteDeps): void;
5
+ export declare function browserIdentityRouteError(error: unknown): HTTPException;
@@ -0,0 +1,6 @@
1
+ import { InteractionActor, type AccessGrant } from "@opengeni/contracts";
2
+ import { type ApiRouteDeps } from "@opengeni/core";
3
+ import type { Hono } from "hono";
4
+ export declare function registerBrowserSessionRoutes(app: Hono, deps: ApiRouteDeps): void;
5
+ export declare function validateBrowserRequestOrigin(value: string | undefined, allowedPattern: string): string | null;
6
+ export declare function interactionActorForGrant(grant: AccessGrant): ReturnType<typeof InteractionActor.parse>;
@@ -0,0 +1,3 @@
1
+ import { type ApiRouteDeps } from "@opengeni/core";
2
+ import type { Hono } from "hono";
3
+ export declare function registerCompanyProfileRoutes(app: Hono, deps: ApiRouteDeps): void;
@@ -0,0 +1,6 @@
1
+ import { type ApiRouteDeps } from "@opengeni/core";
2
+ import type { Hono } from "hono";
3
+ /** Public ComputerSession resource surface. Physical app/window authority stays
4
+ * in the same placement controller used by BrowserSession; this route owns only
5
+ * durable authorization, placement fencing, lifecycle receipts, and routing. */
6
+ export declare function registerComputerSessionRoutes(app: Hono, deps: ApiRouteDeps): void;
@@ -0,0 +1,44 @@
1
+ import type { AccessGrant } from "@opengeni/contracts";
2
+ import { type AccessDeps, type EditableArtifactActor, type EditableArtifactApplicationPort, type EditableArtifactDurableExportService, type EditableArtifactOfficeImportPort, type EditableArtifactModality } from "@opengeni/core";
3
+ import type { Hono } from "hono";
4
+ export declare const EDITABLE_ARTIFACT_HTTP_REQUEST_MAX_BYTES: number;
5
+ export declare const EDITABLE_ARTIFACT_EXPORT_REQUEST_MAX_BYTES: number;
6
+ export declare const EDITABLE_ARTIFACT_LIVE_TICKET_REQUEST_MAX_BYTES: number;
7
+ export type EditableArtifactRouteScope = Readonly<{
8
+ accountId: string;
9
+ workspaceId: string;
10
+ }>;
11
+ export type EditableArtifactRouteActor = EditableArtifactActor;
12
+ /**
13
+ * Authenticated transport context. Until a distinct editable-artifact
14
+ * capability is introduced, the existing workspace `artifacts:read` and
15
+ * `artifacts:publish` capabilities intentionally gate read and write here too;
16
+ * the application still performs the durable per-artifact authorization.
17
+ */
18
+ export type EditableArtifactRouteAuthority = Readonly<{
19
+ scope: EditableArtifactRouteScope;
20
+ actor: EditableArtifactRouteActor;
21
+ }>;
22
+ export type MintEditableArtifactLiveTicketOutput = Readonly<{
23
+ artifactId: string;
24
+ modality: EditableArtifactModality;
25
+ replicaId: string;
26
+ token: string;
27
+ expiresAt: string;
28
+ protocolVersion: number;
29
+ }>;
30
+ export type EditableArtifactRouteDependencies = AccessDeps & Readonly<{
31
+ editableArtifacts?: EditableArtifactApplicationPort;
32
+ editableArtifactExports?: EditableArtifactDurableExportService;
33
+ editableArtifactOfficeImports?: EditableArtifactOfficeImportPort;
34
+ /** Test/embedding seam; returned ids are still authorized one by one. */
35
+ editableArtifactSessionArtifactIds?: (scope: EditableArtifactRouteScope, sourceSessionId: string, limit: number) => Promise<readonly string[]>;
36
+ }>;
37
+ export type EditableArtifactApplicationErrorCode = "not_found" | "forbidden" | "conflict" | "unsupported_protocol" | "limit_exceeded" | "unavailable";
38
+ /** A bounded, deliberate failure emitted by the injected application boundary. */
39
+ export declare class EditableArtifactApplicationError extends Error {
40
+ readonly code: EditableArtifactApplicationErrorCode;
41
+ constructor(code: EditableArtifactApplicationErrorCode);
42
+ }
43
+ export declare function registerEditableArtifactRoutes(app: Hono, deps: EditableArtifactRouteDependencies): void;
44
+ export declare function editableArtifactActorForGrant(grant: AccessGrant, rawReplicaId: string): EditableArtifactRouteActor;
@@ -0,0 +1,3 @@
1
+ import { type ApiRouteDeps } from "@opengeni/core";
2
+ import type { Hono } from "hono";
3
+ export declare function registerIntegrationFeatureRoutes(app: Hono, deps: ApiRouteDeps): void;
@@ -0,0 +1,6 @@
1
+ import { type MemorySlackPublication as MemorySlackPublicationRecord } from "@opengeni/contracts";
2
+ import { type ApiRouteDeps } from "@opengeni/core";
3
+ import type { Hono } from "hono";
4
+ export declare function registerMemorySlackPublicationRoutes(app: Hono, deps: ApiRouteDeps): void;
5
+ /** Defensively protects legacy rows without rewriting their durable projection. */
6
+ export declare function sanitizeMemorySlackPublicationHistory(publication: MemorySlackPublicationRecord): MemorySlackPublicationRecord;
@@ -0,0 +1,8 @@
1
+ import { type ApiRouteDeps, type GitHubSkillSourceClient } from "@opengeni/core";
2
+ import type { FetchLike } from "@opengeni/network";
3
+ import type { Hono } from "hono";
4
+ export type PluginRouteOverrides = Readonly<{
5
+ fetchImpl?: FetchLike;
6
+ github?: GitHubSkillSourceClient;
7
+ }>;
8
+ export declare function registerPluginRoutes(app: Hono, deps: ApiRouteDeps, overrides?: PluginRouteOverrides): void;
@@ -1,7 +1,8 @@
1
- import { type SessionAuthorizationOperation } from "@opengeni/contracts";
1
+ import { ClientSessionEvent, SteerSessionMessageRequest, type SessionAuthorizationOperation } from "@opengeni/contracts";
2
+ import { type SessionDiscoveryCursor } from "@opengeni/db";
2
3
  import type { Context, Hono } from "hono";
3
4
  import type { ApiRouteDeps } from "@opengeni/core";
4
- import { type ViewerServices } from "../sandbox/viewer";
5
+ import { type ViewerServices } from "../sandbox/viewer.js";
5
6
  type SessionRouteDeps = ApiRouteDeps & Pick<ViewerServices, "establishSandboxSession">;
6
7
  export declare function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void;
7
8
  /**
@@ -10,6 +11,20 @@ export declare function registerSessionRoutes(app: Hono, deps: SessionRouteDeps)
10
11
  * host-managed mode; standalone deployments never consult this classifier.
11
12
  */
12
13
  export declare function sessionAuthorizationOperationForHttp(method: string, pathname: string, sessionId: string): SessionAuthorizationOperation | null;
14
+ export type AgentTopologyCursorEnvelope = {
15
+ cursor: SessionDiscoveryCursor;
16
+ parentSessionId: string | null;
17
+ search: string | null;
18
+ };
19
+ export declare function encodeAgentTopologyCursor(value: AgentTopologyCursorEnvelope): string;
20
+ export declare function agentTopologyQuery(query: Record<string, string>): {
21
+ limit: number;
22
+ parentSessionId: string | null;
23
+ search: string | undefined;
24
+ cursor: SessionDiscoveryCursor | undefined;
25
+ };
13
26
  /** Stable, value-free JSON errors for only the create-session boundary. */
14
27
  export declare function sessionCreateErrorResponse(c: Context, error: unknown): Response;
28
+ export declare function parseSessionEventAdmission(raw: unknown): ClientSessionEvent;
29
+ export declare function parseSteerSessionAdmission(raw: unknown): SteerSessionMessageRequest;
15
30
  export {};
@@ -0,0 +1,6 @@
1
+ import { type ApiRouteDeps, type GitHubSkillSourceClient } from "@opengeni/core";
2
+ import type { Hono } from "hono";
3
+ export type SkillRouteOverrides = Readonly<{
4
+ github?: GitHubSkillSourceClient;
5
+ }>;
6
+ export declare function registerSkillRoutes(app: Hono, deps: ApiRouteDeps, overrides?: SkillRouteOverrides): void;
@@ -0,0 +1,3 @@
1
+ import { type ApiRouteDeps } from "@opengeni/core";
2
+ import type { Hono } from "hono";
3
+ export declare function registerVideoGenerationRoutes(app: Hono, deps: ApiRouteDeps): void;
@@ -3,31 +3,73 @@ import type { Session } from "@opengeni/contracts";
3
3
  import { type Database, type LeaseSnapshot } from "@opengeni/db";
4
4
  import { type EventBus } from "@opengeni/events";
5
5
  import { type Observability } from "@opengeni/observability";
6
- import { SandboxChannelAService, type EstablishedSandboxSession, type RoutingSandboxSession } from "@opengeni/runtime/sandbox";
6
+ import { SandboxChannelAService, type ChannelASession, type EstablishedSandboxSession, type RoutingSandboxSession } from "@opengeni/runtime/sandbox";
7
7
  export type ChannelAServices = {
8
8
  db: Database;
9
9
  settings: Settings;
10
10
  bus: EventBus;
11
11
  observability?: Observability | undefined;
12
12
  };
13
+ export type ChannelAOperation = "fs.list" | "fs.list-batch" | "fs.read" | "fs.write" | "fs.delete" | "fs.move" | "fs.mkdir" | "git.status" | "git.diff" | "git.read-batch" | "git.log" | "git.show" | "terminal.exec" | "terminal.pty.open" | "terminal.pty.write" | "terminal.pty.resize" | "terminal.pty.close" | "browser.create" | "browser.resume" | "browser.suspend" | "browser.end" | "browser.read" | "browser.control" | "browser.attach" | "computer.create" | "computer.end" | "computer.read" | "computer.control" | "computer.attach";
13
14
  export type ChannelAContext = {
14
15
  accountId: string;
15
16
  workspaceId: string;
16
17
  session: Session;
17
18
  subjectId: string;
19
+ /** Cancel lifecycle waiting when the originating HTTP request disconnects. */
20
+ waitSignal?: AbortSignal | undefined;
21
+ /** Bounded route identity for metrics and safe operator diagnostics. */
22
+ operation?: ChannelAOperation | undefined;
23
+ };
24
+ export type ChannelAOperationFailureReason = "request_cancelled" | "provider_read_busy" | "provider_unavailable" | "lifecycle_conflict" | "request_rejected" | "unexpected";
25
+ export type ChannelAOperationFailureDiagnostic = {
26
+ reason: ChannelAOperationFailureReason;
27
+ status: number;
28
+ errorCode: "sandbox_channel_a_cancelled" | "sandbox_channel_a_provider_busy" | "sandbox_channel_a_provider_unavailable" | "sandbox_channel_a_lifecycle_conflict" | "sandbox_channel_a_operation_failed";
18
29
  };
19
30
  export type ChannelAHandle = {
20
31
  service: SandboxChannelAService;
21
32
  /** Connected Machine homes deliberately have no cloud lease. Durable PTYs
22
33
  * require a real home-provider lease and reject this null case. */
23
34
  lease: LeaseSnapshot | null;
35
+ /** Exact placement-home session established under this request's lease or
36
+ * Connected Machine fence. Unlike routingSession, this never follows a later
37
+ * active-sandbox pointer and is safe for placement-bound controllers. */
38
+ homeSession: ChannelASession;
24
39
  routingSession: RoutingSandboxSession;
25
40
  requestId: string;
26
41
  };
42
+ export type EstablishedHandleCacheKind = "read" | "process" | "none";
43
+ export declare function isChannelAHandleCacheEntryFresh(lastUsedAtMonotonicMs: number, nowMonotonicMs: number, idleTtlMs?: number): boolean;
44
+ export declare function isChannelAProcessHandleCacheEntryFresh(lastUsedAtMonotonicMs: number, nowMonotonicMs: number, idleTtlMs?: number): boolean;
27
45
  /** Reuse the exact lease-fenced provider handle across API-direct surfaces.
28
46
  * Stream capability negotiation and the first Files/Changes reads commonly run
29
47
  * back-to-back; sharing this handle avoids paying the same Modal resume twice. */
30
48
  export declare function establishCachedChannelAHandle(workspaceId: string, sessionId: string, lease: LeaseSnapshot, establish: () => Promise<EstablishedSandboxSession>): Promise<EstablishedSandboxSession>;
49
+ /**
50
+ * Run independent, side-effect-free Channel-A reads concurrently without
51
+ * releasing the direct-request holder while sibling provider commands are
52
+ * still settling. A typed temporary-unavailable failure is retried exactly
53
+ * once after every first attempt has settled; validation, conflict, not-found,
54
+ * and unknown failures are never replayed.
55
+ */
56
+ export declare function runConcurrentChannelAReads<T>(operations: readonly (() => Promise<T>)[]): Promise<T[]>;
57
+ type ChannelAReadRecoveryOptions = {
58
+ /** Modal may expose one more stale command-router route after the first
59
+ * successful handle rebuild. Keep this closed and statically bounded. */
60
+ maxFreshHandleRetries?: 1 | 2;
61
+ /** Never start another provider attempt after the originating request ends. */
62
+ waitSignal?: AbortSignal | undefined;
63
+ };
64
+ /** Retry a side-effect-free Channel-A read only after the caller has discarded
65
+ * and freshly re-established its provider handle. The ordinary provider-neutral
66
+ * contract allows one retry; Modal opts into one additional rebuild because a
67
+ * command-router rollover can outlive the first replacement handle. Provider
68
+ * commands are never replayed for validation/conflict/unknown errors, mutation
69
+ * routes never call this helper, and request cancellation stops recovery before
70
+ * another provider command begins. */
71
+ export declare function runChannelAReadWithFreshHandleRetry<T>(run: () => Promise<T>, refreshHandle: (attempt: 1 | 2) => Promise<void>, options?: ChannelAReadRecoveryOptions): Promise<T>;
72
+ export declare function shouldEvictChannelAHandleAfterError(error: unknown, cacheKind: EstablishedHandleCacheKind): boolean;
31
73
  /**
32
74
  * Run a Channel-A op against a live box, API-direct. Acquires an exact direct holder
33
75
  * (warming the box when cold), resumes by id, builds the service, runs `fn`, and
@@ -38,6 +80,17 @@ export declare function establishCachedChannelAHandle(workspaceId: string, sessi
38
80
  * otherwise). A `backend:none` session has no box -> 409 before touching it.
39
81
  */
40
82
  export declare function withChannelA<T>(services: ChannelAServices, ctx: ChannelAContext, fn: (handle: ChannelAHandle) => Promise<T>): Promise<T>;
83
+ /** Read-only API-direct seam. Separate requests for the same exact live Modal
84
+ * instance are serialized across API replicas; each request's batched reads
85
+ * remain concurrent behind that one distributed boundary. A typed temporary
86
+ * provider-channel failure gets one retry only after rebuilding the exact
87
+ * lease-fenced handle. */
88
+ export declare function withChannelARead<T>(services: ChannelAServices, ctx: ChannelAContext, fn: (handle: ChannelAHandle) => Promise<T>): Promise<T>;
41
89
  /** Map the service's typed errors to HTTP status (the §5.3 matrix). Re-throws an
42
90
  * already-HTTPException unchanged. */
43
- export declare function mapChannelAError(error: unknown): unknown;
91
+ export declare function mapChannelAError(error: unknown, waitSignal?: AbortSignal): unknown;
92
+ export declare function isChannelARequestCancellation(error: unknown, waitSignal?: AbortSignal): boolean;
93
+ /** Structural classification only: exact provider exception text, codes, URLs,
94
+ * and identifiers never cross the telemetry boundary. */
95
+ export declare function channelAOperationFailureDiagnostic(error: unknown, waitSignal?: AbortSignal): ChannelAOperationFailureDiagnostic;
96
+ export {};
@@ -1,7 +1,8 @@
1
1
  import { type Database, type MachineMetricsSample } from "@opengeni/db";
2
+ import { type AttachedBrowserInventorySnapshot as AttachedBrowserInventoryContractValue } from "@opengeni/contracts";
2
3
  import { type EventBus } from "@opengeni/events";
3
4
  import type { Observability } from "@opengeni/observability";
4
- import { Hello, type MetricsSample } from "@opengeni/agent-proto";
5
+ import { Hello, type AttachedBrowserInventorySnapshot as WireAttachedBrowserInventorySnapshot, type MetricsSample } from "@opengeni/agent-proto";
5
6
  /** The wildcard subject the agent event plane publishes heartbeats on. */
6
7
  export declare const AGENT_EVENTS_SUBJECT = "agent.*.*.events";
7
8
  /** The wildcard subject the agent publishes its connect Hello on. */
@@ -27,6 +28,10 @@ export declare function parseAgentHelloSubject(subject: string): {
27
28
  * GPU plane; cpu/mem/disk 0 is a legitimate reading the dashboard shows as 0).
28
29
  */
29
30
  export declare function wireSampleToDbSample(wire: MetricsSample): MachineMetricsSample;
31
+ /** Validate and project one wire inventory before it reaches durable browser
32
+ * discovery. Unknown enum values and unsafe uint64 counters reject the whole
33
+ * authoritative snapshot instead of partially disconnecting good endpoints. */
34
+ export declare function wireAttachedBrowserInventoryToContract(wire: WireAttachedBrowserInventorySnapshot): AttachedBrowserInventoryContractValue;
30
35
  /**
31
36
  * Ingest ONE decoded heartbeat for an enrolled machine. Resolves the enrollment's
32
37
  * accountId (needed for the RLS-scoped writes) from the enrollment row; an
@@ -65,6 +65,8 @@ export declare function attachViewer(services: ViewerServices, input: {
65
65
  workspaceId: string;
66
66
  session: Session;
67
67
  viewerId?: string;
68
+ /** Cancel lifecycle waiting when the originating HTTP request disconnects. */
69
+ waitSignal?: AbortSignal;
68
70
  }): Promise<ViewerAttachResult>;
69
71
  export type SessionGroupReadinessHold = {
70
72
  lease: LeaseSnapshot;
@@ -149,7 +151,7 @@ export type MintDesktopStreamInput = {
149
151
  session: Session;
150
152
  /** The viewer holder id the scoped token is minted for. */
151
153
  viewerId: string;
152
- /** The live lease (must be warm/draining — the box is up). A selfhosted-active
154
+ /** The live lease (must be holder-confirmed warm). A selfhosted-active
153
155
  * session may have no Modal group lease; omit and the selfhosted branch handles it. */
154
156
  lease?: LeaseSnapshot;
155
157
  /** The epoch the CALLER last observed the URL minted under. When the live
@@ -196,7 +198,7 @@ export type MintTerminalStreamInput = {
196
198
  session: Session;
197
199
  /** The viewer holder / principal id the scoped token is minted for. */
198
200
  viewerId: string;
199
- /** The live lease (must be warm/draining — the box is up). A selfhosted-active
201
+ /** The live lease (must be holder-confirmed warm). A selfhosted-active
200
202
  * session may have no Modal group lease; omit and the selfhosted branch handles it. */
201
203
  lease?: LeaseSnapshot;
202
204
  /** Test seam: override how the box is re-established by id (see
@@ -0,0 +1,26 @@
1
+ import { type Database, type TemporalScheduleCleanupClaim } from "@opengeni/db";
2
+ import type { Observability } from "@opengeni/observability";
3
+ type TemporalScheduleCleanupDependencies = {
4
+ db: Database;
5
+ deleteSchedule: (temporalScheduleId: string) => Promise<void>;
6
+ observability?: Pick<Observability, "info" | "warn" | "error">;
7
+ };
8
+ export type TemporalScheduleCleanupBatchResult = {
9
+ claimed: number;
10
+ deleted: number;
11
+ failed: number;
12
+ stale: number;
13
+ };
14
+ /** Settle exact claims concurrently; every external delete is idempotent. */
15
+ export declare function processTemporalScheduleCleanupClaims(deps: TemporalScheduleCleanupDependencies, claims: readonly TemporalScheduleCleanupClaim[]): Promise<TemporalScheduleCleanupBatchResult>;
16
+ /** Claim and process one globally bounded batch. */
17
+ export declare function drainTemporalScheduleCleanupOutbox(deps: TemporalScheduleCleanupDependencies): Promise<TemporalScheduleCleanupBatchResult>;
18
+ /**
19
+ * Start one non-overlapping reconciliation loop. PostgreSQL claims coordinate
20
+ * replicas; an API crash merely delays the exact row until its short claim
21
+ * expires.
22
+ */
23
+ export declare function startTemporalScheduleCleanupPump(deps: TemporalScheduleCleanupDependencies, options?: {
24
+ intervalMs?: number;
25
+ }): () => Promise<void>;
26
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/api-router",
3
- "version": "0.23.1",
3
+ "version": "0.26.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": {
@@ -41,25 +41,30 @@
41
41
  "@aws-sdk/client-s3": "^3.1044.0",
42
42
  "@aws-sdk/s3-request-presigner": "^3.1044.0",
43
43
  "@hono/zod-validator": "^0.7.6",
44
+ "@llamaindex/liteparse": "^1.5.3",
44
45
  "@modelcontextprotocol/sdk": "^1.29.0",
45
- "@opengeni/agent-proto": "^0.3.0",
46
- "@opengeni/codex": "^0.2.13",
47
- "@opengeni/config": "^0.12.1",
48
- "@opengeni/contracts": "^0.40.0",
49
- "@opengeni/core": "^0.21.10",
50
- "@opengeni/db": "^0.28.9",
51
- "@opengeni/documents": "^0.5.18",
52
- "@opengeni/events": "^0.3.88",
53
- "@opengeni/github": "^0.4.38",
54
- "@opengeni/network": "^0.2.0",
55
- "@opengeni/observability": "^0.5.6",
56
- "@opengeni/runtime": "^0.18.24",
57
- "@opengeni/storage": "^0.2.75",
46
+ "@opengeni/agent-proto": "^0.4.0",
47
+ "@opengeni/artifact-tool": "^0.2.2",
48
+ "@opengeni/capabilities": "^0.1.1",
49
+ "@opengeni/codemode": "^0.2.2",
50
+ "@opengeni/codex": "^0.2.15",
51
+ "@opengeni/config": "^0.13.2",
52
+ "@opengeni/contracts": "^0.44.1",
53
+ "@opengeni/core": "^0.24.1",
54
+ "@opengeni/db": "^0.31.1",
55
+ "@opengeni/documents": "^0.5.32",
56
+ "@opengeni/events": "^0.3.103",
57
+ "@opengeni/github": "^0.4.50",
58
+ "@opengeni/network": "^0.2.2",
59
+ "@opengeni/observability": "^0.6.2",
60
+ "@opengeni/runtime": "^0.19.2",
61
+ "@opengeni/storage": "^0.2.87",
58
62
  "@temporalio/client": "^1.17.0",
59
63
  "better-auth": "^1.6.14",
60
64
  "hono": "^4.12.18",
61
65
  "pg": "^8.21.0",
62
66
  "resend": "^6.12.4",
67
+ "sharp": "^0.34.5",
63
68
  "stripe": "^22.2.0",
64
69
  "zod": "^4.2.1"
65
70
  },