@opengeni/db 0.28.1 → 0.31.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 (187) hide show
  1. package/dist/attached-browser-devices.d.ts +42 -0
  2. package/dist/attempt-tool-catalogs.d.ts +16 -0
  3. package/dist/browser-auth.d.ts +91 -0
  4. package/dist/browser-identities.d.ts +128 -0
  5. package/dist/browser-sessions.d.ts +272 -0
  6. package/dist/browser-state-artifacts.d.ts +17 -0
  7. package/dist/capability-components.d.ts +37 -0
  8. package/dist/capability-integrations.d.ts +192 -0
  9. package/dist/chunk-F5FT7BEZ.js +3535 -0
  10. package/dist/chunk-F5FT7BEZ.js.map +1 -0
  11. package/dist/chunk-ISI2TIUG.js +639 -0
  12. package/dist/chunk-ISI2TIUG.js.map +1 -0
  13. package/dist/chunk-MWDVXQOL.js +11639 -0
  14. package/dist/chunk-MWDVXQOL.js.map +1 -0
  15. package/dist/chunk-NBE6CHRI.js +955 -0
  16. package/dist/chunk-NBE6CHRI.js.map +1 -0
  17. package/dist/{chunk-DBS5HPEW.js → chunk-YHZXQ7HP.js} +760 -11
  18. package/dist/chunk-YHZXQ7HP.js.map +1 -0
  19. package/dist/codemode-operations.d.ts +117 -0
  20. package/dist/codex-token-resolver.d.ts +2 -2
  21. package/dist/company-profile-schema.d.ts +798 -0
  22. package/dist/company-profile.d.ts +268 -0
  23. package/dist/computer-sessions.d.ts +109 -0
  24. package/dist/connection-token-resolver.d.ts +9 -4
  25. package/dist/database.d.ts +52 -3
  26. package/dist/editable-artifact-durable-export.d.ts +153 -0
  27. package/dist/editable-artifact-durable-export.js +788 -0
  28. package/dist/editable-artifact-durable-export.js.map +1 -0
  29. package/dist/editable-artifact-materialization.d.ts +123 -0
  30. package/dist/editable-artifacts-schema.d.ts +4152 -0
  31. package/dist/editable-artifacts.d.ts +610 -0
  32. package/dist/editable-artifacts.js +26 -0
  33. package/dist/editable-artifacts.js.map +1 -0
  34. package/dist/environment-crypto.d.ts +8 -4
  35. package/dist/generated-images.d.ts +139 -0
  36. package/dist/index.d.ts +958 -126
  37. package/dist/index.js +31204 -12172
  38. package/dist/index.js.map +1 -1
  39. package/dist/insights.d.ts +51 -4
  40. package/dist/integration-bindings.d.ts +70 -0
  41. package/dist/integration-features.d.ts +99 -0
  42. package/dist/interaction-revisions.d.ts +3 -0
  43. package/dist/interaction-schema.d.ts +5372 -0
  44. package/dist/knowledge-source-sync-schema.d.ts +1679 -0
  45. package/dist/knowledge-source-sync.d.ts +274 -0
  46. package/dist/lossless-columns.d.ts +58 -0
  47. package/dist/lossless-json.d.ts +39 -0
  48. package/dist/memory-domain.d.ts +3 -6
  49. package/dist/memory-governance.d.ts +2 -2
  50. package/dist/memory-slack-delivery.d.ts +101 -0
  51. package/dist/new-session-drafts.d.ts +2 -2
  52. package/dist/pack-components.d.ts +76 -0
  53. package/dist/pack-installations.d.ts +86 -0
  54. package/dist/persistence-errors.d.ts +7 -14
  55. package/dist/plugin-packages.d.ts +126 -0
  56. package/dist/preference-registry.d.ts +1 -1
  57. package/dist/provision-roles.d.ts +9 -1
  58. package/dist/provision-roles.js +1 -1
  59. package/dist/runtime-posture.d.ts +18 -6
  60. package/dist/schema.d.ts +19122 -8731
  61. package/dist/schema.js +173 -1
  62. package/dist/scoped-knowledge.d.ts +97 -1
  63. package/dist/session-control.d.ts +4 -4
  64. package/dist/session-queue-commands.d.ts +19 -11
  65. package/dist/session-realtime-context.d.ts +2 -2
  66. package/dist/session-realtime-ledger.d.ts +4 -4
  67. package/dist/session-realtime-mirror.d.ts +2 -2
  68. package/dist/session-realtime-state.d.ts +1 -1
  69. package/dist/session-realtime-terminal.d.ts +8 -1
  70. package/dist/session-realtime.d.ts +7 -7
  71. package/dist/session-tool-call-settlement.d.ts +1 -1
  72. package/dist/slack-user-link-access.d.ts +54 -0
  73. package/dist/transcription-recordings-schema.d.ts +44 -6
  74. package/dist/transcription-recordings.d.ts +3 -3
  75. package/dist/turn-initiator.d.ts +2 -2
  76. package/dist/video-generation.d.ts +216 -0
  77. package/dist/video-generation.js +66 -0
  78. package/dist/video-generation.js.map +1 -0
  79. package/dist/workspace-artifacts.d.ts +1 -1
  80. package/dist/workspace-instruction-policies.d.ts +1 -1
  81. package/dist/workspace-learning-policy-schema.d.ts +932 -0
  82. package/dist/workspace-learning-policy.d.ts +161 -0
  83. package/drizzle/0065_enrollment_credential_generation.sql +10 -0
  84. package/drizzle/0140_retained_screenshot_artifacts.sql +192 -0
  85. package/drizzle/0176_lossless_canonical_json.sql +975 -0
  86. package/drizzle/0177_session_events_workspace_turn_type_index.sql +5 -0
  87. package/drizzle/0178_permissioned_secret_reads.sql +14 -0
  88. package/drizzle/0179_slack_private_shortcut_delivery_gate.sql +85 -0
  89. package/drizzle/0180_retained_screenshot_lifecycle_fences.sql +273 -0
  90. package/drizzle/0181_connected_machine_removal.sql +52 -0
  91. package/drizzle/0182_connected_machine_remove_session_default.sql +77 -0
  92. package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
  93. package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
  94. package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
  95. package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
  96. package/drizzle/0187_generated_image_artifacts.sql +173 -0
  97. package/drizzle/0188_image_generation_retention_failure.sql +25 -0
  98. package/drizzle/0189_retained_session_image_formats.sql +13 -0
  99. package/drizzle/0190_timeline_annotations.sql +31 -0
  100. package/drizzle/0191_editable_artifact_engine.sql +3853 -0
  101. package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
  102. package/drizzle/0193_editable_artifact_authorization.sql +190 -0
  103. package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
  104. package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
  105. package/drizzle/0196_rig_provider_images.sql +152 -0
  106. package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
  107. package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
  108. package/drizzle/0199_workspace_learning_policy.sql +980 -0
  109. package/drizzle/0201_company_profile_authority.sql +849 -0
  110. package/drizzle/0202_document_index_checkpoints.sql +85 -0
  111. package/drizzle/0203_durable_video_generation.sql +353 -0
  112. package/drizzle/0204_video_generation_funding.sql +44 -0
  113. package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
  114. package/drizzle/0206_browser_sessions.sql +646 -0
  115. package/drizzle/0207_browser_identities.sql +379 -0
  116. package/drizzle/0208_attached_browser_devices.sql +165 -0
  117. package/drizzle/0209_computer_sessions.sql +575 -0
  118. package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
  119. package/drizzle/0211_editable_artifact_session_links.sql +58 -0
  120. package/drizzle/0212_slack_installation_bindings.sql +348 -0
  121. package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
  122. package/drizzle/0214_session_activity_commit_gate.sql +262 -0
  123. package/drizzle/0215_capabilities_platform.sql +980 -0
  124. package/drizzle/0216_pack_component_ownership.sql +235 -0
  125. package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
  126. package/package.json +18 -5
  127. package/src/attached-browser-devices.ts +371 -0
  128. package/src/attempt-tool-catalogs.ts +131 -0
  129. package/src/browser-auth.ts +1896 -0
  130. package/src/browser-identities.ts +1168 -0
  131. package/src/browser-sessions.ts +2005 -0
  132. package/src/browser-state-artifacts.ts +59 -0
  133. package/src/capability-components.ts +166 -0
  134. package/src/capability-integrations.ts +1663 -0
  135. package/src/codemode-operations.ts +674 -0
  136. package/src/company-profile-schema.ts +143 -0
  137. package/src/company-profile.ts +968 -0
  138. package/src/computer-sessions.ts +1130 -0
  139. package/src/connection-token-resolver.ts +270 -25
  140. package/src/database.ts +434 -24
  141. package/src/editable-artifact-durable-export.ts +1201 -0
  142. package/src/editable-artifact-materialization.ts +893 -0
  143. package/src/editable-artifacts-schema.ts +879 -0
  144. package/src/editable-artifacts.ts +5404 -0
  145. package/src/environment-crypto.ts +22 -9
  146. package/src/generated-images.ts +740 -0
  147. package/src/index.ts +10117 -2730
  148. package/src/insights.ts +160 -13
  149. package/src/integration-bindings.ts +455 -0
  150. package/src/integration-features.ts +859 -0
  151. package/src/interaction-revisions.ts +40 -0
  152. package/src/interaction-schema.ts +1303 -0
  153. package/src/knowledge-source-sync-schema.ts +192 -0
  154. package/src/knowledge-source-sync.ts +1922 -0
  155. package/src/lossless-columns.ts +35 -0
  156. package/src/lossless-json.ts +322 -0
  157. package/src/memory-domain.ts +5 -44
  158. package/src/memory-slack-delivery.ts +801 -0
  159. package/src/pack-components.ts +954 -0
  160. package/src/pack-installations.ts +929 -0
  161. package/src/persistence-errors.ts +26 -34
  162. package/src/plugin-packages.ts +1186 -0
  163. package/src/provision-roles.ts +302 -1
  164. package/src/runtime-posture-cli.ts +9 -0
  165. package/src/runtime-posture.ts +514 -5
  166. package/src/schema.ts +5177 -2957
  167. package/src/scoped-knowledge-schema.ts +1 -1
  168. package/src/scoped-knowledge.ts +655 -3
  169. package/src/session-control.ts +133 -81
  170. package/src/session-queue-commands.ts +511 -247
  171. package/src/session-realtime-context.ts +20 -7
  172. package/src/session-realtime-ledger.ts +32 -10
  173. package/src/session-realtime-mirror.ts +4 -2
  174. package/src/session-realtime-terminal.ts +89 -21
  175. package/src/session-realtime.ts +10 -9
  176. package/src/session-tool-call-settlement.ts +38 -15
  177. package/src/slack-user-link-access.ts +702 -0
  178. package/src/transcription-recordings-schema.ts +5 -2
  179. package/src/transcription-recordings.ts +70 -40
  180. package/src/video-generation.ts +1452 -0
  181. package/src/workspace-learning-policy-schema.ts +139 -0
  182. package/src/workspace-learning-policy.ts +595 -0
  183. package/dist/chunk-DBS5HPEW.js.map +0 -1
  184. package/dist/chunk-HT5T62CC.js +0 -6803
  185. package/dist/chunk-HT5T62CC.js.map +0 -1
  186. package/dist/event-payload-sanitizer.d.ts +0 -32
  187. package/src/event-payload-sanitizer.ts +0 -377
@@ -1,19 +1,36 @@
1
1
  import {
2
+ DraftTimelineAnnotations,
2
3
  McpPersonalConnectionDelegations,
4
+ TimelineAnnotations,
3
5
  metadataWithTurnExecutionPolicyV1,
4
6
  mergeResourceRefs,
7
+ renderTimelineAnnotationsForModel,
5
8
  ResourceRef,
6
9
  resourceMountPath,
7
10
  stableJson,
8
11
  turnExecutionPolicyAuditMetadata,
9
12
  type McpPersonalConnectionDelegation,
13
+ type DraftTimelineAnnotation,
10
14
  type LatencyMode,
11
15
  type ReasoningEffort,
16
+ type SandboxBackend,
17
+ type SandboxOs,
18
+ type SessionEvent,
19
+ type SessionEventType,
20
+ type SessionTurn,
21
+ type SessionTurnSource,
22
+ type SessionTurnStatus,
23
+ type ToolRef,
12
24
  type TurnExecutionPolicyV1,
25
+ type TimelineAnnotation,
13
26
  } from "@opengeni/contracts";
14
27
  import { and, asc, eq, inArray, sql } from "drizzle-orm";
15
- import type { Database } from "./database";
16
- import { sanitizeEventPayload } from "./event-payload-sanitizer";
28
+ import type { Database, SessionActivityDatabase } from "./database";
29
+ import {
30
+ fromPostgresLosslessJson,
31
+ fromPostgresLosslessText,
32
+ withLosslessContentWriteVersion,
33
+ } from "./lossless-json";
17
34
  import { closePendingSessionToolCallsInTransaction } from "./session-tool-call-settlement";
18
35
  import {
19
36
  assertAgentCommandAuthorityInTransaction,
@@ -45,6 +62,10 @@ import {
45
62
  type FrozenTurnInitiator,
46
63
  } from "./turn-initiator";
47
64
 
65
+ type SessionEventInsertWithPayload = typeof schema.sessionEvents.$inferInsert & {
66
+ payload: unknown;
67
+ };
68
+
48
69
  export type QueueCommandConflictCode =
49
70
  | "QUEUE_VERSION_CHANGED"
50
71
  | "QUEUE_PROMPT_STARTED"
@@ -93,6 +114,10 @@ export type SteerQueueCommandResult = QueueCommandResult & {
93
114
  export type SubmitHumanPromptResult = {
94
115
  receipt: SessionCommandReceiptRow;
95
116
  queueVersion: number;
117
+ accepted: SessionEvent;
118
+ events: SessionEvent[];
119
+ turn: SessionTurn;
120
+ /** Backward-compatible row identities for lower-level callers. */
96
121
  acceptedEventId: string;
97
122
  eventIds: string[];
98
123
  turnId: string;
@@ -102,6 +127,69 @@ export type SubmitHumanPromptResult = {
102
127
  replay: boolean;
103
128
  };
104
129
 
130
+ function mapSubmittedPromptEvent(row: typeof schema.sessionEvents.$inferSelect): SessionEvent {
131
+ return {
132
+ id: row.id,
133
+ workspaceId: row.workspaceId,
134
+ sessionId: row.sessionId,
135
+ sequence: row.sequence,
136
+ type: row.type as SessionEventType,
137
+ payload: fromPostgresLosslessJson(row.payload, row.payloadCodecVersion),
138
+ occurredAt: row.occurredAt.toISOString(),
139
+ clientEventId: row.clientEventId,
140
+ turnId: row.turnId,
141
+ turnGeneration: row.turnGeneration,
142
+ turnAttemptId: row.turnAttemptId,
143
+ turnAssociation: row.turnAssociation as SessionEvent["turnAssociation"],
144
+ duplicateOfEventId: row.duplicateOfEventId,
145
+ duplicateReason: row.duplicateReason,
146
+ };
147
+ }
148
+
149
+ function mapSubmittedPromptTurn(row: typeof schema.sessionTurns.$inferSelect): SessionTurn {
150
+ const personalConnections = McpPersonalConnectionDelegations.parse(
151
+ row.personalConnectionDelegations,
152
+ ).map(({ serverId, providerDomain }) => ({ serverId, providerDomain }));
153
+ return {
154
+ id: row.id,
155
+ workspaceId: row.workspaceId,
156
+ sessionId: row.sessionId,
157
+ triggerEventId: row.triggerEventId,
158
+ temporalWorkflowId: row.temporalWorkflowId,
159
+ status: row.status as SessionTurnStatus,
160
+ source: row.source as SessionTurnSource,
161
+ position: row.position,
162
+ prompt: fromPostgresLosslessText(row.prompt, row.promptCodecVersion),
163
+ annotations: TimelineAnnotations.parse(row.annotations),
164
+ resources: row.resources as ResourceRef[],
165
+ tools: row.tools as ToolRef[],
166
+ toolsProvided: row.toolsProvided,
167
+ model: row.model,
168
+ reasoningEffort: row.reasoningEffort as ReasoningEffort,
169
+ latencyMode: (row.latencyMode as LatencyMode | null | undefined) ?? "standard",
170
+ sandboxBackend: row.sandboxBackend as SandboxBackend,
171
+ sandboxOs: (row.sandboxOs as SandboxOs | null) ?? null,
172
+ metadata: row.metadata,
173
+ version: row.version,
174
+ executionGeneration: row.executionGeneration,
175
+ activeAttemptId: row.activeAttemptId,
176
+ lineage: row.lineage,
177
+ initiator: initiatorFromStorage(
178
+ row.initiatorKind,
179
+ row.initiatorSubjectId,
180
+ row.initiatorContext ?? {},
181
+ ),
182
+ initiatorContext: row.initiatorContext ?? {},
183
+ personalConnections,
184
+ cancelledBy: row.cancelledBy,
185
+ cancelReason: row.cancelReason,
186
+ startedAt: row.startedAt ? row.startedAt.toISOString() : null,
187
+ finishedAt: row.finishedAt ? row.finishedAt.toISOString() : null,
188
+ createdAt: row.createdAt.toISOString(),
189
+ updatedAt: row.updatedAt.toISOString(),
190
+ };
191
+ }
192
+
105
193
  export type AgentInternalUpdateCommandResult = {
106
194
  receipt: SessionCommandReceiptRow;
107
195
  updateId: string;
@@ -294,18 +382,22 @@ export async function supersedeSessionCurrentDirectionInTransaction(
294
382
  const cancelledHumanInputEvents = await db
295
383
  .insert(schema.sessionEvents)
296
384
  .values(
297
- cancelledHumanInputs.map((request) => ({
298
- accountId: input.accountId,
299
- workspaceId: input.workspaceId,
300
- sessionId: input.sessionId,
301
- sequence: ++lastSequence,
302
- type: "user.humanInputResponse",
303
- turnId: current.id,
304
- turnGeneration: current.executionGeneration,
305
- turnAssociation: "current",
306
- payload: { requestId: request.id, response: { outcome: "cancelled" } },
307
- occurredAt: now,
308
- })),
385
+ withLosslessContentWriteVersion(
386
+ cancelledHumanInputs.map((request) => ({
387
+ accountId: input.accountId,
388
+ workspaceId: input.workspaceId,
389
+ sessionId: input.sessionId,
390
+ sequence: ++lastSequence,
391
+ type: "user.humanInputResponse",
392
+ turnId: current.id,
393
+ turnGeneration: current.executionGeneration,
394
+ turnAssociation: "current",
395
+ payload: { requestId: request.id, response: { outcome: "cancelled" } },
396
+ occurredAt: now,
397
+ })),
398
+ "payload",
399
+ "payloadCodecVersion",
400
+ ),
309
401
  )
310
402
  .returning();
311
403
  const requestsById = new Map(cancelledHumanInputs.map((request) => [request.id, request]));
@@ -406,7 +498,7 @@ async function loadQueuedTurns(
406
498
  }
407
499
 
408
500
  async function normalizeQueuePositions(
409
- db: Database,
501
+ db: SessionActivityDatabase,
410
502
  workspaceId: string,
411
503
  sessionId: string,
412
504
  orderedIds: string[],
@@ -438,7 +530,19 @@ async function normalizeQueuePositions(
438
530
  }
439
531
 
440
532
  function draftIsNonEmpty(draft: ComposerDraftRow): boolean {
441
- return draft.text.length > 0 || draft.resources.length > 0 || draft.sourceTurnId !== null;
533
+ return (
534
+ draft.text.length > 0 ||
535
+ DraftTimelineAnnotations.parse(draft.annotations).length > 0 ||
536
+ draft.resources.length > 0 ||
537
+ draft.sourceTurnId !== null
538
+ );
539
+ }
540
+
541
+ function draftAnnotationsFromTurn(value: unknown): DraftTimelineAnnotation[] {
542
+ return TimelineAnnotations.parse(value).map(({ ordinal: _ordinal, ...annotation }) => ({
543
+ ...annotation,
544
+ source: { ...annotation.source },
545
+ }));
442
546
  }
443
547
 
444
548
  function withCanonicalResourceMountPaths(resources: readonly unknown[]): unknown[] {
@@ -495,6 +599,7 @@ export async function saveComposerDraftInTransaction(
495
599
  subjectId: string;
496
600
  expectedRevision: number;
497
601
  text: string;
602
+ annotations?: DraftTimelineAnnotation[];
498
603
  resources: ResourceRef[];
499
604
  model: string;
500
605
  reasoningEffort: ReasoningEffort;
@@ -526,6 +631,7 @@ export async function saveComposerDraftInTransaction(
526
631
  subjectId: input.subjectId,
527
632
  revision,
528
633
  text: input.text,
634
+ annotations: DraftTimelineAnnotations.parse(input.annotations ?? []),
529
635
  resources: withCanonicalResourceMountPaths(input.resources),
530
636
  tools: [],
531
637
  toolsProvided: false,
@@ -551,7 +657,7 @@ export async function saveComposerDraftInTransaction(
551
657
  }
552
658
 
553
659
  export async function moveQueuedTurnInTransaction(
554
- db: Database,
660
+ db: SessionActivityDatabase,
555
661
  input: {
556
662
  accountId: string;
557
663
  workspaceId: string;
@@ -639,21 +745,27 @@ export async function moveQueuedTurnInTransaction(
639
745
  );
640
746
  const [event] = await db
641
747
  .insert(schema.sessionEvents)
642
- .values({
643
- accountId: input.accountId,
644
- workspaceId: input.workspaceId,
645
- sessionId: input.sessionId,
646
- sequence: session.lastSequence + 1,
647
- type: "session.queue.changed",
648
- turnId: target.id,
649
- payload: {
650
- operation: "move",
651
- queueVersion,
652
- turnId: target.id,
653
- beforeTurnId: input.beforeTurnId,
654
- },
655
- occurredAt: new Date(),
656
- })
748
+ .values(
749
+ withLosslessContentWriteVersion(
750
+ {
751
+ accountId: input.accountId,
752
+ workspaceId: input.workspaceId,
753
+ sessionId: input.sessionId,
754
+ sequence: session.lastSequence + 1,
755
+ type: "session.queue.changed",
756
+ turnId: target.id,
757
+ payload: {
758
+ operation: "move",
759
+ queueVersion,
760
+ turnId: target.id,
761
+ beforeTurnId: input.beforeTurnId,
762
+ },
763
+ occurredAt: new Date(),
764
+ },
765
+ "payload",
766
+ "payloadCodecVersion",
767
+ ),
768
+ )
657
769
  .returning({ id: schema.sessionEvents.id });
658
770
  if (!event) throw new Error("Queue move event was not inserted");
659
771
  eventIds.push(event.id);
@@ -681,7 +793,7 @@ export async function moveQueuedTurnInTransaction(
681
793
  }
682
794
 
683
795
  export async function deleteSessionQueueItemInTransaction(
684
- db: Database,
796
+ db: SessionActivityDatabase,
685
797
  input: {
686
798
  accountId: string;
687
799
  workspaceId: string;
@@ -767,20 +879,26 @@ export async function deleteSessionQueueItemInTransaction(
767
879
  );
768
880
  const [event] = await db
769
881
  .insert(schema.sessionEvents)
770
- .values({
771
- accountId: input.accountId,
772
- workspaceId: input.workspaceId,
773
- sessionId: input.sessionId,
774
- sequence: session.lastSequence + 1,
775
- type: "session.queue.changed",
776
- turnId: turn.id,
777
- payload: {
778
- operation: "delete",
779
- queueVersion,
780
- turnId: turn.id,
781
- },
782
- occurredAt: now,
783
- })
882
+ .values(
883
+ withLosslessContentWriteVersion(
884
+ {
885
+ accountId: input.accountId,
886
+ workspaceId: input.workspaceId,
887
+ sessionId: input.sessionId,
888
+ sequence: session.lastSequence + 1,
889
+ type: "session.queue.changed",
890
+ turnId: turn.id,
891
+ payload: {
892
+ operation: "delete",
893
+ queueVersion,
894
+ turnId: turn.id,
895
+ },
896
+ occurredAt: now,
897
+ },
898
+ "payload",
899
+ "payloadCodecVersion",
900
+ ),
901
+ )
784
902
  .returning({ id: schema.sessionEvents.id });
785
903
  if (!event) throw new Error("Queue delete event was not inserted");
786
904
  await db
@@ -806,7 +924,7 @@ export async function deleteSessionQueueItemInTransaction(
806
924
  }
807
925
 
808
926
  export async function editQueuedTurnInTransaction(
809
- db: Database,
927
+ db: SessionActivityDatabase,
810
928
  input: {
811
929
  accountId: string;
812
930
  workspaceId: string;
@@ -899,6 +1017,7 @@ export async function editQueuedTurnInTransaction(
899
1017
  subjectId: input.subjectId,
900
1018
  revision: nextDraftRevision,
901
1019
  text: turn.prompt,
1020
+ annotations: draftAnnotationsFromTurn(turn.annotations),
902
1021
  resources: withCanonicalResourceMountPaths(turn.resources),
903
1022
  tools: [],
904
1023
  toolsProvided: false,
@@ -939,21 +1058,27 @@ export async function editQueuedTurnInTransaction(
939
1058
  );
940
1059
  const [event] = await db
941
1060
  .insert(schema.sessionEvents)
942
- .values({
943
- accountId: input.accountId,
944
- workspaceId: input.workspaceId,
945
- sessionId: input.sessionId,
946
- sequence: session.lastSequence + 1,
947
- type: "session.queue.changed",
948
- turnId: turn.id,
949
- payload: {
950
- operation: "edit",
951
- queueVersion,
952
- turnId: turn.id,
953
- draftRevision: nextDraftRevision,
954
- },
955
- occurredAt: now,
956
- })
1061
+ .values(
1062
+ withLosslessContentWriteVersion(
1063
+ {
1064
+ accountId: input.accountId,
1065
+ workspaceId: input.workspaceId,
1066
+ sessionId: input.sessionId,
1067
+ sequence: session.lastSequence + 1,
1068
+ type: "session.queue.changed",
1069
+ turnId: turn.id,
1070
+ payload: {
1071
+ operation: "edit",
1072
+ queueVersion,
1073
+ turnId: turn.id,
1074
+ draftRevision: nextDraftRevision,
1075
+ },
1076
+ occurredAt: now,
1077
+ },
1078
+ "payload",
1079
+ "payloadCodecVersion",
1080
+ ),
1081
+ )
957
1082
  .returning({ id: schema.sessionEvents.id });
958
1083
  if (!event) throw new Error("Queue edit event was not inserted");
959
1084
  await db
@@ -981,7 +1106,7 @@ export async function editQueuedTurnInTransaction(
981
1106
  }
982
1107
 
983
1108
  export async function steerQueuedTurnInTransaction(
984
- db: Database,
1109
+ db: SessionActivityDatabase,
985
1110
  input: {
986
1111
  accountId: string;
987
1112
  workspaceId: string;
@@ -1118,7 +1243,7 @@ export async function steerQueuedTurnInTransaction(
1118
1243
  input.actor.type === "agent_attempt"
1119
1244
  ? `attempt:${input.actor.attemptId}`
1120
1245
  : input.actor.subjectId;
1121
- const eventValues: Array<typeof schema.sessionEvents.$inferInsert> = [];
1246
+ const eventValues: SessionEventInsertWithPayload[] = [];
1122
1247
  if (supersededTurnId && !liveCurrentTurnId) {
1123
1248
  eventValues.push({
1124
1249
  accountId: input.accountId,
@@ -1146,9 +1271,12 @@ export async function steerQueuedTurnInTransaction(
1146
1271
  },
1147
1272
  occurredAt: now,
1148
1273
  });
1149
- const eventRows = await db.insert(schema.sessionEvents).values(eventValues).returning({
1150
- id: schema.sessionEvents.id,
1151
- });
1274
+ const eventRows = await db
1275
+ .insert(schema.sessionEvents)
1276
+ .values(withLosslessContentWriteVersion(eventValues, "payload", "payloadCodecVersion"))
1277
+ .returning({
1278
+ id: schema.sessionEvents.id,
1279
+ });
1152
1280
  await db
1153
1281
  .update(schema.sessions)
1154
1282
  .set({
@@ -1161,19 +1289,25 @@ export async function steerQueuedTurnInTransaction(
1161
1289
  updatedAt: now,
1162
1290
  })
1163
1291
  .where(eq(schema.sessions.id, input.sessionId));
1164
- await db.insert(schema.auditEvents).values({
1165
- accountId: input.accountId,
1166
- workspaceId: input.workspaceId,
1167
- subjectId: actor,
1168
- action: "session.queue.steer",
1169
- targetType: "session_turn",
1170
- targetId: target.id,
1171
- metadata: {
1172
- operationId: reserved.receipt.id,
1173
- replacedTurnId: supersededTurnId,
1174
- interruptionCount,
1175
- },
1176
- });
1292
+ await db.insert(schema.auditEvents).values(
1293
+ withLosslessContentWriteVersion(
1294
+ {
1295
+ accountId: input.accountId,
1296
+ workspaceId: input.workspaceId,
1297
+ subjectId: actor,
1298
+ action: "session.queue.steer",
1299
+ targetType: "session_turn",
1300
+ targetId: target.id,
1301
+ metadata: {
1302
+ operationId: reserved.receipt.id,
1303
+ replacedTurnId: supersededTurnId,
1304
+ interruptionCount,
1305
+ },
1306
+ },
1307
+ "metadata",
1308
+ "metadataCodecVersion",
1309
+ ),
1310
+ );
1177
1311
  const wakeRevision = await registerSessionWorkflowWakeInTransaction(db, {
1178
1312
  accountId: input.accountId,
1179
1313
  workspaceId: input.workspaceId,
@@ -1205,7 +1339,7 @@ export async function steerQueuedTurnInTransaction(
1205
1339
  }
1206
1340
 
1207
1341
  export async function submitHumanPromptInTransaction(
1208
- db: Database,
1342
+ db: SessionActivityDatabase,
1209
1343
  input: {
1210
1344
  accountId: string;
1211
1345
  workspaceId: string;
@@ -1218,6 +1352,7 @@ export async function submitHumanPromptInTransaction(
1218
1352
  controlEtag?: string | null;
1219
1353
  expectedDraftRevision?: number | null;
1220
1354
  text: string;
1355
+ annotations?: TimelineAnnotation[];
1221
1356
  turnInstructions?: string | null;
1222
1357
  resources: ResourceRef[];
1223
1358
  model?: string | null;
@@ -1235,6 +1370,8 @@ export async function submitHumanPromptInTransaction(
1235
1370
  context: string;
1236
1371
  };
1237
1372
  source: "user" | "api";
1373
+ /** Record the admitted run's durable usage fact in this transaction. */
1374
+ recordAgentRunUsage?: boolean;
1238
1375
  personalConnectionDelegations?: McpPersonalConnectionDelegation[];
1239
1376
  mcpCredentialUpdates?: Array<{
1240
1377
  id: string;
@@ -1242,6 +1379,7 @@ export async function submitHumanPromptInTransaction(
1242
1379
  }>;
1243
1380
  },
1244
1381
  ): Promise<SubmitHumanPromptResult> {
1382
+ const annotations = TimelineAnnotations.parse(input.annotations ?? []);
1245
1383
  const workspaceControl = await lockWorkspaceInferenceControl(db, input.workspaceId, "update");
1246
1384
  await lockSessionEventWriteRows(db, {
1247
1385
  workspaceId: input.workspaceId,
@@ -1258,6 +1396,7 @@ export async function submitHumanPromptInTransaction(
1258
1396
  controlEtag: input.controlEtag ?? null,
1259
1397
  expectedDraftRevision: input.expectedDraftRevision ?? null,
1260
1398
  text: input.text,
1399
+ annotations,
1261
1400
  turnInstructions: input.turnInstructions ?? null,
1262
1401
  resources: withCanonicalResourceMountPaths(input.resources),
1263
1402
  model: input.model ?? null,
@@ -1297,9 +1436,39 @@ export async function submitHumanPromptInTransaction(
1297
1436
  if (!turnId || !acceptedEventId || wakeRevision < 1) {
1298
1437
  throw new SessionControlInvariantError("Replayed prompt receipt is incomplete");
1299
1438
  }
1439
+ const replayEvents = await db
1440
+ .select()
1441
+ .from(schema.sessionEvents)
1442
+ .where(
1443
+ and(
1444
+ eq(schema.sessionEvents.workspaceId, input.workspaceId),
1445
+ eq(schema.sessionEvents.sessionId, input.sessionId),
1446
+ inArray(schema.sessionEvents.id, eventIds),
1447
+ ),
1448
+ )
1449
+ .orderBy(asc(schema.sessionEvents.sequence));
1450
+ const [replayTurn] = await db
1451
+ .select()
1452
+ .from(schema.sessionTurns)
1453
+ .where(
1454
+ and(
1455
+ eq(schema.sessionTurns.workspaceId, input.workspaceId),
1456
+ eq(schema.sessionTurns.sessionId, input.sessionId),
1457
+ eq(schema.sessionTurns.id, turnId),
1458
+ ),
1459
+ )
1460
+ .limit(1);
1461
+ const events = replayEvents.map(mapSubmittedPromptEvent);
1462
+ const accepted = events.find((event) => event.id === acceptedEventId);
1463
+ if (!accepted || !replayTurn || events.length !== eventIds.length) {
1464
+ throw new SessionControlInvariantError("Replayed prompt rows are incomplete");
1465
+ }
1300
1466
  return {
1301
1467
  receipt: reserved.receipt,
1302
1468
  queueVersion: Number(reserved.receipt.appliedQueueVersion),
1469
+ accepted,
1470
+ events,
1471
+ turn: mapSubmittedPromptTurn(replayTurn),
1303
1472
  acceptedEventId,
1304
1473
  eventIds,
1305
1474
  turnId,
@@ -1369,6 +1538,7 @@ export async function submitHumanPromptInTransaction(
1369
1538
  draft &&
1370
1539
  canonicalSessionCommandHash({
1371
1540
  text: draft.text,
1541
+ annotations: DraftTimelineAnnotations.parse(draft.annotations),
1372
1542
  resources: withCanonicalResourceMountPaths(draft.resources),
1373
1543
  model: draft.model,
1374
1544
  reasoningEffort: draft.reasoningEffort,
@@ -1376,6 +1546,7 @@ export async function submitHumanPromptInTransaction(
1376
1546
  }) !==
1377
1547
  canonicalSessionCommandHash({
1378
1548
  text: input.text,
1549
+ annotations: annotations.map(({ ordinal: _ordinal, ...annotation }) => annotation),
1379
1550
  resources: withCanonicalResourceMountPaths(input.resources),
1380
1551
  model: input.model ?? session.model,
1381
1552
  reasoningEffort: input.reasoningEffort ?? input.reasoningEffortFallback,
@@ -1485,7 +1656,7 @@ export async function submitHumanPromptInTransaction(
1485
1656
  const turnId = crypto.randomUUID();
1486
1657
  const workflowId = session.temporalWorkflowId ?? `session-${session.id}`;
1487
1658
  let sequence = session.lastSequence;
1488
- const eventValues: Array<typeof schema.sessionEvents.$inferInsert> = [
1659
+ const eventValues: SessionEventInsertWithPayload[] = [
1489
1660
  {
1490
1661
  id: acceptedEventId,
1491
1662
  accountId: input.accountId,
@@ -1494,8 +1665,9 @@ export async function submitHumanPromptInTransaction(
1494
1665
  sequence: ++sequence,
1495
1666
  type: "user.message",
1496
1667
  clientEventId: input.operationKey,
1497
- payload: sanitizeEventPayload({
1668
+ payload: {
1498
1669
  text: input.messagePresentation?.text ?? input.text,
1670
+ ...(annotations.length > 0 ? { annotations } : {}),
1499
1671
  ...(input.messagePresentation
1500
1672
  ? {
1501
1673
  presentation: {
@@ -1510,56 +1682,64 @@ export async function submitHumanPromptInTransaction(
1510
1682
  ...(input.latencyMode ? { latencyMode: input.latencyMode } : {}),
1511
1683
  delivery: input.delivery,
1512
1684
  initiator: frozenInitiator.initiator,
1513
- }),
1685
+ },
1514
1686
  occurredAt: now,
1515
1687
  },
1516
1688
  ];
1517
1689
  const existingQueued = await loadQueuedTurns(db, input.workspaceId, input.sessionId, true);
1518
1690
  const [turn] = await db
1519
1691
  .insert(schema.sessionTurns)
1520
- .values({
1521
- id: turnId,
1522
- accountId: input.accountId,
1523
- workspaceId: input.workspaceId,
1524
- sessionId: input.sessionId,
1525
- triggerEventId: acceptedEventId,
1526
- temporalWorkflowId: workflowId,
1527
- status: "queued",
1528
- source: input.source,
1529
- position: input.delivery === "steer" ? 0 : existingQueued.length + 1,
1530
- prompt: input.text,
1531
- turnInstructions:
1532
- editedSourceTurnInstructions !== undefined
1533
- ? editedSourceTurnInstructions
1534
- : (input.turnInstructions ?? null),
1535
- resources: input.resources,
1536
- tools: [],
1537
- toolsProvided: false,
1538
- model: input.model ?? session.model,
1539
- reasoningEffort: input.reasoningEffort ?? input.reasoningEffortFallback,
1540
- latencyMode: input.turnExecutionPolicy?.latencyMode ?? input.latencyMode ?? "standard",
1541
- sandboxBackend: session.sandboxBackend,
1542
- metadata: input.turnExecutionPolicy
1543
- ? metadataWithTurnExecutionPolicyV1(input.turnMetadata ?? {}, input.turnExecutionPolicy)
1544
- : (input.turnMetadata ?? {}),
1545
- lineage: { actor: input.actor.type },
1546
- ...initiatorColumns(frozenInitiator),
1547
- initiatingHumanSubjectId: editedSourceTurn
1548
- ? (editedSourceTurn.initiatingHumanSubjectId ??
1549
- (editedSourceTurn.initiatorKind === "subject"
1550
- ? editedSourceTurn.initiatorSubjectId
1551
- : null))
1552
- : frozenInitiator.initiator.kind === "subject"
1553
- ? frozenInitiator.initiator.subjectId
1554
- : null,
1555
- personalConnectionDelegations: editedSourceTurn
1556
- ? editedSourceTurn.personalConnectionDelegations
1557
- : (input.personalConnectionDelegations ?? []),
1558
- createdAt: now,
1559
- updatedAt: now,
1560
- })
1692
+ .values(
1693
+ withLosslessContentWriteVersion(
1694
+ {
1695
+ id: turnId,
1696
+ accountId: input.accountId,
1697
+ workspaceId: input.workspaceId,
1698
+ sessionId: input.sessionId,
1699
+ triggerEventId: acceptedEventId,
1700
+ temporalWorkflowId: workflowId,
1701
+ status: "queued",
1702
+ source: input.source,
1703
+ position: input.delivery === "steer" ? 0 : existingQueued.length + 1,
1704
+ prompt: input.text,
1705
+ annotations,
1706
+ turnInstructions:
1707
+ editedSourceTurnInstructions !== undefined
1708
+ ? editedSourceTurnInstructions
1709
+ : (input.turnInstructions ?? null),
1710
+ resources: input.resources,
1711
+ tools: [],
1712
+ toolsProvided: false,
1713
+ model: input.model ?? session.model,
1714
+ reasoningEffort: input.reasoningEffort ?? input.reasoningEffortFallback,
1715
+ latencyMode: input.turnExecutionPolicy?.latencyMode ?? input.latencyMode ?? "standard",
1716
+ sandboxBackend: session.sandboxBackend,
1717
+ metadata: input.turnExecutionPolicy
1718
+ ? metadataWithTurnExecutionPolicyV1(input.turnMetadata ?? {}, input.turnExecutionPolicy)
1719
+ : (input.turnMetadata ?? {}),
1720
+ lineage: { actor: input.actor.type },
1721
+ ...initiatorColumns(frozenInitiator),
1722
+ initiatingHumanSubjectId: editedSourceTurn
1723
+ ? (editedSourceTurn.initiatingHumanSubjectId ??
1724
+ (editedSourceTurn.initiatorKind === "subject"
1725
+ ? editedSourceTurn.initiatorSubjectId
1726
+ : null))
1727
+ : frozenInitiator.initiator.kind === "subject"
1728
+ ? frozenInitiator.initiator.subjectId
1729
+ : null,
1730
+ personalConnectionDelegations: editedSourceTurn
1731
+ ? editedSourceTurn.personalConnectionDelegations
1732
+ : (input.personalConnectionDelegations ?? []),
1733
+ createdAt: now,
1734
+ updatedAt: now,
1735
+ },
1736
+ "prompt",
1737
+ "promptCodecVersion",
1738
+ ),
1739
+ )
1561
1740
  .returning();
1562
1741
  if (!turn) throw new SessionControlInvariantError("Prompt turn was not inserted");
1742
+ let committedTurn = turn;
1563
1743
  eventValues.push({
1564
1744
  accountId: input.accountId,
1565
1745
  workspaceId: input.workspaceId,
@@ -1610,7 +1790,7 @@ export async function submitHumanPromptInTransaction(
1610
1790
  // settlement has already cleared sessions.active_turn_id. Do not infer this
1611
1791
  // state from a local request spinner or queue position.
1612
1792
  if (input.delivery === "steer") {
1613
- await db
1793
+ const [updatedTurn] = await db
1614
1794
  .update(schema.sessionTurns)
1615
1795
  .set({
1616
1796
  metadata: {
@@ -1628,7 +1808,12 @@ export async function submitHumanPromptInTransaction(
1628
1808
  eq(schema.sessionTurns.sessionId, input.sessionId),
1629
1809
  eq(schema.sessionTurns.id, turnId),
1630
1810
  ),
1631
- );
1811
+ )
1812
+ .returning();
1813
+ if (!updatedTurn) {
1814
+ throw new SessionControlInvariantError("Steer prompt turn metadata was not updated");
1815
+ }
1816
+ committedTurn = updatedTurn;
1632
1817
  }
1633
1818
  if (supersession.replacedTurn) {
1634
1819
  const current = supersession.replacedTurn;
@@ -1686,7 +1871,10 @@ export async function submitHumanPromptInTransaction(
1686
1871
  occurredAt: now,
1687
1872
  });
1688
1873
  }
1689
- const eventRows = await db.insert(schema.sessionEvents).values(eventValues).returning();
1874
+ const eventRows = await db
1875
+ .insert(schema.sessionEvents)
1876
+ .values(withLosslessContentWriteVersion(eventValues, "payload", "payloadCodecVersion"))
1877
+ .returning();
1690
1878
  if (input.actor.type === "human") {
1691
1879
  const realtimeRouting =
1692
1880
  input.delivery === "steer"
@@ -1705,7 +1893,7 @@ export async function submitHumanPromptInTransaction(
1705
1893
  text: renderRealtimeHumanInputContext({
1706
1894
  delivery: input.delivery,
1707
1895
  routing: realtimeRouting,
1708
- text: input.text,
1896
+ text: renderTimelineAnnotationsForModel(input.text, annotations),
1709
1897
  }),
1710
1898
  payload: {
1711
1899
  delivery: input.delivery,
@@ -1742,25 +1930,52 @@ export async function submitHumanPromptInTransaction(
1742
1930
  reason: input.delivery === "steer" ? "prompt_steer" : "prompt_send",
1743
1931
  controlRequested: input.delivery === "steer",
1744
1932
  });
1745
- await db.insert(schema.auditEvents).values({
1746
- accountId: input.accountId,
1747
- workspaceId: input.workspaceId,
1748
- subjectId:
1749
- input.actor.type === "agent_attempt"
1750
- ? `attempt:${input.actor.attemptId}`
1751
- : input.actor.subjectId,
1752
- action: input.delivery === "steer" ? "session.prompt.steer" : "session.prompt.send",
1753
- targetType: "session_turn",
1754
- targetId: turnId,
1755
- metadata: {
1756
- operationId: reserved.receipt.id,
1757
- replacedTurnId,
1758
- interruptionCount,
1759
- ...(input.turnExecutionPolicy
1760
- ? turnExecutionPolicyAuditMetadata(input.turnExecutionPolicy, turnId)
1761
- : {}),
1762
- },
1763
- });
1933
+ await db.insert(schema.auditEvents).values(
1934
+ withLosslessContentWriteVersion(
1935
+ {
1936
+ accountId: input.accountId,
1937
+ workspaceId: input.workspaceId,
1938
+ subjectId:
1939
+ input.actor.type === "agent_attempt"
1940
+ ? `attempt:${input.actor.attemptId}`
1941
+ : input.actor.subjectId,
1942
+ action: input.delivery === "steer" ? "session.prompt.steer" : "session.prompt.send",
1943
+ targetType: "session_turn",
1944
+ targetId: turnId,
1945
+ metadata: {
1946
+ operationId: reserved.receipt.id,
1947
+ replacedTurnId,
1948
+ interruptionCount,
1949
+ ...(input.turnExecutionPolicy
1950
+ ? turnExecutionPolicyAuditMetadata(input.turnExecutionPolicy, turnId)
1951
+ : {}),
1952
+ },
1953
+ },
1954
+ "metadata",
1955
+ "metadataCodecVersion",
1956
+ ),
1957
+ );
1958
+ if (input.recordAgentRunUsage) {
1959
+ await db
1960
+ .insert(schema.usageEvents)
1961
+ .values({
1962
+ accountId: input.accountId,
1963
+ workspaceId: input.workspaceId,
1964
+ subjectId: input.subjectId,
1965
+ eventType: "agent_run.created",
1966
+ quantity: 1,
1967
+ unit: "run",
1968
+ sourceResourceType: "session_turn",
1969
+ sourceResourceId: turnId,
1970
+ sessionId: input.sessionId,
1971
+ turnId,
1972
+ ...initiatorColumns(frozenInitiator),
1973
+ origin: input.source,
1974
+ idempotencyKey: `agent_run.created:${input.workspaceId}:${turnId}`,
1975
+ occurredAt: now,
1976
+ })
1977
+ .onConflictDoNothing({ target: schema.usageEvents.idempotencyKey });
1978
+ }
1764
1979
  const eventIds = eventRows.map((event) => event.id);
1765
1980
  const receipt = await updateSessionCommandReceiptResult(db, reserved.receipt.id, {
1766
1981
  controlRevision: resumed.revision,
@@ -1782,9 +1997,17 @@ export async function submitHumanPromptInTransaction(
1782
1997
  : {}),
1783
1998
  },
1784
1999
  });
2000
+ const events = eventRows.map(mapSubmittedPromptEvent);
2001
+ const accepted = events.find((event) => event.id === acceptedEventId);
2002
+ if (!accepted) {
2003
+ throw new SessionControlInvariantError("Inserted user.message event is missing");
2004
+ }
1785
2005
  return {
1786
2006
  receipt,
1787
2007
  queueVersion,
2008
+ accepted,
2009
+ events,
2010
+ turn: mapSubmittedPromptTurn(committedTurn),
1788
2011
  acceptedEventId,
1789
2012
  eventIds,
1790
2013
  turnId,
@@ -1796,7 +2019,7 @@ export async function submitHumanPromptInTransaction(
1796
2019
  }
1797
2020
 
1798
2021
  export async function sendAgentMessageInTransaction(
1799
- db: Database,
2022
+ db: SessionActivityDatabase,
1800
2023
  input: {
1801
2024
  accountId: string;
1802
2025
  workspaceId: string;
@@ -1879,46 +2102,62 @@ export async function sendAgentMessageInTransaction(
1879
2102
  const now = new Date();
1880
2103
  const [update] = await db
1881
2104
  .insert(schema.sessionSystemUpdates)
1882
- .values({
1883
- accountId: input.accountId,
1884
- workspaceId: input.workspaceId,
1885
- sessionId: input.targetSessionId,
1886
- kind: "agent_message",
1887
- classification: "info",
1888
- sourceId: input.actor.sessionId,
1889
- dedupeKey: `agent-message:${reserved.receipt.id}`,
1890
- summary: input.text,
1891
- payload: {
1892
- type: "agent_message",
1893
- text: input.text,
1894
- operationId: reserved.receipt.id,
1895
- },
1896
- lineage: {
1897
- callerSessionId: input.actor.sessionId,
1898
- callerTurnId: input.actor.turnId,
1899
- callerAttemptId: input.actor.attemptId,
1900
- callerExecutionGeneration: input.actor.executionGeneration,
1901
- },
1902
- personalConnectionDelegations,
1903
- state: "pending",
1904
- })
2105
+ .values(
2106
+ withLosslessContentWriteVersion(
2107
+ withLosslessContentWriteVersion(
2108
+ {
2109
+ accountId: input.accountId,
2110
+ workspaceId: input.workspaceId,
2111
+ sessionId: input.targetSessionId,
2112
+ kind: "agent_message",
2113
+ classification: "info",
2114
+ sourceId: input.actor.sessionId,
2115
+ dedupeKey: `agent-message:${reserved.receipt.id}`,
2116
+ summary: input.text,
2117
+ payload: {
2118
+ type: "agent_message",
2119
+ text: input.text,
2120
+ operationId: reserved.receipt.id,
2121
+ },
2122
+ lineage: {
2123
+ callerSessionId: input.actor.sessionId,
2124
+ callerTurnId: input.actor.turnId,
2125
+ callerAttemptId: input.actor.attemptId,
2126
+ callerExecutionGeneration: input.actor.executionGeneration,
2127
+ },
2128
+ personalConnectionDelegations,
2129
+ state: "pending",
2130
+ },
2131
+ "summary",
2132
+ "summaryCodecVersion",
2133
+ ),
2134
+ "payload",
2135
+ "payloadCodecVersion",
2136
+ ),
2137
+ )
1905
2138
  .returning({ id: schema.sessionSystemUpdates.id });
1906
2139
  if (!update) throw new SessionControlInvariantError("Agent message was not inserted");
1907
2140
  const [event] = await db
1908
2141
  .insert(schema.sessionEvents)
1909
- .values({
1910
- accountId: input.accountId,
1911
- workspaceId: input.workspaceId,
1912
- sessionId: input.targetSessionId,
1913
- sequence: session.lastSequence + 1,
1914
- type: "system.update.pending",
1915
- payload: {
1916
- updateId: update.id,
1917
- kind: "agent_message",
1918
- sourceSessionId: input.actor.sessionId,
1919
- },
1920
- occurredAt: now,
1921
- })
2142
+ .values(
2143
+ withLosslessContentWriteVersion(
2144
+ {
2145
+ accountId: input.accountId,
2146
+ workspaceId: input.workspaceId,
2147
+ sessionId: input.targetSessionId,
2148
+ sequence: session.lastSequence + 1,
2149
+ type: "system.update.pending",
2150
+ payload: {
2151
+ updateId: update.id,
2152
+ kind: "agent_message",
2153
+ sourceSessionId: input.actor.sessionId,
2154
+ },
2155
+ occurredAt: now,
2156
+ },
2157
+ "payload",
2158
+ "payloadCodecVersion",
2159
+ ),
2160
+ )
1922
2161
  .returning({ id: schema.sessionEvents.id });
1923
2162
  if (!event) throw new SessionControlInvariantError("Agent message event was not inserted");
1924
2163
  const workflowId = session.temporalWorkflowId ?? `session-${session.id}`;
@@ -1939,21 +2178,27 @@ export async function sendAgentMessageInTransaction(
1939
2178
  updatedAt: now,
1940
2179
  })
1941
2180
  .where(eq(schema.sessions.id, input.targetSessionId));
1942
- await db.insert(schema.auditEvents).values({
1943
- accountId: input.accountId,
1944
- workspaceId: input.workspaceId,
1945
- subjectId: `attempt:${input.actor.attemptId}`,
1946
- action: "session.agent_message",
1947
- targetType: "session",
1948
- targetId: input.targetSessionId,
1949
- metadata: {
1950
- operationId: reserved.receipt.id,
1951
- callerSessionId: input.actor.sessionId,
1952
- callerTurnId: input.actor.turnId,
1953
- callerAttemptId: input.actor.attemptId,
1954
- callerExecutionGeneration: input.actor.executionGeneration,
1955
- },
1956
- });
2181
+ await db.insert(schema.auditEvents).values(
2182
+ withLosslessContentWriteVersion(
2183
+ {
2184
+ accountId: input.accountId,
2185
+ workspaceId: input.workspaceId,
2186
+ subjectId: `attempt:${input.actor.attemptId}`,
2187
+ action: "session.agent_message",
2188
+ targetType: "session",
2189
+ targetId: input.targetSessionId,
2190
+ metadata: {
2191
+ operationId: reserved.receipt.id,
2192
+ callerSessionId: input.actor.sessionId,
2193
+ callerTurnId: input.actor.turnId,
2194
+ callerAttemptId: input.actor.attemptId,
2195
+ callerExecutionGeneration: input.actor.executionGeneration,
2196
+ },
2197
+ },
2198
+ "metadata",
2199
+ "metadataCodecVersion",
2200
+ ),
2201
+ );
1957
2202
  const receipt = await updateSessionCommandReceiptResult(db, reserved.receipt.id, {
1958
2203
  result: {
1959
2204
  updateId: update.id,
@@ -1978,7 +2223,7 @@ export async function sendAgentMessageInTransaction(
1978
2223
  }
1979
2224
 
1980
2225
  export async function steerAgentSessionInTransaction(
1981
- db: Database,
2226
+ db: SessionActivityDatabase,
1982
2227
  input: {
1983
2228
  accountId: string;
1984
2229
  workspaceId: string;
@@ -2084,33 +2329,43 @@ export async function steerAgentSessionInTransaction(
2084
2329
  const now = new Date();
2085
2330
  const [update] = await db
2086
2331
  .insert(schema.sessionSystemUpdates)
2087
- .values({
2088
- accountId: input.accountId,
2089
- workspaceId: input.workspaceId,
2090
- sessionId: input.targetSessionId,
2091
- kind: "agent_steer_instruction",
2092
- classification: "action_required",
2093
- sourceId: input.actor.sessionId,
2094
- dedupeKey: `agent-steer:${reserved.receipt.id}`,
2095
- summary: input.instruction,
2096
- payload: {
2097
- type: "agent_steer_instruction",
2098
- instruction: input.instruction,
2099
- operationId: reserved.receipt.id,
2100
- },
2101
- lineage: {
2102
- callerSessionId: input.actor.sessionId,
2103
- callerTurnId: input.actor.turnId,
2104
- callerAttemptId: input.actor.attemptId,
2105
- callerExecutionGeneration: input.actor.executionGeneration,
2106
- },
2107
- personalConnectionDelegations,
2108
- state: "pending",
2109
- })
2332
+ .values(
2333
+ withLosslessContentWriteVersion(
2334
+ withLosslessContentWriteVersion(
2335
+ {
2336
+ accountId: input.accountId,
2337
+ workspaceId: input.workspaceId,
2338
+ sessionId: input.targetSessionId,
2339
+ kind: "agent_steer_instruction",
2340
+ classification: "action_required",
2341
+ sourceId: input.actor.sessionId,
2342
+ dedupeKey: `agent-steer:${reserved.receipt.id}`,
2343
+ summary: input.instruction,
2344
+ payload: {
2345
+ type: "agent_steer_instruction",
2346
+ instruction: input.instruction,
2347
+ operationId: reserved.receipt.id,
2348
+ },
2349
+ lineage: {
2350
+ callerSessionId: input.actor.sessionId,
2351
+ callerTurnId: input.actor.turnId,
2352
+ callerAttemptId: input.actor.attemptId,
2353
+ callerExecutionGeneration: input.actor.executionGeneration,
2354
+ },
2355
+ personalConnectionDelegations,
2356
+ state: "pending",
2357
+ },
2358
+ "summary",
2359
+ "summaryCodecVersion",
2360
+ ),
2361
+ "payload",
2362
+ "payloadCodecVersion",
2363
+ ),
2364
+ )
2110
2365
  .returning({ id: schema.sessionSystemUpdates.id });
2111
2366
  if (!update) throw new SessionControlInvariantError("Agent Steer instruction was not inserted");
2112
2367
  let sequence = supersession.lastSequence;
2113
- const events: Array<typeof schema.sessionEvents.$inferInsert> = [];
2368
+ const events: SessionEventInsertWithPayload[] = [];
2114
2369
  if (supersession.replacedTurn && !supersession.liveCurrentTurnId) {
2115
2370
  events.push({
2116
2371
  accountId: input.accountId,
@@ -2175,9 +2430,12 @@ export async function steerAgentSessionInTransaction(
2175
2430
  occurredAt: now,
2176
2431
  },
2177
2432
  );
2178
- const insertedEvents = await db.insert(schema.sessionEvents).values(events).returning({
2179
- id: schema.sessionEvents.id,
2180
- });
2433
+ const insertedEvents = await db
2434
+ .insert(schema.sessionEvents)
2435
+ .values(withLosslessContentWriteVersion(events, "payload", "payloadCodecVersion"))
2436
+ .returning({
2437
+ id: schema.sessionEvents.id,
2438
+ });
2181
2439
  const workflowId = session.temporalWorkflowId ?? `session-${session.id}`;
2182
2440
  const wakeRevision = await registerSessionWorkflowWakeInTransaction(db, {
2183
2441
  accountId: input.accountId,
@@ -2196,24 +2454,30 @@ export async function steerAgentSessionInTransaction(
2196
2454
  updatedAt: now,
2197
2455
  })
2198
2456
  .where(eq(schema.sessions.id, input.targetSessionId));
2199
- await db.insert(schema.auditEvents).values({
2200
- accountId: input.accountId,
2201
- workspaceId: input.workspaceId,
2202
- subjectId: `attempt:${input.actor.attemptId}`,
2203
- action: "session.agent_steer",
2204
- targetType: "session",
2205
- targetId: input.targetSessionId,
2206
- metadata: {
2207
- operationId: reserved.receipt.id,
2208
- callerSessionId: input.actor.sessionId,
2209
- callerTurnId: input.actor.turnId,
2210
- callerAttemptId: input.actor.attemptId,
2211
- callerExecutionGeneration: input.actor.executionGeneration,
2212
- controlRevision: resumed.revision,
2213
- interruptionCount: supersession.interruptionCount,
2214
- workspaceControlEventId: resumed.workspaceControlEventId,
2215
- },
2216
- });
2457
+ await db.insert(schema.auditEvents).values(
2458
+ withLosslessContentWriteVersion(
2459
+ {
2460
+ accountId: input.accountId,
2461
+ workspaceId: input.workspaceId,
2462
+ subjectId: `attempt:${input.actor.attemptId}`,
2463
+ action: "session.agent_steer",
2464
+ targetType: "session",
2465
+ targetId: input.targetSessionId,
2466
+ metadata: {
2467
+ operationId: reserved.receipt.id,
2468
+ callerSessionId: input.actor.sessionId,
2469
+ callerTurnId: input.actor.turnId,
2470
+ callerAttemptId: input.actor.attemptId,
2471
+ callerExecutionGeneration: input.actor.executionGeneration,
2472
+ controlRevision: resumed.revision,
2473
+ interruptionCount: supersession.interruptionCount,
2474
+ workspaceControlEventId: resumed.workspaceControlEventId,
2475
+ },
2476
+ },
2477
+ "metadata",
2478
+ "metadataCodecVersion",
2479
+ ),
2480
+ );
2217
2481
  const eventIds = insertedEvents.map((event) => event.id);
2218
2482
  const receipt = await updateSessionCommandReceiptResult(db, reserved.receipt.id, {
2219
2483
  controlRevision: resumed.revision,