@opengeni/api-router 0.5.2 → 0.5.4

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 (48) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/{chunk-YY6OAEL6.js → chunk-DO2G3JSB.js} +5333 -2205
  3. package/dist/chunk-DO2G3JSB.js.map +1 -0
  4. package/dist/index.d.ts +2 -1
  5. package/dist/index.js +297 -54
  6. package/dist/index.js.map +1 -1
  7. package/package.json +21 -21
  8. package/src/app.ts +415 -147
  9. package/src/auth/managed-auth.ts +32 -16
  10. package/src/http/auth.ts +8 -1
  11. package/src/http/common.ts +6 -2
  12. package/src/http/sse.ts +27 -6
  13. package/src/index.ts +196 -74
  14. package/src/integrations/oauth-client.ts +592 -131
  15. package/src/integrations/provider-domain.ts +4 -1
  16. package/src/mcp/documents.ts +173 -94
  17. package/src/mcp/server.ts +1517 -692
  18. package/src/mcp/session-view.ts +8 -2
  19. package/src/mcp/toolspace.ts +175 -84
  20. package/src/observability.ts +7 -1
  21. package/src/routes/api-keys.ts +39 -23
  22. package/src/routes/billing.ts +180 -65
  23. package/src/routes/capabilities.ts +17 -8
  24. package/src/routes/catalog-assets.ts +5 -2
  25. package/src/routes/codex.ts +244 -63
  26. package/src/routes/connections.ts +72 -34
  27. package/src/routes/documents.ts +242 -92
  28. package/src/routes/enrollments.ts +100 -70
  29. package/src/routes/environments.ts +205 -136
  30. package/src/routes/files.ts +164 -39
  31. package/src/routes/github.ts +123 -50
  32. package/src/routes/install.ts +9 -2
  33. package/src/routes/machines.ts +9 -8
  34. package/src/routes/packs.ts +141 -89
  35. package/src/routes/rigs.ts +189 -0
  36. package/src/routes/scheduled-tasks.ts +51 -9
  37. package/src/routes/sessions.ts +839 -329
  38. package/src/routes/social.ts +50 -38
  39. package/src/routes/workspace-capture.ts +238 -0
  40. package/src/routes/workspaces.ts +159 -13
  41. package/src/sandbox/access.ts +11 -3
  42. package/src/sandbox/auth-callout.ts +5 -1
  43. package/src/sandbox/channel-a.ts +104 -27
  44. package/src/sandbox/enrollment.ts +13 -3
  45. package/src/sandbox/machines.ts +68 -59
  46. package/src/sandbox/metrics-ingestion.ts +238 -17
  47. package/src/sandbox/viewer.ts +172 -46
  48. package/dist/chunk-YY6OAEL6.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { getSettings } from '@opengeni/config';
2
2
  import { ScheduledTaskOverlapPolicy, ScheduledTaskScheduleSpec } from '@opengeni/contracts';
3
+ import { Database } from '@opengeni/db';
3
4
  import { ScheduleOverlapPolicy, ScheduleSpec } from '@temporalio/client';
4
5
  import { SessionWorkflowClient, DocumentIndexClient } from '@opengeni/core';
5
6
 
6
- declare function createTemporalWorkflowClient(settings: ReturnType<typeof getSettings>): Promise<{
7
+ declare function createTemporalWorkflowClient(settings: ReturnType<typeof getSettings>, db: Database): Promise<{
7
8
  client: SessionWorkflowClient;
8
9
  documentIndexer: DocumentIndexClient;
9
10
  close: () => Promise<void>;
package/dist/index.js CHANGED
@@ -1,13 +1,26 @@
1
1
  import {
2
2
  createApp
3
- } from "./chunk-YY6OAEL6.js";
3
+ } from "./chunk-DO2G3JSB.js";
4
4
 
5
5
  // src/index.ts
6
- import { dbSearchPath, getSettings, resolveNatsCalloutConfig, resolveNatsControlPlaneAuth, retryStartupDependency, startupRetryOptions } from "@opengeni/config";
7
- import { createDb } from "@opengeni/db";
6
+ import {
7
+ dbSearchPath,
8
+ getSettings,
9
+ resolveNatsCalloutConfig,
10
+ resolveNatsControlPlaneAuth,
11
+ retryStartupDependency,
12
+ startupRetryOptions
13
+ } from "@opengeni/config";
14
+ import { createDb, markSessionWorkflowWakeDelivered } from "@opengeni/db";
8
15
  import { createNatsEventBus } from "@opengeni/events";
9
16
  import { createObservability, logStartupDependencyRetry } from "@opengeni/observability";
10
- import { Connection, Client as TemporalClient, ScheduleNotFoundError, ScheduleOverlapPolicy, WorkflowExecutionAlreadyStartedError } from "@temporalio/client";
17
+ import {
18
+ Connection,
19
+ Client as TemporalClient,
20
+ ScheduleNotFoundError,
21
+ ScheduleOverlapPolicy,
22
+ WorkflowExecutionAlreadyStartedError
23
+ } from "@temporalio/client";
11
24
 
12
25
  // src/observability.ts
13
26
  function observabilityEventLogger(observability) {
@@ -38,7 +51,9 @@ function eventAttributeValue(value) {
38
51
  }
39
52
 
40
53
  // src/sandbox/auth-callout.ts
41
- import { resolveEnrollmentSigningSecret } from "@opengeni/config";
54
+ import {
55
+ resolveEnrollmentSigningSecret
56
+ } from "@opengeni/config";
42
57
  import { verifyEnrollmentBearer } from "@opengeni/contracts";
43
58
  import { getEnrollment } from "@opengeni/db";
44
59
  import {
@@ -135,12 +150,22 @@ async function startAuthCalloutResponder(deps, natsUrl) {
135
150
 
136
151
  // src/sandbox/metrics-ingestion.ts
137
152
  import {
153
+ clearEnrollmentWentOffline,
138
154
  getEnrollment as getEnrollment2,
139
155
  ingestMachineMetricsSample,
156
+ sessionsWithActiveOpOnEnrollment,
140
157
  setEnrollmentDisplayState,
158
+ setEnrollmentOpStreamState,
159
+ setEnrollmentWentOffline,
141
160
  touchEnrollmentLastSeen
142
161
  } from "@opengeni/db";
143
- import { AgentEvent, Hello } from "@opengeni/agent-proto";
162
+ import { appendAndPublishEvents } from "@opengeni/events";
163
+ import {
164
+ AgentEvent,
165
+ GoingOfflineReason,
166
+ Hello,
167
+ goingOfflineReasonToJSON
168
+ } from "@opengeni/agent-proto";
144
169
  var AGENT_EVENTS_SUBJECT = "agent.*.*.events";
145
170
  var AGENT_HELLO_SUBJECT = "agent.*.*.hello";
146
171
  function parseAgentSubject(subject, tail) {
@@ -196,7 +221,27 @@ async function ingestHeartbeat(db, input) {
196
221
  });
197
222
  return { ingested: true, seriesAppended: result.seriesAppended };
198
223
  }
199
- async function handleAgentEventPayload(db, observability, payload, subject) {
224
+ async function fanOutMachineLinkEvents(db, bus, observability, workspaceId, enrollmentId, build) {
225
+ const sessions = await sessionsWithActiveOpOnEnrollment(db, { workspaceId, enrollmentId });
226
+ for (const session of sessions) {
227
+ try {
228
+ await appendAndPublishEvents(
229
+ db,
230
+ bus,
231
+ workspaceId,
232
+ session.sessionId,
233
+ build(session.activeTurnId)
234
+ );
235
+ } catch (error) {
236
+ observability?.warn?.("Failed to fan out a machine-link event to a session", {
237
+ workspaceId,
238
+ sessionId: session.sessionId,
239
+ error: error instanceof Error ? error.message : String(error)
240
+ });
241
+ }
242
+ }
243
+ }
244
+ async function handleAgentEventPayload(db, observability, payload, subject, bus) {
200
245
  const ids = parseAgentEventSubject(subject);
201
246
  if (!ids) {
202
247
  return;
@@ -211,6 +256,54 @@ async function handleAgentEventPayload(db, observability, payload, subject) {
211
256
  });
212
257
  return;
213
258
  }
259
+ if (event.event?.$case === "goingOffline") {
260
+ const reason = goingOfflineReasonToJSON(event.event.goingOffline.reason);
261
+ observability?.incrementCounter({
262
+ name: "opengeni_machine_going_offline_total",
263
+ help: "Total Connected Machine clean GoingOffline signals by typed reason.",
264
+ labels: { reason }
265
+ });
266
+ try {
267
+ const enrollment = await getEnrollment2(db, ids.workspaceId, ids.agentId);
268
+ if (enrollment) {
269
+ await setEnrollmentWentOffline(db, {
270
+ accountId: enrollment.accountId,
271
+ workspaceId: ids.workspaceId,
272
+ enrollmentId: ids.agentId,
273
+ reason
274
+ });
275
+ if (bus) {
276
+ const isSelfUpdate = event.event.goingOffline.reason === GoingOfflineReason.GOING_OFFLINE_REASON_UPDATE;
277
+ await fanOutMachineLinkEvents(
278
+ db,
279
+ bus,
280
+ observability,
281
+ ids.workspaceId,
282
+ ids.agentId,
283
+ (activeTurnId) => {
284
+ const events = [
285
+ { type: "machine.link.lost", turnId: activeTurnId, payload: { reason } }
286
+ ];
287
+ if (isSelfUpdate) {
288
+ events.push({
289
+ type: "machine.runner.restarted",
290
+ turnId: activeTurnId,
291
+ payload: {}
292
+ });
293
+ }
294
+ return events;
295
+ }
296
+ );
297
+ }
298
+ }
299
+ } catch (error) {
300
+ observability?.warn?.("Failed to record a machine clean going-offline", {
301
+ subject,
302
+ error: error instanceof Error ? error.message : String(error)
303
+ });
304
+ }
305
+ return;
306
+ }
214
307
  if (event.event?.$case !== "heartbeat") {
215
308
  return;
216
309
  }
@@ -219,7 +312,11 @@ async function handleAgentEventPayload(db, observability, payload, subject) {
219
312
  return;
220
313
  }
221
314
  try {
222
- await ingestHeartbeat(db, { workspaceId: ids.workspaceId, agentId: ids.agentId, sample: metrics });
315
+ await ingestHeartbeat(db, {
316
+ workspaceId: ids.workspaceId,
317
+ agentId: ids.agentId,
318
+ sample: metrics
319
+ });
223
320
  } catch (error) {
224
321
  observability?.warn?.("Failed to ingest a machine metrics heartbeat", {
225
322
  subject,
@@ -230,7 +327,7 @@ async function handleAgentEventPayload(db, observability, payload, subject) {
230
327
  function startMetricsIngestion(deps) {
231
328
  return deps.bus.subscribeAgentEvents(
232
329
  AGENT_EVENTS_SUBJECT,
233
- (payload, subject) => handleAgentEventPayload(deps.db, deps.observability, payload, subject)
330
+ (payload, subject) => handleAgentEventPayload(deps.db, deps.observability, payload, subject, deps.bus)
234
331
  );
235
332
  }
236
333
  function helloReportsDisplay(hello) {
@@ -247,6 +344,9 @@ function helloDesktopUnavailableReason(hello) {
247
344
  const reason = hello.capabilities?.desktopUnavailableReason;
248
345
  return reason ? reason : null;
249
346
  }
347
+ function helloReportsOpStream(hello) {
348
+ return hello.capabilities?.opStream === true;
349
+ }
250
350
  async function refreshEnrollmentDisplay(db, input) {
251
351
  const desktopUnavailableReason = input.desktopUnavailableReason ?? null;
252
352
  const enrollment = await getEnrollment2(db, input.workspaceId, input.agentId);
@@ -264,7 +364,22 @@ async function refreshEnrollmentDisplay(db, input) {
264
364
  desktopUnavailableReason
265
365
  });
266
366
  }
267
- async function handleHelloPayload(db, observability, payload, subject) {
367
+ async function refreshEnrollmentOpStream(db, input) {
368
+ const enrollment = await getEnrollment2(db, input.workspaceId, input.agentId);
369
+ if (!enrollment) {
370
+ return { updated: false };
371
+ }
372
+ if (enrollment.opStream === input.opStream) {
373
+ return { updated: false };
374
+ }
375
+ return await setEnrollmentOpStreamState(db, {
376
+ accountId: enrollment.accountId,
377
+ workspaceId: input.workspaceId,
378
+ enrollmentId: input.agentId,
379
+ opStream: input.opStream
380
+ });
381
+ }
382
+ async function handleHelloPayload(db, observability, payload, subject, bus) {
268
383
  const ids = parseAgentHelloSubject(subject);
269
384
  if (!ids) {
270
385
  return;
@@ -286,8 +401,38 @@ async function handleHelloPayload(db, observability, payload, subject) {
286
401
  hasDisplay: helloReportsDisplay(hello),
287
402
  desktopUnavailableReason: helloDesktopUnavailableReason(hello)
288
403
  });
404
+ await refreshEnrollmentOpStream(db, {
405
+ workspaceId: ids.workspaceId,
406
+ agentId: ids.agentId,
407
+ opStream: helloReportsOpStream(hello)
408
+ });
409
+ } catch (error) {
410
+ observability?.warn?.("Failed to refresh an enrollment's capabilities from a Hello", {
411
+ subject,
412
+ error: error instanceof Error ? error.message : String(error)
413
+ });
414
+ }
415
+ try {
416
+ const enrollment = await getEnrollment2(db, ids.workspaceId, ids.agentId);
417
+ if (enrollment) {
418
+ const { cleared } = await clearEnrollmentWentOffline(db, {
419
+ accountId: enrollment.accountId,
420
+ workspaceId: ids.workspaceId,
421
+ enrollmentId: ids.agentId
422
+ });
423
+ if (cleared && bus) {
424
+ await fanOutMachineLinkEvents(
425
+ db,
426
+ bus,
427
+ observability,
428
+ ids.workspaceId,
429
+ ids.agentId,
430
+ (activeTurnId) => [{ type: "machine.link.restored", turnId: activeTurnId, payload: {} }]
431
+ );
432
+ }
433
+ }
289
434
  } catch (error) {
290
- observability?.warn?.("Failed to refresh an enrollment's display from a Hello", {
435
+ observability?.warn?.("Failed to clear a machine going-offline marker on a Hello", {
291
436
  subject,
292
437
  error: error instanceof Error ? error.message : String(error)
293
438
  });
@@ -296,7 +441,7 @@ async function handleHelloPayload(db, observability, payload, subject) {
296
441
  function startHelloIngestion(deps) {
297
442
  return deps.bus.subscribeAgentEvents(
298
443
  AGENT_HELLO_SUBJECT,
299
- (payload, subject) => handleHelloPayload(deps.db, deps.observability, payload, subject)
444
+ (payload, subject) => handleHelloPayload(deps.db, deps.observability, payload, subject, deps.bus)
300
445
  );
301
446
  }
302
447
 
@@ -318,7 +463,7 @@ var TEMPORAL_MONTHS = [
318
463
  "NOVEMBER",
319
464
  "DECEMBER"
320
465
  ];
321
- async function createTemporalWorkflowClient(settings) {
466
+ async function createTemporalWorkflowClient(settings, db) {
322
467
  const connection = await Connection.connect({ address: settings.temporalHost });
323
468
  const temporal = new TemporalClient({
324
469
  connection,
@@ -328,7 +473,13 @@ async function createTemporalWorkflowClient(settings) {
328
473
  signalUserMessage: async ({ eventId, workflowId }) => {
329
474
  await temporal.workflow.getHandle(workflowId).signal("userMessage", eventId);
330
475
  },
331
- wakeSessionWorkflow: async ({ accountId, workspaceId, sessionId, workflowId }) => {
476
+ wakeSessionWorkflow: async ({
477
+ accountId,
478
+ workspaceId,
479
+ sessionId,
480
+ workflowId,
481
+ wakeRevision
482
+ }) => {
332
483
  await temporal.workflow.signalWithStart("sessionWorkflow", {
333
484
  taskQueue: settings.temporalTaskQueue,
334
485
  workflowId,
@@ -336,19 +487,85 @@ async function createTemporalWorkflowClient(settings) {
336
487
  args: [{ accountId, workspaceId, sessionId }],
337
488
  signal: "queueChanged"
338
489
  });
490
+ await markSessionWorkflowWakeDelivered(db, {
491
+ accountId,
492
+ workspaceId,
493
+ sessionId,
494
+ temporalWorkflowId: workflowId,
495
+ wakeRevision
496
+ });
339
497
  },
340
- signalApprovalDecision: async ({ eventId, workflowId }) => {
341
- await temporal.workflow.getHandle(workflowId).signal("approvalDecision", eventId);
498
+ signalCodexCapacity: async ({
499
+ accountId,
500
+ workspaceId,
501
+ sessionId,
502
+ workflowId,
503
+ wakeRevision,
504
+ workflowWakeRevision
505
+ }) => {
506
+ await temporal.workflow.signalWithStart("sessionWorkflow", {
507
+ taskQueue: settings.temporalTaskQueue,
508
+ workflowId,
509
+ workflowIdReusePolicy: "ALLOW_DUPLICATE",
510
+ args: [{ accountId, workspaceId, sessionId }],
511
+ signal: "codexCapacityChanged",
512
+ signalArgs: [wakeRevision]
513
+ });
514
+ await markSessionWorkflowWakeDelivered(db, {
515
+ accountId,
516
+ workspaceId,
517
+ sessionId,
518
+ temporalWorkflowId: workflowId,
519
+ wakeRevision: workflowWakeRevision
520
+ });
342
521
  },
343
- signalInterrupt: async ({ accountId, workspaceId, sessionId, eventId, workflowId }) => {
522
+ signalApprovalDecision: async ({
523
+ accountId,
524
+ workspaceId,
525
+ sessionId,
526
+ eventId,
527
+ workflowId,
528
+ workflowWakeRevision
529
+ }) => {
344
530
  await temporal.workflow.signalWithStart("sessionWorkflow", {
345
531
  taskQueue: settings.temporalTaskQueue,
346
532
  workflowId,
347
533
  workflowIdReusePolicy: "ALLOW_DUPLICATE",
348
534
  args: [{ accountId, workspaceId, sessionId }],
349
- signal: "interrupt",
535
+ signal: "approvalDecision",
350
536
  signalArgs: [eventId]
351
537
  });
538
+ await markSessionWorkflowWakeDelivered(db, {
539
+ accountId,
540
+ workspaceId,
541
+ sessionId,
542
+ temporalWorkflowId: workflowId,
543
+ wakeRevision: workflowWakeRevision
544
+ });
545
+ },
546
+ signalSessionControl: async ({
547
+ accountId,
548
+ workspaceId,
549
+ sessionId,
550
+ eventId,
551
+ workflowId,
552
+ workflowWakeRevision
553
+ }) => {
554
+ await temporal.workflow.signalWithStart("sessionWorkflow", {
555
+ taskQueue: settings.temporalTaskQueue,
556
+ workflowId,
557
+ workflowIdReusePolicy: "ALLOW_DUPLICATE",
558
+ args: [{ accountId, workspaceId, sessionId }],
559
+ signal: "sessionControl",
560
+ signalArgs: [eventId]
561
+ });
562
+ await markSessionWorkflowWakeDelivered(db, {
563
+ accountId,
564
+ workspaceId,
565
+ sessionId,
566
+ temporalWorkflowId: workflowId,
567
+ wakeRevision: workflowWakeRevision
568
+ });
352
569
  },
353
570
  syncScheduledTask: async ({ task }) => {
354
571
  const schedule = temporal.schedule.getHandle(task.temporalScheduleId);
@@ -372,13 +589,15 @@ async function createTemporalWorkflowClient(settings) {
372
589
  taskQueue: settings.temporalTaskQueue,
373
590
  workflowId,
374
591
  workflowIdReusePolicy: "REJECT_DUPLICATE",
375
- args: [{
376
- accountId: task.accountId,
377
- workspaceId: task.workspaceId,
378
- taskId: task.id,
379
- triggerType: "manual",
380
- agentRunUsageIdempotencyKey
381
- }]
592
+ args: [
593
+ {
594
+ accountId: task.accountId,
595
+ workspaceId: task.workspaceId,
596
+ taskId: task.id,
597
+ triggerType: "manual",
598
+ agentRunUsageIdempotencyKey
599
+ }
600
+ ]
382
601
  });
383
602
  } catch (error) {
384
603
  if (isWorkflowAlreadyStarted(error)) {
@@ -387,6 +606,24 @@ async function createTemporalWorkflowClient(settings) {
387
606
  throw error;
388
607
  }
389
608
  },
609
+ startRigVerification: async ({ workspaceId, changeId, versionId, workflowId }) => {
610
+ const targetId = changeId ?? versionId;
611
+ if (!targetId) {
612
+ throw new Error("rig verification requires changeId or versionId");
613
+ }
614
+ await temporal.workflow.start("rigVerificationWorkflow", {
615
+ taskQueue: settings.temporalTaskQueue,
616
+ workflowId: workflowId ?? `rig-verification-${targetId}-${crypto.randomUUID()}`,
617
+ workflowIdReusePolicy: "ALLOW_DUPLICATE",
618
+ args: [
619
+ {
620
+ workspaceId,
621
+ ...changeId ? { changeId } : {},
622
+ ...versionId ? { versionId } : {}
623
+ }
624
+ ]
625
+ });
626
+ },
390
627
  check: async () => {
391
628
  await connection.workflowService.getSystemInfo({});
392
629
  }
@@ -435,16 +672,16 @@ async function startApi() {
435
672
  onRetry
436
673
  }
437
674
  );
438
- workflowClient = await retryStartupDependency("Temporal", () => createTemporalWorkflowClient(settings), {
439
- ...retryOptions,
440
- onRetry
441
- });
675
+ workflowClient = await retryStartupDependency(
676
+ "Temporal",
677
+ () => createTemporalWorkflowClient(settings, dbClient.db),
678
+ {
679
+ ...retryOptions,
680
+ onRetry
681
+ }
682
+ );
442
683
  } catch (error) {
443
- await Promise.allSettled([
444
- bus?.close(),
445
- workflowClient?.close(),
446
- dbClient.close()
447
- ]);
684
+ await Promise.allSettled([bus?.close(), workflowClient?.close(), dbClient.close()]);
448
685
  throw error;
449
686
  }
450
687
  if (!bus || !workflowClient) {
@@ -535,25 +772,29 @@ function temporalScheduleSpec(schedule) {
535
772
  }
536
773
  if (schedule.type === "calendar") {
537
774
  return {
538
- calendars: [{
539
- hour: schedule.hour,
540
- minute: schedule.minute,
541
- second: 0,
542
- ...schedule.daysOfWeek ? { dayOfWeek: schedule.daysOfWeek } : {}
543
- }],
775
+ calendars: [
776
+ {
777
+ hour: schedule.hour,
778
+ minute: schedule.minute,
779
+ second: 0,
780
+ ...schedule.daysOfWeek ? { dayOfWeek: schedule.daysOfWeek } : {}
781
+ }
782
+ ],
544
783
  timezone: schedule.timeZone
545
784
  };
546
785
  }
547
786
  const runAt = new Date(schedule.runAt);
548
787
  return {
549
- calendars: [{
550
- year: runAt.getUTCFullYear(),
551
- month: temporalMonth(runAt.getUTCMonth()),
552
- dayOfMonth: runAt.getUTCDate(),
553
- hour: runAt.getUTCHours(),
554
- minute: runAt.getUTCMinutes(),
555
- second: runAt.getUTCSeconds()
556
- }],
788
+ calendars: [
789
+ {
790
+ year: runAt.getUTCFullYear(),
791
+ month: temporalMonth(runAt.getUTCMonth()),
792
+ dayOfMonth: runAt.getUTCDate(),
793
+ hour: runAt.getUTCHours(),
794
+ minute: runAt.getUTCMinutes(),
795
+ second: runAt.getUTCSeconds()
796
+ }
797
+ ],
557
798
  timezone: "UTC"
558
799
  };
559
800
  }
@@ -568,12 +809,14 @@ function temporalScheduleOptions(task, taskQueue) {
568
809
  type: "startWorkflow",
569
810
  workflowType: "scheduledTaskFireWorkflow",
570
811
  taskQueue,
571
- args: [{
572
- accountId: task.accountId,
573
- workspaceId: task.workspaceId,
574
- taskId: task.id,
575
- triggerType: "scheduled"
576
- }]
812
+ args: [
813
+ {
814
+ accountId: task.accountId,
815
+ workspaceId: task.workspaceId,
816
+ taskId: task.id,
817
+ triggerType: "scheduled"
818
+ }
819
+ ]
577
820
  },
578
821
  policies: {
579
822
  overlap: temporalOverlapPolicy(task.overlapPolicy),