@opengeni/api-router 0.22.2 → 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 (124) hide show
  1. package/dist/app.d.ts +2 -2
  2. package/dist/app.js +1 -1
  3. package/dist/auth/managed-auth.d.ts +0 -30
  4. package/dist/browser-controller-authority.d.ts +43 -0
  5. package/dist/browser-state-authority.d.ts +27 -0
  6. package/dist/{chunk-HWXJW5C7.js → chunk-JIKNR5YL.js} +27993 -14546
  7. package/dist/chunk-JIKNR5YL.js.map +1 -0
  8. package/dist/codemode.d.ts +23 -0
  9. package/dist/editable-artifact-live-hints.d.ts +11 -0
  10. package/dist/editable-artifact-native-kernel.d.ts +37 -0
  11. package/dist/editable-artifact-office-import.d.ts +22 -0
  12. package/dist/editable-artifact-production.d.ts +29 -0
  13. package/dist/editable-artifact-websocket.d.ts +49 -0
  14. package/dist/editable-artifact-workspace-files.d.ts +23 -0
  15. package/dist/github-browser-flow.d.ts +6 -0
  16. package/dist/http/cors.d.ts +1 -0
  17. package/dist/http/sse.d.ts +2 -0
  18. package/dist/index.d.ts +3 -2
  19. package/dist/index.js +1824 -30
  20. package/dist/index.js.map +1 -1
  21. package/dist/integrations/api-integrations.d.ts +24 -0
  22. package/dist/integrations/atlassian.d.ts +176 -0
  23. package/dist/integrations/github-skill-source.d.ts +5 -0
  24. package/dist/integrations/google-drive.d.ts +85 -0
  25. package/dist/integrations/oauth-client.d.ts +30 -1
  26. package/dist/integrations/provider-oauth.d.ts +19 -0
  27. package/dist/integrations/slack-bot.d.ts +4 -0
  28. package/dist/integrations/slack-interactions.d.ts +14 -2
  29. package/dist/integrations/social-api.d.ts +2 -1
  30. package/dist/mcp/editable-artifact-query-schema.d.ts +4 -0
  31. package/dist/mcp/editable-artifacts.d.ts +13 -0
  32. package/dist/mcp/receipts.d.ts +28 -0
  33. package/dist/mcp/scheduled-task-view.d.ts +518 -0
  34. package/dist/mcp/server.d.ts +14 -3
  35. package/dist/memory-slack-delivery.d.ts +9 -0
  36. package/dist/routes/api-integrations.d.ts +8 -0
  37. package/dist/routes/browser-identities.d.ts +5 -0
  38. package/dist/routes/browser-sessions.d.ts +6 -0
  39. package/dist/routes/company-profile.d.ts +3 -0
  40. package/dist/routes/computer-sessions.d.ts +6 -0
  41. package/dist/routes/editable-artifacts.d.ts +44 -0
  42. package/dist/routes/integration-features.d.ts +3 -0
  43. package/dist/routes/memory-slack-publications.d.ts +6 -0
  44. package/dist/routes/plugins.d.ts +8 -0
  45. package/dist/routes/sessions.d.ts +17 -2
  46. package/dist/routes/skills.d.ts +6 -0
  47. package/dist/routes/video-generation.d.ts +3 -0
  48. package/dist/sandbox/auth-callout.d.ts +2 -0
  49. package/dist/sandbox/channel-a.d.ts +59 -2
  50. package/dist/sandbox/metrics-ingestion.d.ts +6 -1
  51. package/dist/sandbox/viewer.d.ts +4 -2
  52. package/dist/temporal-schedule-cleanup.d.ts +26 -0
  53. package/package.json +19 -14
  54. package/src/app.ts +277 -41
  55. package/src/auth/managed-auth.ts +0 -16
  56. package/src/browser-controller-authority.ts +137 -0
  57. package/src/browser-state-authority.ts +236 -0
  58. package/src/codemode.ts +186 -0
  59. package/src/editable-artifact-live-hints.ts +64 -0
  60. package/src/editable-artifact-native-kernel.ts +659 -0
  61. package/src/editable-artifact-office-import.ts +230 -0
  62. package/src/editable-artifact-production.ts +419 -0
  63. package/src/editable-artifact-websocket.ts +311 -0
  64. package/src/editable-artifact-workspace-files.ts +186 -0
  65. package/src/github-browser-flow.ts +35 -6
  66. package/src/http/auth.ts +2 -0
  67. package/src/http/cors.ts +3 -0
  68. package/src/http/sse.ts +101 -6
  69. package/src/index.ts +147 -23
  70. package/src/integrations/api-integrations.ts +350 -0
  71. package/src/integrations/atlassian.ts +1621 -0
  72. package/src/integrations/github-skill-source.ts +142 -0
  73. package/src/integrations/google-drive.ts +1000 -64
  74. package/src/integrations/oauth-client.ts +159 -89
  75. package/src/integrations/provider-oauth.ts +777 -0
  76. package/src/integrations/slack-bot.ts +31 -2
  77. package/src/integrations/slack-interactions.ts +610 -42
  78. package/src/integrations/social-api.ts +11 -0
  79. package/src/mcp/documents.ts +74 -26
  80. package/src/mcp/editable-artifact-query-schema.ts +236 -0
  81. package/src/mcp/editable-artifacts.ts +448 -0
  82. package/src/mcp/receipts.ts +95 -0
  83. package/src/mcp/scheduled-task-view.ts +642 -0
  84. package/src/mcp/server.ts +1718 -310
  85. package/src/memory-slack-delivery.ts +209 -0
  86. package/src/observability.ts +3 -3
  87. package/src/routes/api-integrations.ts +407 -0
  88. package/src/routes/api-keys.ts +7 -1
  89. package/src/routes/browser-identities.ts +136 -0
  90. package/src/routes/browser-sessions.ts +2543 -0
  91. package/src/routes/codex.ts +7 -4
  92. package/src/routes/company-profile.ts +255 -0
  93. package/src/routes/computer-sessions.ts +1247 -0
  94. package/src/routes/connections.ts +358 -102
  95. package/src/routes/documents.ts +22 -3
  96. package/src/routes/editable-artifacts.ts +1159 -0
  97. package/src/routes/enrollments.ts +54 -12
  98. package/src/routes/environments.ts +60 -11
  99. package/src/routes/files.ts +277 -65
  100. package/src/routes/github.ts +18 -2
  101. package/src/routes/install.ts +38 -2
  102. package/src/routes/integration-features.ts +258 -0
  103. package/src/routes/machines.ts +1 -1
  104. package/src/routes/memory-slack-publications.ts +216 -0
  105. package/src/routes/packs.ts +437 -7
  106. package/src/routes/plugins.ts +751 -0
  107. package/src/routes/rigs.ts +77 -20
  108. package/src/routes/scheduled-tasks.ts +94 -42
  109. package/src/routes/sessions.ts +475 -234
  110. package/src/routes/skills.ts +174 -0
  111. package/src/routes/transcription-recordings.ts +65 -33
  112. package/src/routes/video-generation.ts +132 -0
  113. package/src/routes/workspaces.ts +46 -24
  114. package/src/sandbox/auth-callout.ts +16 -4
  115. package/src/sandbox/channel-a.ts +809 -85
  116. package/src/sandbox/enrollment.ts +13 -3
  117. package/src/sandbox/machines.ts +1 -1
  118. package/src/sandbox/metrics-ingestion.ts +121 -3
  119. package/src/sandbox/rematerialize.ts +35 -47
  120. package/src/sandbox/viewer.ts +58 -29
  121. package/src/temporal-schedule-cleanup.ts +135 -0
  122. package/dist/chunk-HWXJW5C7.js.map +0 -1
  123. package/dist/mcp/toolspace.d.ts +0 -62
  124. package/src/mcp/toolspace.ts +0 -1186
@@ -0,0 +1,642 @@
1
+ import type {
2
+ ResourceRef,
3
+ ScheduledTask,
4
+ ScheduledTaskScheduleSpec,
5
+ ToolRef,
6
+ } from "@opengeni/contracts";
7
+
8
+ export const SCHEDULED_TASK_MCP_MAX_BYTES = 64 * 1024;
9
+ export const SCHEDULED_TASK_NAME_MAX_BYTES = 512;
10
+ export const SCHEDULED_TASK_PROMPT_MAX_BYTES = 8 * 1024;
11
+ export const SCHEDULED_TASK_SCHEDULE_FIELD_MAX_BYTES = 256;
12
+ const SCHEDULED_TASK_GOAL_FIELD_MAX_BYTES = 2 * 1024;
13
+ const SCHEDULED_TASK_MODEL_MAX_BYTES = 512;
14
+ const SCHEDULED_TASK_TIMESTAMP_MAX_BYTES = 128;
15
+ const SCHEDULED_TASK_IDENTITY_PREVIEW_LIMIT = 20;
16
+
17
+ type Utf8Projection = {
18
+ value: string;
19
+ originalBytes: number;
20
+ deliveredBytes: number;
21
+ truncated: boolean;
22
+ };
23
+
24
+ export type ScheduledTaskUtf8ProjectionFact = Omit<Utf8Projection, "value">;
25
+
26
+ type ScheduledTaskScheduleProjection =
27
+ | {
28
+ schedule: Extract<ScheduledTaskScheduleSpec, { type: "manual" }>;
29
+ projection: {
30
+ type: "manual";
31
+ truncated: false;
32
+ fields: Record<string, never>;
33
+ };
34
+ }
35
+ | {
36
+ schedule: Extract<ScheduledTaskScheduleSpec, { type: "once" }>;
37
+ projection: {
38
+ type: "once";
39
+ truncated: boolean;
40
+ fields: {
41
+ runAt: ScheduledTaskUtf8ProjectionFact;
42
+ timeZone: ScheduledTaskUtf8ProjectionFact;
43
+ };
44
+ };
45
+ }
46
+ | {
47
+ schedule: Extract<ScheduledTaskScheduleSpec, { type: "interval" }>;
48
+ projection: {
49
+ type: "interval";
50
+ truncated: boolean;
51
+ fields: {
52
+ startAt: ScheduledTaskUtf8ProjectionFact | null;
53
+ endAt: ScheduledTaskUtf8ProjectionFact | null;
54
+ };
55
+ };
56
+ }
57
+ | {
58
+ schedule: Extract<ScheduledTaskScheduleSpec, { type: "calendar" }>;
59
+ projection: {
60
+ type: "calendar";
61
+ truncated: boolean;
62
+ fields: {
63
+ timeZone: ScheduledTaskUtf8ProjectionFact;
64
+ };
65
+ };
66
+ };
67
+
68
+ type DetailBudget = {
69
+ promptBytes: number;
70
+ goalFieldBytes: number;
71
+ identityPreviewLimit: number;
72
+ resourceUriBytes: number;
73
+ resourceRefBytes: number;
74
+ toolIdBytes: number;
75
+ metadataKeyBytes: number;
76
+ };
77
+
78
+ const DETAIL_BUDGETS: readonly DetailBudget[] = [
79
+ {
80
+ promptBytes: SCHEDULED_TASK_PROMPT_MAX_BYTES,
81
+ goalFieldBytes: SCHEDULED_TASK_GOAL_FIELD_MAX_BYTES,
82
+ identityPreviewLimit: SCHEDULED_TASK_IDENTITY_PREVIEW_LIMIT,
83
+ resourceUriBytes: 512,
84
+ resourceRefBytes: 256,
85
+ toolIdBytes: 256,
86
+ metadataKeyBytes: 256,
87
+ },
88
+ {
89
+ promptBytes: 4 * 1024,
90
+ goalFieldBytes: 1024,
91
+ identityPreviewLimit: 10,
92
+ resourceUriBytes: 256,
93
+ resourceRefBytes: 128,
94
+ toolIdBytes: 128,
95
+ metadataKeyBytes: 128,
96
+ },
97
+ {
98
+ promptBytes: 2 * 1024,
99
+ goalFieldBytes: 512,
100
+ identityPreviewLimit: 5,
101
+ resourceUriBytes: 128,
102
+ resourceRefBytes: 96,
103
+ toolIdBytes: 96,
104
+ metadataKeyBytes: 96,
105
+ },
106
+ {
107
+ promptBytes: 1024,
108
+ goalFieldBytes: 256,
109
+ identityPreviewLimit: 2,
110
+ resourceUriBytes: 96,
111
+ resourceRefBytes: 64,
112
+ toolIdBytes: 64,
113
+ metadataKeyBytes: 64,
114
+ },
115
+ {
116
+ promptBytes: 512,
117
+ goalFieldBytes: 128,
118
+ identityPreviewLimit: 0,
119
+ resourceUriBytes: 64,
120
+ resourceRefBytes: 64,
121
+ toolIdBytes: 64,
122
+ metadataKeyBytes: 64,
123
+ },
124
+ ];
125
+
126
+ function utf8Prefix(value: string, maxBytes: number): string {
127
+ let index = 0;
128
+ let bytes = 0;
129
+ while (index < value.length) {
130
+ const codePoint = value.codePointAt(index)!;
131
+ const character = String.fromCodePoint(codePoint);
132
+ const characterBytes = Buffer.byteLength(character, "utf8");
133
+ if (bytes + characterBytes > maxBytes) break;
134
+ bytes += characterBytes;
135
+ index += character.length;
136
+ }
137
+ return value.slice(0, index);
138
+ }
139
+
140
+ export function projectScheduledTaskUtf8(value: string, maxBytes: number): Utf8Projection {
141
+ const byteLimit = Math.max(0, Math.floor(maxBytes));
142
+ const originalBytes = Buffer.byteLength(value, "utf8");
143
+ if (originalBytes <= byteLimit) {
144
+ return { value, originalBytes, deliveredBytes: originalBytes, truncated: false };
145
+ }
146
+ let marker = "";
147
+ let prefix = "";
148
+ let omittedBytes = originalBytes;
149
+ for (let attempt = 0; attempt < 4; attempt += 1) {
150
+ marker = `…[${omittedBytes} UTF-8 bytes omitted]`;
151
+ if (Buffer.byteLength(marker, "utf8") > byteLimit) {
152
+ marker = utf8Prefix(marker, byteLimit);
153
+ prefix = "";
154
+ break;
155
+ }
156
+ prefix = utf8Prefix(value, byteLimit - Buffer.byteLength(marker, "utf8"));
157
+ const nextOmitted = originalBytes - Buffer.byteLength(prefix, "utf8");
158
+ if (nextOmitted === omittedBytes) break;
159
+ omittedBytes = nextOmitted;
160
+ }
161
+ const projected = `${prefix}${marker}`;
162
+ return {
163
+ value: projected,
164
+ originalBytes,
165
+ deliveredBytes: Buffer.byteLength(projected, "utf8"),
166
+ truncated: true,
167
+ };
168
+ }
169
+
170
+ function projectionFact(projection: Utf8Projection): ScheduledTaskUtf8ProjectionFact {
171
+ return {
172
+ originalBytes: projection.originalBytes,
173
+ deliveredBytes: projection.deliveredBytes,
174
+ truncated: projection.truncated,
175
+ };
176
+ }
177
+
178
+ export function projectScheduledTaskSchedule(
179
+ schedule: ScheduledTaskScheduleSpec,
180
+ ): ScheduledTaskScheduleProjection {
181
+ switch (schedule.type) {
182
+ case "manual":
183
+ return {
184
+ schedule: { type: "manual" },
185
+ projection: { type: "manual", truncated: false, fields: {} },
186
+ };
187
+ case "once": {
188
+ const runAt = projectScheduledTaskUtf8(
189
+ schedule.runAt,
190
+ SCHEDULED_TASK_SCHEDULE_FIELD_MAX_BYTES,
191
+ );
192
+ const timeZone = projectScheduledTaskUtf8(
193
+ schedule.timeZone,
194
+ SCHEDULED_TASK_SCHEDULE_FIELD_MAX_BYTES,
195
+ );
196
+ return {
197
+ schedule: { type: "once", runAt: runAt.value, timeZone: timeZone.value },
198
+ projection: {
199
+ type: "once",
200
+ truncated: runAt.truncated || timeZone.truncated,
201
+ fields: { runAt: projectionFact(runAt), timeZone: projectionFact(timeZone) },
202
+ },
203
+ };
204
+ }
205
+ case "interval": {
206
+ const startAt = schedule.startAt
207
+ ? projectScheduledTaskUtf8(schedule.startAt, SCHEDULED_TASK_SCHEDULE_FIELD_MAX_BYTES)
208
+ : null;
209
+ const endAt = schedule.endAt
210
+ ? projectScheduledTaskUtf8(schedule.endAt, SCHEDULED_TASK_SCHEDULE_FIELD_MAX_BYTES)
211
+ : null;
212
+ return {
213
+ schedule: {
214
+ type: "interval",
215
+ everySeconds: schedule.everySeconds,
216
+ ...(startAt ? { startAt: startAt.value } : {}),
217
+ ...(endAt ? { endAt: endAt.value } : {}),
218
+ },
219
+ projection: {
220
+ type: "interval",
221
+ truncated: Boolean(startAt?.truncated || endAt?.truncated),
222
+ fields: {
223
+ startAt: startAt ? projectionFact(startAt) : null,
224
+ endAt: endAt ? projectionFact(endAt) : null,
225
+ },
226
+ },
227
+ };
228
+ }
229
+ case "calendar": {
230
+ const timeZone = projectScheduledTaskUtf8(
231
+ schedule.timeZone,
232
+ SCHEDULED_TASK_SCHEDULE_FIELD_MAX_BYTES,
233
+ );
234
+ return {
235
+ schedule: {
236
+ type: "calendar",
237
+ timeZone: timeZone.value,
238
+ hour: schedule.hour,
239
+ minute: schedule.minute,
240
+ ...(schedule.daysOfWeek ? { daysOfWeek: schedule.daysOfWeek } : {}),
241
+ },
242
+ projection: {
243
+ type: "calendar",
244
+ truncated: timeZone.truncated,
245
+ fields: { timeZone: projectionFact(timeZone) },
246
+ },
247
+ };
248
+ }
249
+ }
250
+ }
251
+
252
+ function jsonBytes(value: unknown): number {
253
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
254
+ }
255
+
256
+ function mcpJsonBytes(value: unknown): number {
257
+ return Buffer.byteLength(JSON.stringify(value, null, 2), "utf8");
258
+ }
259
+
260
+ function settleMeasuredBytes(
261
+ value: unknown,
262
+ read: () => number,
263
+ write: (bytes: number) => void,
264
+ ): number {
265
+ for (let attempt = 0; attempt < 16; attempt += 1) {
266
+ const bytes = mcpJsonBytes(value);
267
+ if (bytes === read()) return bytes;
268
+ write(bytes);
269
+ }
270
+ return mcpJsonBytes(value);
271
+ }
272
+
273
+ function resourceIdentity(
274
+ resource: ResourceRef,
275
+ budget: DetailBudget,
276
+ ): { value: Record<string, unknown>; truncatedFieldCount: number } {
277
+ if (resource.kind === "repository") {
278
+ const uri = projectScheduledTaskUtf8(resource.uri, budget.resourceUriBytes);
279
+ const ref = resource.ref
280
+ ? projectScheduledTaskUtf8(resource.ref, budget.resourceRefBytes)
281
+ : null;
282
+ return {
283
+ value: {
284
+ kind: resource.kind,
285
+ uri: uri.value,
286
+ ...(ref ? { ref: ref.value } : {}),
287
+ },
288
+ truncatedFieldCount: Number(uri.truncated) + Number(ref?.truncated ?? false),
289
+ };
290
+ }
291
+ return { value: { kind: resource.kind, fileId: resource.fileId }, truncatedFieldCount: 0 };
292
+ }
293
+
294
+ function toolIdentity(
295
+ tool: ToolRef,
296
+ budget: DetailBudget,
297
+ ): { value: Record<string, unknown>; truncatedFieldCount: number } {
298
+ const id = projectScheduledTaskUtf8(tool.id, budget.toolIdBytes);
299
+ return {
300
+ value: {
301
+ kind: tool.kind,
302
+ id: id.value,
303
+ ...(tool.optional !== undefined ? { optional: tool.optional } : {}),
304
+ },
305
+ truncatedFieldCount: Number(id.truncated),
306
+ };
307
+ }
308
+
309
+ export function scheduledTaskMcpSummary(task: ScheduledTask) {
310
+ const action = task.action ?? ({ kind: "agent_turn" } as const);
311
+ const name = projectScheduledTaskUtf8(task.name, SCHEDULED_TASK_NAME_MAX_BYTES);
312
+ const schedule = projectScheduledTaskSchedule(task.schedule);
313
+ const model = task.agentConfig.model
314
+ ? projectScheduledTaskUtf8(task.agentConfig.model, SCHEDULED_TASK_MODEL_MAX_BYTES)
315
+ : null;
316
+ const createdAt = projectScheduledTaskUtf8(task.createdAt, SCHEDULED_TASK_TIMESTAMP_MAX_BYTES);
317
+ const updatedAt = projectScheduledTaskUtf8(task.updatedAt, SCHEDULED_TASK_TIMESTAMP_MAX_BYTES);
318
+ const result = {
319
+ id: task.id,
320
+ name: name.value,
321
+ status: task.status,
322
+ schedule: schedule.schedule,
323
+ runMode: task.runMode,
324
+ overlapPolicy: task.overlapPolicy,
325
+ action:
326
+ action.kind === "agent_turn"
327
+ ? { kind: "agent_turn" as const }
328
+ : {
329
+ kind: action.kind,
330
+ sourceId: action.sourceId,
331
+ sourceGeneration: action.sourceGeneration,
332
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
333
+ destination: action.destination,
334
+ initiatingSubjectId: action.initiatingSubjectId,
335
+ allDescendants: action.allDescendants,
336
+ connection: {
337
+ connectionId: action.connection.connectionId,
338
+ connectionVersion: action.connection.connectionVersion,
339
+ providerDomain: action.connection.providerDomain,
340
+ kind: action.connection.kind,
341
+ ownerSubjectId: action.connection.ownerSubjectId,
342
+ },
343
+ limits: action.limits,
344
+ },
345
+ targetSessionId: task.targetSessionId,
346
+ reusableSessionId: task.reusableSessionId,
347
+ variableSetId: task.variableSetId,
348
+ rigId: task.rigId,
349
+ createdAt: createdAt.value,
350
+ updatedAt: updatedAt.value,
351
+ configuration: {
352
+ model: model?.value ?? null,
353
+ reasoningEffort: task.agentConfig.reasoningEffort ?? null,
354
+ sandboxBackend: task.agentConfig.sandboxBackend ?? null,
355
+ hasGoal: task.agentConfig.goal !== undefined,
356
+ promptBytes: Buffer.byteLength(task.agentConfig.prompt, "utf8"),
357
+ resourceCount: task.agentConfig.resources.length,
358
+ toolCount: task.agentConfig.tools.length,
359
+ metadataKeyCount: Object.keys(task.agentConfig.metadata).length,
360
+ taskMetadataKeyCount: Object.keys(task.metadata).length,
361
+ },
362
+ projection: {
363
+ bounded: true,
364
+ name: projectionFact(name),
365
+ schedule: schedule.projection,
366
+ model: model ? projectionFact(model) : null,
367
+ createdAt: projectionFact(createdAt),
368
+ updatedAt: projectionFact(updatedAt),
369
+ bytes: 0,
370
+ maxBytes: SCHEDULED_TASK_MCP_MAX_BYTES,
371
+ },
372
+ };
373
+ settleMeasuredBytes(
374
+ result,
375
+ () => result.projection.bytes,
376
+ (bytes) => {
377
+ result.projection.bytes = bytes;
378
+ },
379
+ );
380
+ return result;
381
+ }
382
+
383
+ function buildScheduledTaskDetailMcp(
384
+ task: ScheduledTask,
385
+ budget: DetailBudget,
386
+ reducedForBytes: boolean,
387
+ ) {
388
+ const prompt = projectScheduledTaskUtf8(task.agentConfig.prompt, budget.promptBytes);
389
+ const goalText = task.agentConfig.goal
390
+ ? projectScheduledTaskUtf8(task.agentConfig.goal.text, budget.goalFieldBytes)
391
+ : null;
392
+ const goalCriteria = task.agentConfig.goal?.successCriteria
393
+ ? projectScheduledTaskUtf8(task.agentConfig.goal.successCriteria, budget.goalFieldBytes)
394
+ : null;
395
+ const resourceProjections = task.agentConfig.resources
396
+ .slice(0, budget.identityPreviewLimit)
397
+ .map((resource) => resourceIdentity(resource, budget));
398
+ const toolProjections = task.agentConfig.tools
399
+ .slice(0, budget.identityPreviewLimit)
400
+ .map((tool) => toolIdentity(tool, budget));
401
+ const resources = resourceProjections.map((projection) => projection.value);
402
+ const tools = toolProjections.map((projection) => projection.value);
403
+ const metadataKeyProjections = Object.keys(task.agentConfig.metadata)
404
+ .sort()
405
+ .slice(0, budget.identityPreviewLimit)
406
+ .map((key) => projectScheduledTaskUtf8(key, budget.metadataKeyBytes));
407
+ const taskMetadataKeyProjections = Object.keys(task.metadata)
408
+ .sort()
409
+ .slice(0, budget.identityPreviewLimit)
410
+ .map((key) => projectScheduledTaskUtf8(key, budget.metadataKeyBytes));
411
+ const metadataKeys = metadataKeyProjections.map((projection) => projection.value);
412
+ const taskMetadataKeys = taskMetadataKeyProjections.map((projection) => projection.value);
413
+
414
+ const result = {
415
+ ...scheduledTaskMcpSummary(task),
416
+ entity: {
417
+ agentConfig: {
418
+ prompt: prompt.value,
419
+ resources,
420
+ tools,
421
+ metadataKeys,
422
+ model: task.agentConfig.model
423
+ ? projectScheduledTaskUtf8(task.agentConfig.model, SCHEDULED_TASK_MODEL_MAX_BYTES).value
424
+ : null,
425
+ reasoningEffort: task.agentConfig.reasoningEffort ?? null,
426
+ sandboxBackend: task.agentConfig.sandboxBackend ?? null,
427
+ goal: goalText
428
+ ? {
429
+ text: goalText.value,
430
+ successCriteria: goalCriteria?.value ?? null,
431
+ maxAutoContinuations: task.agentConfig.goal?.maxAutoContinuations ?? null,
432
+ }
433
+ : null,
434
+ },
435
+ taskMetadataKeys,
436
+ },
437
+ detailProjection: {
438
+ bounded: true,
439
+ fullEntityAvailableViaRest: true,
440
+ reducedForBytes,
441
+ terminal: false,
442
+ prompt: projectionFact(prompt),
443
+ goalText: goalText ? projectionFact(goalText) : null,
444
+ goalSuccessCriteria: goalCriteria ? projectionFact(goalCriteria) : null,
445
+ resources: {
446
+ originalCount: task.agentConfig.resources.length,
447
+ deliveredCount: resources.length,
448
+ originalBytes: jsonBytes(task.agentConfig.resources),
449
+ truncatedIdentityFieldCount: resourceProjections.reduce(
450
+ (count, projection) => count + projection.truncatedFieldCount,
451
+ 0,
452
+ ),
453
+ },
454
+ tools: {
455
+ originalCount: task.agentConfig.tools.length,
456
+ deliveredCount: tools.length,
457
+ originalBytes: jsonBytes(task.agentConfig.tools),
458
+ truncatedIdentityFieldCount: toolProjections.reduce(
459
+ (count, projection) => count + projection.truncatedFieldCount,
460
+ 0,
461
+ ),
462
+ },
463
+ metadata: {
464
+ originalKeyCount: Object.keys(task.agentConfig.metadata).length,
465
+ deliveredKeyCount: metadataKeys.length,
466
+ originalBytes: jsonBytes(task.agentConfig.metadata),
467
+ truncatedKeyCount: metadataKeyProjections.filter((projection) => projection.truncated)
468
+ .length,
469
+ valuesIncluded: false,
470
+ },
471
+ taskMetadata: {
472
+ originalKeyCount: Object.keys(task.metadata).length,
473
+ deliveredKeyCount: taskMetadataKeys.length,
474
+ originalBytes: jsonBytes(task.metadata),
475
+ truncatedKeyCount: taskMetadataKeyProjections.filter((projection) => projection.truncated)
476
+ .length,
477
+ valuesIncluded: false,
478
+ },
479
+ bytes: 0,
480
+ maxBytes: SCHEDULED_TASK_MCP_MAX_BYTES,
481
+ },
482
+ };
483
+ settleMeasuredBytes(
484
+ result,
485
+ () => result.detailProjection.bytes,
486
+ (bytes) => {
487
+ result.detailProjection.bytes = bytes;
488
+ },
489
+ );
490
+ return result;
491
+ }
492
+
493
+ export function boundScheduledTaskDetailMcp(task: ScheduledTask) {
494
+ for (const [index, budget] of DETAIL_BUDGETS.entries()) {
495
+ const result = buildScheduledTaskDetailMcp(task, budget, index > 0);
496
+ if (result.detailProjection.bytes <= SCHEDULED_TASK_MCP_MAX_BYTES) return result;
497
+ }
498
+
499
+ const summary = scheduledTaskMcpSummary(task);
500
+ const prompt = projectScheduledTaskUtf8(task.agentConfig.prompt, 128);
501
+ const goalText = task.agentConfig.goal
502
+ ? projectScheduledTaskUtf8(task.agentConfig.goal.text, 64)
503
+ : null;
504
+ const goalCriteria = task.agentConfig.goal?.successCriteria
505
+ ? projectScheduledTaskUtf8(task.agentConfig.goal.successCriteria, 64)
506
+ : null;
507
+ const terminal = {
508
+ ...summary,
509
+ entity: {
510
+ agentConfig: {
511
+ prompt: prompt.value,
512
+ resources: [],
513
+ tools: [],
514
+ metadataKeys: [],
515
+ model: null,
516
+ reasoningEffort: task.agentConfig.reasoningEffort ?? null,
517
+ sandboxBackend: task.agentConfig.sandboxBackend ?? null,
518
+ goal: goalText
519
+ ? {
520
+ text: goalText.value,
521
+ successCriteria: goalCriteria?.value ?? null,
522
+ maxAutoContinuations: task.agentConfig.goal?.maxAutoContinuations ?? null,
523
+ }
524
+ : null,
525
+ },
526
+ taskMetadataKeys: [],
527
+ },
528
+ detailProjection: {
529
+ bounded: true,
530
+ fullEntityAvailableViaRest: true,
531
+ reducedForBytes: true,
532
+ terminal: true,
533
+ reason: "detail_projection_exceeded_model_envelope",
534
+ nextAction: {
535
+ surface: "REST",
536
+ resourceType: "scheduled_task",
537
+ resourceId: task.id,
538
+ },
539
+ prompt: projectionFact(prompt),
540
+ goalText: goalText ? projectionFact(goalText) : null,
541
+ goalSuccessCriteria: goalCriteria ? projectionFact(goalCriteria) : null,
542
+ resources: {
543
+ originalCount: task.agentConfig.resources.length,
544
+ deliveredCount: 0,
545
+ originalBytes: jsonBytes(task.agentConfig.resources),
546
+ truncatedIdentityFieldCount: 0,
547
+ },
548
+ tools: {
549
+ originalCount: task.agentConfig.tools.length,
550
+ deliveredCount: 0,
551
+ originalBytes: jsonBytes(task.agentConfig.tools),
552
+ truncatedIdentityFieldCount: 0,
553
+ },
554
+ metadata: {
555
+ originalKeyCount: Object.keys(task.agentConfig.metadata).length,
556
+ deliveredKeyCount: 0,
557
+ originalBytes: jsonBytes(task.agentConfig.metadata),
558
+ truncatedKeyCount: 0,
559
+ valuesIncluded: false,
560
+ },
561
+ taskMetadata: {
562
+ originalKeyCount: Object.keys(task.metadata).length,
563
+ deliveredKeyCount: 0,
564
+ originalBytes: jsonBytes(task.metadata),
565
+ truncatedKeyCount: 0,
566
+ valuesIncluded: false,
567
+ },
568
+ bytes: 0,
569
+ maxBytes: SCHEDULED_TASK_MCP_MAX_BYTES,
570
+ },
571
+ };
572
+ settleMeasuredBytes(
573
+ terminal,
574
+ () => terminal.detailProjection.bytes,
575
+ (bytes) => {
576
+ terminal.detailProjection.bytes = bytes;
577
+ },
578
+ );
579
+ return terminal;
580
+ }
581
+
582
+ export function boundScheduledTaskMcpPage(input: {
583
+ tasks: ScheduledTask[];
584
+ limit: number;
585
+ offset: number;
586
+ sourceHasMore: boolean;
587
+ maxBytes?: number;
588
+ }) {
589
+ const maxBytes = Math.max(8 * 1024, input.maxBytes ?? SCHEDULED_TASK_MCP_MAX_BYTES);
590
+ let summaries = input.tasks.map(scheduledTaskMcpSummary);
591
+
592
+ const build = () => {
593
+ const droppedTasks = input.tasks.slice(summaries.length);
594
+ const rowsDroppedForBytes = droppedTasks.length > 0;
595
+ const hasMore = input.sourceHasMore || rowsDroppedForBytes;
596
+ const nextOffset = hasMore && input.tasks.length > 0 ? input.offset + input.tasks.length : null;
597
+ const page = {
598
+ tasks: summaries,
599
+ page: {
600
+ limit: input.limit,
601
+ offset: input.offset,
602
+ hasMore,
603
+ nextOffset,
604
+ },
605
+ projection: {
606
+ bounded: true,
607
+ rowsDroppedForBytes,
608
+ droppedRowCount: droppedTasks.length,
609
+ droppedRows: droppedTasks.map((task) => ({
610
+ id: task.id,
611
+ nextAction: {
612
+ tool: "scheduled_tasks_get",
613
+ arguments: { id: task.id, includeEntity: false },
614
+ },
615
+ })),
616
+ sourceRowsConsumed: input.tasks.length,
617
+ rowsReturned: summaries.length,
618
+ terminal: hasMore && nextOffset === null,
619
+ ...(hasMore && nextOffset === null
620
+ ? { reason: "source_reported_more_rows_without_a_consumed_row" }
621
+ : {}),
622
+ bytes: 0,
623
+ maxBytes,
624
+ },
625
+ };
626
+ settleMeasuredBytes(
627
+ page,
628
+ () => page.projection.bytes,
629
+ (bytes) => {
630
+ page.projection.bytes = bytes;
631
+ },
632
+ );
633
+ return page;
634
+ };
635
+
636
+ let page = build();
637
+ while (page.projection.bytes > maxBytes && summaries.length > 0) {
638
+ summaries = summaries.slice(0, -1);
639
+ page = build();
640
+ }
641
+ return page;
642
+ }