@opengeni/core 0.21.2 → 0.24.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 (123) hide show
  1. package/dist/access/index.d.ts +9 -1
  2. package/dist/application/new-session-drafts.d.ts +1 -1
  3. package/dist/application/session-commands.d.ts +9 -4
  4. package/dist/billing/limits.d.ts +1 -1
  5. package/dist/chunk-6WKPWE5S.js +2380 -0
  6. package/dist/chunk-6WKPWE5S.js.map +1 -0
  7. package/dist/chunk-JJU6I5XD.js +2713 -0
  8. package/dist/chunk-JJU6I5XD.js.map +1 -0
  9. package/dist/chunk-NYPMQ7NO.js +2270 -0
  10. package/dist/chunk-NYPMQ7NO.js.map +1 -0
  11. package/dist/dependencies.d.ts +26 -6
  12. package/dist/domain/capabilities.d.ts +35 -3
  13. package/dist/domain/company-profile-durable-learning-adapter.d.ts +39 -0
  14. package/dist/domain/durable-learning-slack-publication.d.ts +72 -0
  15. package/dist/domain/editable-artifacts/agent-application.d.ts +192 -0
  16. package/dist/domain/editable-artifacts/durable-export-postgres.d.ts +4 -0
  17. package/dist/domain/editable-artifacts/durable-export.d.ts +212 -0
  18. package/dist/domain/editable-artifacts/errors.d.ts +47 -0
  19. package/dist/domain/editable-artifacts/hash.d.ts +11 -0
  20. package/dist/domain/editable-artifacts/in-memory.d.ts +99 -0
  21. package/dist/domain/editable-artifacts/index.d.ts +14 -0
  22. package/dist/domain/editable-artifacts/ports.d.ts +341 -0
  23. package/dist/domain/editable-artifacts/postgres-adapters.d.ts +13 -0
  24. package/dist/domain/editable-artifacts/service.d.ts +105 -0
  25. package/dist/domain/editable-artifacts/snapshot-compaction.d.ts +44 -0
  26. package/dist/domain/editable-artifacts/snapshot-verifier-genesis.d.ts +50 -0
  27. package/dist/domain/editable-artifacts/snapshot-verifier-materialization.d.ts +21 -0
  28. package/dist/domain/editable-artifacts/snapshot-verifier.d.ts +118 -0
  29. package/dist/domain/editable-artifacts/types.d.ts +410 -0
  30. package/dist/domain/memory-slack-delivery.d.ts +19 -0
  31. package/dist/domain/memory-slack-publication.d.ts +0 -2
  32. package/dist/domain/packs.d.ts +29 -1
  33. package/dist/domain/personal-connection-delegations.d.ts +9 -1
  34. package/dist/domain/scheduled-tasks.d.ts +27 -3
  35. package/dist/domain/session-tool-policy.d.ts +2 -2
  36. package/dist/domain/sessions.d.ts +44 -10
  37. package/dist/domain/skill-imports.d.ts +32 -0
  38. package/dist/domain/slack-publication-secret-safety.d.ts +8 -0
  39. package/dist/domain/timeline-annotations.d.ts +5 -0
  40. package/dist/domain/video-generation-capabilities.d.ts +9 -0
  41. package/dist/domain/video-generation.d.ts +47 -0
  42. package/dist/editable-artifact-live/application.d.ts +69 -0
  43. package/dist/editable-artifact-live/errors.d.ts +1 -0
  44. package/dist/editable-artifact-live/index.d.ts +8 -0
  45. package/dist/editable-artifact-live/outbox-dispatcher.d.ts +162 -0
  46. package/dist/editable-artifact-live/ports.d.ts +100 -0
  47. package/dist/editable-artifact-live/server.d.ts +71 -0
  48. package/dist/editable-artifact-live/ticket.d.ts +40 -0
  49. package/dist/editable-artifact-live/types.d.ts +213 -0
  50. package/dist/editable-artifact-live/wire.d.ts +34 -0
  51. package/dist/editable-artifact-live.d.ts +2 -0
  52. package/dist/editable-artifact-live.js +58 -0
  53. package/dist/editable-artifact-live.js.map +1 -0
  54. package/dist/editable-artifacts.d.ts +2 -0
  55. package/dist/editable-artifacts.js +179 -0
  56. package/dist/editable-artifacts.js.map +1 -0
  57. package/dist/index.d.ts +35 -25
  58. package/dist/index.js +3184 -652
  59. package/dist/index.js.map +1 -1
  60. package/dist/managed-session.d.ts +53 -0
  61. package/dist/rigs/index.d.ts +1 -1
  62. package/dist/rigs/provider-images.d.ts +31 -0
  63. package/dist/sandbox/fleet.d.ts +9 -1
  64. package/dist/sandbox/routing.d.ts +1 -0
  65. package/dist/session-authorization.d.ts +10 -1
  66. package/dist/workflow-wake-contract.d.ts +7 -0
  67. package/package.json +18 -10
  68. package/src/access/index.ts +43 -3
  69. package/src/application/new-session-drafts.ts +2 -0
  70. package/src/application/session-commands.ts +217 -116
  71. package/src/dependencies.ts +30 -2
  72. package/src/domain/capabilities.ts +672 -218
  73. package/src/domain/company-profile-durable-learning-adapter.ts +221 -0
  74. package/src/domain/durable-learning-slack-publication.ts +202 -0
  75. package/src/domain/editable-artifacts/agent-application.ts +687 -0
  76. package/src/domain/editable-artifacts/durable-export-postgres.ts +139 -0
  77. package/src/domain/editable-artifacts/durable-export.ts +734 -0
  78. package/src/domain/editable-artifacts/errors.ts +115 -0
  79. package/src/domain/editable-artifacts/hash.ts +131 -0
  80. package/src/domain/editable-artifacts/in-memory.ts +1662 -0
  81. package/src/domain/editable-artifacts/index.ts +14 -0
  82. package/src/domain/editable-artifacts/ports.ts +462 -0
  83. package/src/domain/editable-artifacts/postgres-adapters.ts +49 -0
  84. package/src/domain/editable-artifacts/service.ts +2402 -0
  85. package/src/domain/editable-artifacts/snapshot-compaction.ts +246 -0
  86. package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +235 -0
  87. package/src/domain/editable-artifacts/snapshot-verifier-materialization.ts +74 -0
  88. package/src/domain/editable-artifacts/snapshot-verifier.ts +687 -0
  89. package/src/domain/editable-artifacts/types.ts +835 -0
  90. package/src/domain/insights.ts +136 -33
  91. package/src/domain/memory-slack-delivery.ts +204 -0
  92. package/src/domain/memory-slack-publication.ts +10 -19
  93. package/src/domain/packs.ts +266 -5
  94. package/src/domain/personal-connection-delegations.ts +97 -6
  95. package/src/domain/resources.ts +10 -20
  96. package/src/domain/scheduled-tasks.ts +365 -25
  97. package/src/domain/session-tool-policy.ts +8 -2
  98. package/src/domain/sessions.ts +388 -155
  99. package/src/domain/skill-imports.ts +350 -0
  100. package/src/domain/slack-publication-secret-safety.ts +42 -0
  101. package/src/domain/timeline-annotations.ts +204 -0
  102. package/src/domain/video-generation-capabilities.ts +69 -0
  103. package/src/domain/video-generation.ts +249 -0
  104. package/src/editable-artifact-live/application.ts +182 -0
  105. package/src/editable-artifact-live/errors.ts +4 -0
  106. package/src/editable-artifact-live/index.ts +8 -0
  107. package/src/editable-artifact-live/outbox-dispatcher.ts +916 -0
  108. package/src/editable-artifact-live/ports.ts +139 -0
  109. package/src/editable-artifact-live/server.ts +1707 -0
  110. package/src/editable-artifact-live/ticket.ts +217 -0
  111. package/src/editable-artifact-live/types.ts +286 -0
  112. package/src/editable-artifact-live/wire.ts +95 -0
  113. package/src/editable-artifact-live.ts +2 -0
  114. package/src/editable-artifacts.ts +2 -0
  115. package/src/index.ts +14 -0
  116. package/src/managed-auth-type.ts +4 -3
  117. package/src/managed-session.ts +36 -0
  118. package/src/rigs/index.ts +26 -19
  119. package/src/rigs/provider-images.ts +110 -0
  120. package/src/sandbox/fleet.ts +56 -1
  121. package/src/sandbox/routing.ts +32 -3
  122. package/src/session-authorization.ts +20 -0
  123. package/src/workflow-wake-contract.ts +8 -0
@@ -16,7 +16,11 @@ import type {
16
16
  WorkspaceInferenceControlRequest,
17
17
  WorkspaceInferenceControlResponse,
18
18
  } from "@opengeni/contracts";
19
- import { latencyModeForMetadata, reasoningEffortForMetadata } from "@opengeni/contracts";
19
+ import {
20
+ DraftTimelineAnnotations,
21
+ latencyModeForMetadata,
22
+ reasoningEffortForMetadata,
23
+ } from "@opengeni/contracts";
20
24
  import {
21
25
  deleteSessionQueueItemInTransaction,
22
26
  editQueuedTurnInTransaction,
@@ -36,8 +40,11 @@ import {
36
40
  steerAgentSessionInTransaction,
37
41
  steerQueuedTurnInTransaction,
38
42
  withWorkspaceRls,
43
+ withWorkspaceSessionActivityRls,
39
44
  withWorkspaceSubjectRls,
45
+ withWorkspaceSubjectSessionActivityRls,
40
46
  type Database,
47
+ type SessionActivityDatabase,
41
48
  type SessionCommandReceiptRow,
42
49
  } from "@opengeni/db";
43
50
  import {
@@ -46,6 +53,8 @@ import {
46
53
  type EventBus,
47
54
  } from "@opengeni/events";
48
55
  import type { SessionWorkflowClient } from "../dependencies";
56
+ import { normalizeResources } from "../domain/resources";
57
+ import { validateDraftTimelineAnnotations } from "../domain/timeline-annotations";
49
58
  import {
50
59
  requireSessionAuthorization,
51
60
  type ResolvedSessionAuthorization,
@@ -142,17 +151,18 @@ function agentActor(context: AgentSessionCommandContext) {
142
151
  }
143
152
 
144
153
  /**
145
- * Retry only one operation-keyed Agent command transaction. The caller keeps
154
+ * Retry only one operation-keyed session command transaction. Callers keep
146
155
  * event publication and Temporal wake delivery after this returns, so a
147
- * deadlock/serialization retry can never replay an external effect.
156
+ * deadlock/serialization retry can never replay an external effect. Human
157
+ * draft ownership is applied inside every retry attempt when required.
148
158
  */
149
- async function runAgentCommandPersistenceTransaction<T>(
159
+ async function runSessionCommandPersistenceTransaction<T>(
150
160
  deps: { db: Database },
151
- context: AgentSessionCommandContext,
161
+ scope: { workspaceId: string; subjectId?: string },
152
162
  input: {
153
163
  stage: string;
154
164
  eventTypes: string[];
155
- transaction: (tx: Database) => Promise<T>;
165
+ transaction: (tx: SessionActivityDatabase) => Promise<T>;
156
166
  },
157
167
  ): Promise<T> {
158
168
  return await runIdempotentPersistenceTransaction(
@@ -161,10 +171,17 @@ async function runAgentCommandPersistenceTransaction<T>(
161
171
  eventTypes: input.eventTypes,
162
172
  maxAttempts: 3,
163
173
  },
164
- async () =>
165
- await withWorkspaceRls(deps.db, context.workspaceId, async (scoped) =>
166
- scoped.transaction(async (tx) => await input.transaction(tx as unknown as Database)),
167
- ),
174
+ async () => {
175
+ const run = async (scoped: SessionActivityDatabase) => await input.transaction(scoped);
176
+ return scope.subjectId === undefined
177
+ ? await withWorkspaceSessionActivityRls(deps.db, scope.workspaceId, run)
178
+ : await withWorkspaceSubjectSessionActivityRls(
179
+ deps.db,
180
+ scope.workspaceId,
181
+ scope.subjectId,
182
+ run,
183
+ );
184
+ },
168
185
  );
169
186
  }
170
187
 
@@ -200,10 +217,14 @@ async function publishAndWakeAgentCommand(
200
217
  ? { interruptionRequested: true }
201
218
  : {}),
202
219
  });
203
- } catch (error) {
220
+ } catch {
204
221
  console.warn(
205
- `[session-commands] immediate Agent command wake failed for ${input.workspaceId}/${input.sessionId}; durable outbox will retry`,
206
- error,
222
+ "[session-commands] immediate Agent command wake failed; durable outbox will retry",
223
+ {
224
+ errorClass: "WorkflowWakeOperationError",
225
+ errorCode: "agent_command_wake_failed",
226
+ origin: "core",
227
+ },
207
228
  );
208
229
  }
209
230
  }
@@ -222,10 +243,15 @@ async function requestControlWakeDispatch(
222
243
  if (wakeCount === 0) return;
223
244
  try {
224
245
  await deps.workflowClient.requestSessionWorkflowWakeDispatch();
225
- } catch (error) {
246
+ } catch {
226
247
  console.warn(
227
- `[session-commands] immediate control wake dispatch failed for ${wakeCount} committed revisions; durable outbox will retry`,
228
- error,
248
+ "[session-commands] immediate control wake dispatch failed; durable outbox will retry",
249
+ {
250
+ errorClass: "WorkflowWakeOperationError",
251
+ errorCode: "control_wake_dispatch_failed",
252
+ origin: "core",
253
+ wakeCount,
254
+ },
229
255
  );
230
256
  }
231
257
  }
@@ -272,19 +298,23 @@ export async function sendAgentSessionMessage(
272
298
  input: { targetSessionId: string; text: string; idempotencyKey: string },
273
299
  ) {
274
300
  await authorizeAgentSessionCommand(deps, context, input.targetSessionId, "session.append");
275
- const result = await runAgentCommandPersistenceTransaction(deps, context, {
276
- stage: "session_commands.agent_message",
277
- eventTypes: ["system.update.pending"],
278
- transaction: async (tx) =>
279
- await sendAgentMessageInTransaction(tx, {
280
- accountId: context.accountId,
281
- workspaceId: context.workspaceId,
282
- targetSessionId: input.targetSessionId,
283
- actor: agentActor(context),
284
- operationKey: input.idempotencyKey,
285
- text: input.text,
286
- }),
287
- });
301
+ const result = await runSessionCommandPersistenceTransaction(
302
+ deps,
303
+ { workspaceId: context.workspaceId },
304
+ {
305
+ stage: "session_commands.agent_message",
306
+ eventTypes: ["system.update.pending"],
307
+ transaction: async (tx) =>
308
+ await sendAgentMessageInTransaction(tx, {
309
+ accountId: context.accountId,
310
+ workspaceId: context.workspaceId,
311
+ targetSessionId: input.targetSessionId,
312
+ actor: agentActor(context),
313
+ operationKey: input.idempotencyKey,
314
+ text: input.text,
315
+ }),
316
+ },
317
+ );
288
318
  await publishAndWakeAgentCommand(deps, {
289
319
  accountId: context.accountId,
290
320
  workspaceId: context.workspaceId,
@@ -307,22 +337,30 @@ export async function steerAgentSession(
307
337
  sessionAuthorization?: SessionAuthorizationPort | null;
308
338
  },
309
339
  context: AgentSessionCommandContext,
310
- input: { targetSessionId: string; instruction: string; idempotencyKey: string },
340
+ input: {
341
+ targetSessionId: string;
342
+ instruction: string;
343
+ idempotencyKey: string;
344
+ },
311
345
  ) {
312
346
  await authorizeAgentSessionCommand(deps, context, input.targetSessionId, "session.steer");
313
- const result = await runAgentCommandPersistenceTransaction(deps, context, {
314
- stage: "session_commands.agent_steer",
315
- eventTypes: ["session.control.steer_requested", "system.update.pending", "turn.superseded"],
316
- transaction: async (tx) =>
317
- await steerAgentSessionInTransaction(tx, {
318
- accountId: context.accountId,
319
- workspaceId: context.workspaceId,
320
- targetSessionId: input.targetSessionId,
321
- actor: agentActor(context),
322
- operationKey: input.idempotencyKey,
323
- instruction: input.instruction,
324
- }),
325
- });
347
+ const result = await runSessionCommandPersistenceTransaction(
348
+ deps,
349
+ { workspaceId: context.workspaceId },
350
+ {
351
+ stage: "session_commands.agent_steer",
352
+ eventTypes: ["session.control.steer_requested", "system.update.pending", "turn.superseded"],
353
+ transaction: async (tx) =>
354
+ await steerAgentSessionInTransaction(tx, {
355
+ accountId: context.accountId,
356
+ workspaceId: context.workspaceId,
357
+ targetSessionId: input.targetSessionId,
358
+ actor: agentActor(context),
359
+ operationKey: input.idempotencyKey,
360
+ instruction: input.instruction,
361
+ }),
362
+ },
363
+ );
326
364
  await publishAndWakeAgentCommand(deps, {
327
365
  accountId: context.accountId,
328
366
  workspaceId: context.workspaceId,
@@ -359,18 +397,23 @@ export async function controlAgentSessionWorkstream(
359
397
  input.targetSessionId,
360
398
  "session.control",
361
399
  );
362
- const result = await withWorkspaceRls(deps.db, context.workspaceId, (scoped) =>
363
- scoped.transaction((tx) =>
364
- mutateSessionControlInTransaction(tx as unknown as Database, {
365
- accountId: context.accountId,
366
- workspaceId: context.workspaceId,
367
- sessionId: input.targetSessionId,
368
- actor: agentActor(context),
369
- operationKey: input.idempotencyKey,
370
- action: input.action,
371
- reason: input.reason ?? null,
372
- }),
373
- ),
400
+ const result = await runSessionCommandPersistenceTransaction(
401
+ deps,
402
+ { workspaceId: context.workspaceId },
403
+ {
404
+ stage: "session_commands.agent_control",
405
+ eventTypes: [input.action === "pause" ? "session.control.paused" : "session.control.resumed"],
406
+ transaction: async (tx) =>
407
+ await mutateSessionControlInTransaction(tx, {
408
+ accountId: context.accountId,
409
+ workspaceId: context.workspaceId,
410
+ sessionId: input.targetSessionId,
411
+ actor: agentActor(context),
412
+ operationKey: input.idempotencyKey,
413
+ action: input.action,
414
+ reason: input.reason ?? null,
415
+ }),
416
+ },
374
417
  );
375
418
  await publishSessionEventIds(deps, context.workspaceId, input.targetSessionId, [
376
419
  result.sessionControlEventId,
@@ -402,6 +445,7 @@ function composerDraft(
402
445
  return {
403
446
  revision: row.revision,
404
447
  text: row.text,
448
+ annotations: DraftTimelineAnnotations.parse(row.annotations),
405
449
  resources: row.resources as ComposerDraft["resources"],
406
450
  model: row.model,
407
451
  reasoningEffort: row.reasoningEffort as ComposerDraft["reasoningEffort"],
@@ -431,23 +475,32 @@ async function authoritativeQueue(
431
475
  }
432
476
 
433
477
  export async function moveHumanQueuePrompt(
434
- deps: { db: Database; bus: EventBus; sessionAuthorization?: SessionAuthorizationPort | null },
478
+ deps: {
479
+ db: Database;
480
+ bus: EventBus;
481
+ sessionAuthorization?: SessionAuthorizationPort | null;
482
+ },
435
483
  context: HumanSessionCommandContext,
436
484
  turnId: string,
437
485
  input: MoveSessionQueueItemRequest,
438
486
  ): Promise<SessionQueueMutationResponse> {
439
487
  const authorization = await authorizeHumanSessionCommand(deps, context, "session.queue.control");
440
- const result = await withWorkspaceRls(deps.db, context.workspaceId, (scoped) =>
441
- scoped.transaction((tx) =>
442
- moveQueuedTurnInTransaction(tx as unknown as Database, {
443
- ...context,
444
- turnId,
445
- beforeTurnId: input.beforeTurnId,
446
- expectedQueueVersion: input.expectedQueueVersion,
447
- actor: { type: "human", subjectId: context.subjectId },
448
- operationKey: input.clientEventId,
449
- }),
450
- ),
488
+ const result = await runSessionCommandPersistenceTransaction(
489
+ deps,
490
+ { workspaceId: context.workspaceId },
491
+ {
492
+ stage: "session_commands.human_queue_move",
493
+ eventTypes: ["session.queue.changed"],
494
+ transaction: async (tx) =>
495
+ await moveQueuedTurnInTransaction(tx, {
496
+ ...context,
497
+ turnId,
498
+ beforeTurnId: input.beforeTurnId,
499
+ expectedQueueVersion: input.expectedQueueVersion,
500
+ actor: { type: "human", subjectId: context.subjectId },
501
+ operationKey: input.clientEventId,
502
+ }),
503
+ },
451
504
  );
452
505
  const response = {
453
506
  receipt: receipt(result.receipt),
@@ -463,23 +516,32 @@ export async function moveHumanQueuePrompt(
463
516
  }
464
517
 
465
518
  export async function deleteHumanQueuePrompt(
466
- deps: { db: Database; bus: EventBus; sessionAuthorization?: SessionAuthorizationPort | null },
519
+ deps: {
520
+ db: Database;
521
+ bus: EventBus;
522
+ sessionAuthorization?: SessionAuthorizationPort | null;
523
+ },
467
524
  context: HumanSessionCommandContext,
468
525
  turnId: string,
469
526
  input: DeleteSessionQueueItemRequest,
470
527
  ): Promise<SessionQueueMutationResponse> {
471
528
  const authorization = await authorizeHumanSessionCommand(deps, context, "session.queue.control");
472
- const result = await withWorkspaceRls(deps.db, context.workspaceId, (scoped) =>
473
- scoped.transaction((tx) =>
474
- deleteSessionQueueItemInTransaction(tx as unknown as Database, {
475
- ...context,
476
- turnId,
477
- expectedTurnVersion: input.expectedTurnVersion,
478
- actor: { type: "human", subjectId: context.subjectId },
479
- operationKey: input.clientEventId,
480
- reason: input.reason ?? null,
481
- }),
482
- ),
529
+ const result = await runSessionCommandPersistenceTransaction(
530
+ deps,
531
+ { workspaceId: context.workspaceId },
532
+ {
533
+ stage: "session_commands.human_queue_delete",
534
+ eventTypes: ["session.queue.changed"],
535
+ transaction: async (tx) =>
536
+ await deleteSessionQueueItemInTransaction(tx, {
537
+ ...context,
538
+ turnId,
539
+ expectedTurnVersion: input.expectedTurnVersion,
540
+ actor: { type: "human", subjectId: context.subjectId },
541
+ operationKey: input.clientEventId,
542
+ reason: input.reason ?? null,
543
+ }),
544
+ },
483
545
  );
484
546
  const response = {
485
547
  receipt: receipt(result.receipt),
@@ -495,19 +557,24 @@ export async function deleteHumanQueuePrompt(
495
557
  }
496
558
 
497
559
  export async function editHumanQueuePrompt(
498
- deps: { db: Database; bus: EventBus; sessionAuthorization?: SessionAuthorizationPort | null },
560
+ deps: {
561
+ db: Database;
562
+ bus: EventBus;
563
+ sessionAuthorization?: SessionAuthorizationPort | null;
564
+ },
499
565
  context: HumanSessionCommandContext,
500
566
  turnId: string,
501
567
  input: EditSessionQueueItemRequest,
502
568
  ): Promise<SessionQueueMutationResponse> {
503
569
  const authorization = await authorizeHumanSessionCommand(deps, context, "session.queue.control");
504
- const result = await withWorkspaceSubjectRls(
505
- deps.db,
506
- context.workspaceId,
507
- context.subjectId,
508
- (scoped) =>
509
- scoped.transaction((tx) =>
510
- editQueuedTurnInTransaction(tx as unknown as Database, {
570
+ const result = await runSessionCommandPersistenceTransaction(
571
+ deps,
572
+ { workspaceId: context.workspaceId, subjectId: context.subjectId },
573
+ {
574
+ stage: "session_commands.human_queue_edit",
575
+ eventTypes: ["session.queue.changed"],
576
+ transaction: async (tx) =>
577
+ await editQueuedTurnInTransaction(tx, {
511
578
  ...context,
512
579
  turnId,
513
580
  expectedTurnVersion: input.expectedTurnVersion,
@@ -516,7 +583,7 @@ export async function editHumanQueuePrompt(
516
583
  actor: { type: "human", subjectId: context.subjectId },
517
584
  operationKey: input.clientEventId,
518
585
  }),
519
- ),
586
+ },
520
587
  );
521
588
  const response = {
522
589
  receipt: receipt(result.receipt),
@@ -533,23 +600,32 @@ export async function editHumanQueuePrompt(
533
600
  }
534
601
 
535
602
  export async function steerHumanQueuePrompt(
536
- deps: { db: Database; bus: EventBus; sessionAuthorization?: SessionAuthorizationPort | null },
603
+ deps: {
604
+ db: Database;
605
+ bus: EventBus;
606
+ sessionAuthorization?: SessionAuthorizationPort | null;
607
+ },
537
608
  context: HumanSessionCommandContext,
538
609
  turnId: string,
539
610
  input: SteerSessionQueueItemRequest,
540
611
  ): Promise<SessionQueueMutationResponse> {
541
612
  const authorization = await authorizeHumanSessionCommand(deps, context, "session.queue.control");
542
- const result = await withWorkspaceRls(deps.db, context.workspaceId, (scoped) =>
543
- scoped.transaction((tx) =>
544
- steerQueuedTurnInTransaction(tx as unknown as Database, {
545
- ...context,
546
- turnId,
547
- expectedTurnVersion: input.expectedTurnVersion,
548
- controlEtag: input.controlEtag ?? null,
549
- actor: { type: "human", subjectId: context.subjectId },
550
- operationKey: input.clientEventId,
551
- }),
552
- ),
613
+ const result = await runSessionCommandPersistenceTransaction(
614
+ deps,
615
+ { workspaceId: context.workspaceId },
616
+ {
617
+ stage: "session_commands.human_queue_steer",
618
+ eventTypes: ["session.control.steer_requested", "session.queue.changed", "turn.superseded"],
619
+ transaction: async (tx) =>
620
+ await steerQueuedTurnInTransaction(tx, {
621
+ ...context,
622
+ turnId,
623
+ expectedTurnVersion: input.expectedTurnVersion,
624
+ controlEtag: input.controlEtag ?? null,
625
+ actor: { type: "human", subjectId: context.subjectId },
626
+ operationKey: input.clientEventId,
627
+ }),
628
+ },
553
629
  );
554
630
  const response = {
555
631
  receipt: receipt(result.receipt),
@@ -565,7 +641,7 @@ export async function steerHumanQueuePrompt(
565
641
  return response;
566
642
  }
567
643
 
568
- export async function controlHumanSessionWorkstream(
644
+ export async function controlHumanSessionWorkstreamWithOutcome(
569
645
  deps: {
570
646
  db: Database;
571
647
  bus: EventBus;
@@ -574,21 +650,28 @@ export async function controlHumanSessionWorkstream(
574
650
  },
575
651
  context: HumanSessionCommandContext,
576
652
  input: SessionControlRequest,
577
- ): Promise<SessionControlResponse> {
653
+ ): Promise<{ response: SessionControlResponse; replay: boolean }> {
578
654
  const authorization = await authorizeHumanSessionCommand(deps, context, "session.control");
579
- const result = await withWorkspaceRls(deps.db, context.workspaceId, (scoped) =>
580
- scoped.transaction((tx) =>
581
- mutateSessionControlInTransaction(tx as unknown as Database, {
582
- accountId: context.accountId,
583
- workspaceId: context.workspaceId,
584
- sessionId: context.sessionId,
585
- actor: { type: "human", subjectId: context.subjectId },
586
- operationKey: input.clientEventId,
587
- action: input.action,
588
- reason: input.reason ?? null,
589
- expectedControlEtag: input.expectedControlEtag ?? null,
590
- }),
591
- ),
655
+ const result = await runSessionCommandPersistenceTransaction(
656
+ deps,
657
+ { workspaceId: context.workspaceId },
658
+ {
659
+ stage: "session_commands.human_control",
660
+ eventTypes: [
661
+ input.action === "resume" ? "session.control.resumed" : "session.control.paused",
662
+ ],
663
+ transaction: async (tx) =>
664
+ await mutateSessionControlInTransaction(tx, {
665
+ accountId: context.accountId,
666
+ workspaceId: context.workspaceId,
667
+ sessionId: context.sessionId,
668
+ actor: { type: "human", subjectId: context.subjectId },
669
+ operationKey: input.clientEventId,
670
+ action: input.action,
671
+ reason: input.reason ?? null,
672
+ expectedControlEtag: input.expectedControlEtag ?? null,
673
+ }),
674
+ },
592
675
  );
593
676
  const response = {
594
677
  receipt: receipt(result.receipt),
@@ -607,7 +690,16 @@ export async function controlHumanSessionWorkstream(
607
690
  }
608
691
  await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
609
692
  await requestControlWakeDispatch(deps, result.wakeCount);
610
- return response;
693
+ return { response, replay: result.replay };
694
+ }
695
+
696
+ /** Backward-compatible response path used by the REST control route. */
697
+ export async function controlHumanSessionWorkstream(
698
+ deps: Parameters<typeof controlHumanSessionWorkstreamWithOutcome>[0],
699
+ context: Parameters<typeof controlHumanSessionWorkstreamWithOutcome>[1],
700
+ input: Parameters<typeof controlHumanSessionWorkstreamWithOutcome>[2],
701
+ ): Promise<SessionControlResponse> {
702
+ return (await controlHumanSessionWorkstreamWithOutcome(deps, context, input)).response;
611
703
  }
612
704
 
613
705
  export async function controlHumanWorkspace(
@@ -667,6 +759,7 @@ export async function getHumanComposerDraft(
667
759
  return {
668
760
  revision: 0,
669
761
  text: "",
762
+ annotations: [],
670
763
  resources: [],
671
764
  model: session.model,
672
765
  reasoningEffort: reasoningEffortForMetadata(session.metadata, "medium"),
@@ -683,6 +776,12 @@ export async function saveHumanComposerDraft(
683
776
  input: SaveComposerDraftRequest,
684
777
  ): Promise<ComposerDraft> {
685
778
  await authorizeHumanSessionCommand(deps, context, "session.composer.write");
779
+ const annotations = await validateDraftTimelineAnnotations(
780
+ deps.db,
781
+ context.workspaceId,
782
+ context.sessionId,
783
+ input.annotations ?? [],
784
+ );
686
785
  const row = await withWorkspaceSubjectRls(
687
786
  deps.db,
688
787
  context.workspaceId,
@@ -692,6 +791,8 @@ export async function saveHumanComposerDraft(
692
791
  saveComposerDraftInTransaction(tx as unknown as Database, {
693
792
  ...context,
694
793
  ...input,
794
+ annotations,
795
+ resources: normalizeResources(input.resources),
695
796
  subjectId: context.subjectId,
696
797
  }),
697
798
  ),
@@ -5,6 +5,7 @@ import type {
5
5
  DocumentAuthorityKind,
6
6
  GitHubAppApiPort,
7
7
  ScheduledTask,
8
+ ScheduledTaskTriggerType,
8
9
  SessionAuthorizationPort,
9
10
  TurnInitiator,
10
11
  } from "@opengeni/contracts";
@@ -16,6 +17,12 @@ import type { createObjectStorage } from "@opengeni/storage";
16
17
  import type { ManagedAuth } from "./managed-auth-type";
17
18
  import type { ApiSandboxClient, ResumeBoxByIdInput, ResumedSandboxSession } from "./sandbox-types";
18
19
  import type { TranscriptionSegmenter, TranscriptionService } from "./transcription";
20
+ import type { EditableArtifactApplicationPort } from "./editable-artifact-live";
21
+ import type {
22
+ EditableArtifactAgentApplication,
23
+ EditableArtifactDurableExportService,
24
+ EditableArtifactOfficeImportPort,
25
+ } from "./editable-artifacts";
19
26
 
20
27
  export type SessionWorkflowClient = {
21
28
  signalUserMessage: (input: {
@@ -59,6 +66,10 @@ export type SessionWorkflowClient = {
59
66
  agentRunUsageIdempotencyKey: string;
60
67
  triggerWorkflowId: string;
61
68
  initiator: TurnInitiator;
69
+ triggerType?: Extract<
70
+ ScheduledTaskTriggerType,
71
+ "manual" | "initial" | "provider_event" | "retry" | "repair"
72
+ >;
62
73
  }) => Promise<void>;
63
74
  startRigVerification: (input: {
64
75
  workspaceId: string;
@@ -83,8 +94,22 @@ export type DocumentIndexClient = {
83
94
  export type AppDependencies = {
84
95
  settings: Settings;
85
96
  db: Database;
97
+ /**
98
+ * Host-composed editable artifact engine. Standalone startup binds the same
99
+ * native kernel/DB/object-store implementation; embedded hosts may inject an
100
+ * equivalent deployment-scoped composition.
101
+ */
102
+ editableArtifacts?: EditableArtifactApplicationPort;
103
+ /** Durable immutable version/materialization API paired with editableArtifacts. */
104
+ editableArtifactExports?: EditableArtifactDurableExportService;
105
+ /** Canonical agent-facing artifact use cases shared by MCP and Codemode. */
106
+ editableArtifactAgent?: EditableArtifactAgentApplication;
107
+ /** Trusted Office-file to canonical sequence-zero import boundary. */
108
+ editableArtifactOfficeImports?: EditableArtifactOfficeImportPort;
86
109
  bus: EventBus;
87
110
  workflowClient: SessionWorkflowClient;
111
+ /** Injectable structural seam for replayable prompt fanout and wake work. */
112
+ schedulePromptPostCommit?: (task: () => Promise<void>) => void;
88
113
  /** Optional provider override for deterministic API/object-storage tests. */
89
114
  objectStorage?: ObjectStorageDependency;
90
115
  documentIndexer?: DocumentIndexClient;
@@ -100,7 +125,7 @@ export type AppDependencies = {
100
125
  githubAppApi?: GitHubAppApiPort;
101
126
  /**
102
127
  * Optional host-owned connection credential seam. API-side consumers use
103
- * the MCP leg for Toolspace/Code Mode; worker consumers bind the same port
128
+ * the MCP leg for Codemode/Code Mode; worker consumers bind the same port
104
129
  * for model MCP, Git, and sandbox-secret resolution.
105
130
  */
106
131
  connectionCredentials?: ConnectionCredentialsPort | null;
@@ -117,6 +142,9 @@ export type AppDependencies = {
117
142
  slackFetch?: typeof fetch;
118
143
  /** Injectable Google OAuth/Drive transport for deterministic connector tests. */
119
144
  googleDriveFetch?: typeof fetch;
145
+ /** Injectable provider-preset OAuth/API transport for deterministic integration tests. */
146
+ apiIntegrationOAuthFetch?: typeof fetch;
147
+ atlassianFetch?: typeof fetch;
120
148
  /** Injectable MCP OAuth setup deadline for deterministic stalled-provider tests. */
121
149
  oauthStartDeadlineMs?: number;
122
150
  /** Injectable MCP OAuth callback deadline for deterministic stalled-provider tests. */
@@ -163,7 +191,7 @@ export type ApiRouteDeps = AppDependencies & {
163
191
  */
164
192
  export type AcceptSessionUserMessageDependencies = Pick<
165
193
  AppDependencies,
166
- "settings" | "db" | "bus" | "sessionAuthorization"
194
+ "settings" | "db" | "bus" | "sessionAuthorization" | "schedulePromptPostCommit"
167
195
  > & {
168
196
  workflowClient: Pick<SessionWorkflowClient, "wakeSessionWorkflow">;
169
197
  objectStorage: ObjectStorageDependency;