@codemation/host 0.8.0 → 0.9.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 (148) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/{ApiPaths-Dv1dcHu_.js → ApiPaths-DCvrlIjg.js} +12 -1
  3. package/dist/{ApiPaths-Dv1dcHu_.js.map → ApiPaths-DCvrlIjg.js.map} +1 -1
  4. package/dist/{AppConfigFactory-Cx4qQvRk.js → AppConfigFactory-D4LL1aOR.js} +77 -297
  5. package/dist/AppConfigFactory-D4LL1aOR.js.map +1 -0
  6. package/dist/{AppConfigFactory-BT0y0LVC.d.ts → AppConfigFactory-DncmwCD1.d.ts} +2918 -199
  7. package/dist/{AppContainerFactory-DRTjG7nG.js → AppContainerFactory-CHCXP2rn.js} +1735 -474
  8. package/dist/AppContainerFactory-CHCXP2rn.js.map +1 -0
  9. package/dist/{CodemationAppContext-CGFYVcSb.d.ts → CodemationAppContext-K51b7oXe.d.ts} +3 -3
  10. package/dist/{CodemationAuthoring.types-DiKKogum.d.ts → CodemationAuthoring.types-BXlXIl4K.d.ts} +4 -4
  11. package/dist/{CodemationConfigNormalizer-48f-T66P.d.ts → CodemationConfigNormalizer-B4rDYC9h.d.ts} +3 -3
  12. package/dist/{CodemationConsumerConfigLoader-_PIYqwVx.d.ts → CodemationConsumerConfigLoader-Dt4jyLx6.d.ts} +2 -2
  13. package/dist/{CodemationPluginListMerger-DP7djJ9S.d.ts → CodemationPluginListMerger-DS6I3Xe0.d.ts} +24 -12
  14. package/dist/{persistenceServer-C-hH4z6l.js → CodemationPostgresPrismaClientFactory-C7156Fe-.js} +2 -2
  15. package/dist/CodemationPostgresPrismaClientFactory-C7156Fe-.js.map +1 -0
  16. package/dist/CodemationPostgresPrismaClientFactory-CTNTPnDr.d.ts +9 -0
  17. package/dist/{CredentialContractsRegistry-Bq2bq28t.d.ts → CredentialContractsRegistry-Dgu-rEXi.d.ts} +16 -3
  18. package/dist/{CredentialServices-BLloBztI.d.ts → CredentialServices-B3wPyp2y.d.ts} +4 -4
  19. package/dist/{CredentialServices-Dk8yypeL.js → CredentialServices-Bios0dM8.js} +10 -4
  20. package/dist/CredentialServices-Bios0dM8.js.map +1 -0
  21. package/dist/{InternalHonoApiRouteRegistrar-c7t3KnV_.d.ts → InternalHonoApiRouteRegistrar-Ce1yxpnO.d.ts} +1 -1
  22. package/dist/{InternalPingRegistrar-DY3kSfxP.js → InternalPingRegistrar-BavAAnvk.js} +19 -16
  23. package/dist/InternalPingRegistrar-BavAAnvk.js.map +1 -0
  24. package/dist/{ItemsInputNormalizer-_RwIfRIQ.d.ts → ItemsInputNormalizer-CFkfNMLt.d.ts} +1434 -1225
  25. package/dist/PrismaMigrationDeployer-DdEcXXVi.d.ts +14 -0
  26. package/dist/{PublicFrontendBootstrapFactory-Dv04tJ-6.d.ts → PublicFrontendBootstrapFactory-ClEjZP74.d.ts} +2 -2
  27. package/dist/{PublicFrontendBootstrapJsonCodec-CXG9Dxft.d.ts → PublicFrontendBootstrapJsonCodec-HNItQ7ol.d.ts} +6 -1
  28. package/dist/{TelemetryContracts-BtDx84Cp.d.ts → TelemetryContracts-DpZEODQM.d.ts} +2 -2
  29. package/dist/{WorkflowPolicyUiPresentationFactory-6MyjCvBO.d.ts → WorkflowPolicyUiPresentationFactory-BNn2fvR_.d.ts} +2 -2
  30. package/dist/{WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js → WorkflowPolicyUiPresentationFactory-DfvD2VHk.js} +1 -1
  31. package/dist/{WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js.map → WorkflowPolicyUiPresentationFactory-DfvD2VHk.js.map} +1 -1
  32. package/dist/authoring.d.ts +4 -4
  33. package/dist/client.d.ts +1 -1
  34. package/dist/client.js +1 -1
  35. package/dist/consumer.d.ts +5 -5
  36. package/dist/credentials.d.ts +5 -5
  37. package/dist/credentials.js +1 -1
  38. package/dist/devServerSidecar.d.ts +2 -2
  39. package/dist/dto.d.ts +5 -5
  40. package/dist/{index-DilAYwnH.d.ts → index-ChIfeWzk.d.ts} +71 -28
  41. package/dist/index.d.ts +17 -16
  42. package/dist/index.js +8 -8
  43. package/dist/infrastructure/persistence/PrismaMigrationOperations.d.ts +44 -0
  44. package/dist/infrastructure/persistence/PrismaMigrationOperations.js +302 -0
  45. package/dist/infrastructure/persistence/PrismaMigrationOperations.js.map +1 -0
  46. package/dist/mapping.d.ts +2 -2
  47. package/dist/mapping.js +1 -1
  48. package/dist/nextServer.d.ts +15 -13
  49. package/dist/nextServer.js +6 -6
  50. package/dist/pairing.d.ts +28 -9
  51. package/dist/pairing.js +19 -3
  52. package/dist/pairing.js.map +1 -0
  53. package/dist/{pairing.types-snfZ_OzB.d.ts → pairing.types-D9Bjn98U.d.ts} +1 -1
  54. package/dist/persistenceServer.d.ts +31 -7
  55. package/dist/persistenceServer.js +2 -2
  56. package/dist/{server-09PKasWR.d.ts → server-B5trn7y4.d.ts} +5 -5
  57. package/dist/{server-vtRCPgRJ.js → server-CNj_y0QO.js} +4 -4
  58. package/dist/{server-vtRCPgRJ.js.map → server-CNj_y0QO.js.map} +1 -1
  59. package/dist/server.d.ts +10 -10
  60. package/dist/server.js +8 -8
  61. package/package.json +11 -10
  62. package/playwright.config.ts +8 -2
  63. package/playwright.scaffolded-dev.config.ts +8 -2
  64. package/prisma/migrations/20260526120000_credential_material_pointer/migration.sql +18 -0
  65. package/prisma/migrations/20260527120000_add_human_task/migration.sql +32 -0
  66. package/prisma/migrations/20260527130000_add_hitl_state_json/migration.sql +6 -0
  67. package/prisma/migrations/20260527130000_add_hmac_nonce/migration.sql +12 -0
  68. package/prisma/migrations.sqlite/20260526120000_credential_material_pointer/migration.sql +13 -0
  69. package/prisma/migrations.sqlite/20260527120000_add_human_task/migration.sql +30 -0
  70. package/prisma/migrations.sqlite/20260527130000_add_hitl_state_json/migration.sql +6 -0
  71. package/prisma/migrations.sqlite/20260527130000_add_hmac_nonce/migration.sql +9 -0
  72. package/prisma/schema.postgresql.prisma +48 -0
  73. package/prisma/schema.sqlite.prisma +48 -0
  74. package/prisma-generated/prisma-postgresql-client/edge.js +40 -6
  75. package/prisma-generated/prisma-postgresql-client/index-browser.js +36 -2
  76. package/prisma-generated/prisma-postgresql-client/index.d.ts +3179 -163
  77. package/prisma-generated/prisma-postgresql-client/index.js +40 -6
  78. package/prisma-generated/prisma-postgresql-client/package.json +1 -1
  79. package/prisma-generated/prisma-postgresql-client/schema.prisma +48 -0
  80. package/prisma-generated/prisma-sqlite-client/edge.js +40 -6
  81. package/prisma-generated/prisma-sqlite-client/index-browser.js +36 -2
  82. package/prisma-generated/prisma-sqlite-client/index.d.ts +3175 -163
  83. package/prisma-generated/prisma-sqlite-client/index.js +40 -6
  84. package/prisma-generated/prisma-sqlite-client/package.json +1 -1
  85. package/prisma-generated/prisma-sqlite-client/schema.prisma +48 -0
  86. package/src/application/contracts/CredentialContractsRegistry.ts +15 -0
  87. package/src/application/credentials/AppGalleryProjector.ts +69 -0
  88. package/src/application/hitl/DecideHumanTaskCommandHandler.ts +149 -0
  89. package/src/application/hitl/DecisionSchemaValidator.ts +22 -0
  90. package/src/application/hitl/HitlCallbackHandler.ts +96 -0
  91. package/src/application/mapping/WorkflowDefinitionMapper.ts +1 -3
  92. package/src/application/queries/CredentialQueryHandlers.ts +2 -0
  93. package/src/application/queries/GetCredentialAppsQuery.ts +4 -0
  94. package/src/application/queries/GetCredentialAppsQueryHandler.ts +27 -0
  95. package/src/application/telemetry/ResumeTelemetryContextForRun.ts +53 -0
  96. package/src/application/telemetry/TelemetryRetentionTimestampFactory.ts +9 -8
  97. package/src/applicationTokens.ts +11 -1
  98. package/src/auth/managed/ManagedCorsMiddleware.ts +20 -5
  99. package/src/bootstrap/AppContainerFactory.ts +100 -0
  100. package/src/credentials/CachingCredentialMaterialProvider.ts +96 -0
  101. package/src/credentials/CompositeCredentialMaterialProvider.ts +47 -0
  102. package/src/credentials/ControlPlaneCatalogFetcher.ts +4 -24
  103. package/src/credentials/ControlPlaneCredentialMaterialProvider.ts +79 -0
  104. package/src/credentials/CredentialOAuth2MaterialReader.ts +2 -7
  105. package/src/credentials/InternalCredentialsBindingRegistrar.ts +83 -0
  106. package/src/credentials/LocalCredentialMaterialProvider.ts +92 -0
  107. package/src/domain/credentials/CredentialInstanceService.ts +5 -1
  108. package/src/domain/credentials/CredentialTypeRegistryImpl.ts +18 -4
  109. package/src/domain/workflows/WorkflowActivationPreflightRules.ts +7 -4
  110. package/src/dto.ts +2 -0
  111. package/src/hitl/ControlPlaneInboxChannel.ts +102 -0
  112. package/src/hitl/HitlResumeTokenSigner.ts +80 -0
  113. package/src/hitl/HitlTimeoutJobScheduler.ts +89 -0
  114. package/src/hitl/HitlTimeoutWorker.ts +143 -0
  115. package/src/hitl/InboxChannelResolver.ts +49 -0
  116. package/src/hitl/LocalInboxChannel.ts +37 -0
  117. package/src/infrastructure/persistence/PrismaCredentialStore.ts +10 -0
  118. package/src/infrastructure/persistence/PrismaHmacNonceStore.ts +29 -0
  119. package/src/infrastructure/persistence/PrismaHumanTaskStore.ts +156 -0
  120. package/src/infrastructure/persistence/PrismaMigrationDeployer.ts +53 -383
  121. package/src/infrastructure/persistence/PrismaMigrationOperations.ts +401 -0
  122. package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +39 -0
  123. package/src/mcp/AgentMcpIntegrationImpl.ts +5 -1
  124. package/src/pairing/HmacNonceStore.ts +14 -0
  125. package/src/pairing/HmacNonceStoreToken.ts +4 -0
  126. package/src/pairing/HmacRequestSigner.ts +10 -1
  127. package/src/pairing/InMemoryHmacNonceStore.ts +24 -0
  128. package/src/pairing/IncomingHmacVerifier.ts +28 -12
  129. package/src/pairing/InternalHmacAuthMiddleware.ts +1 -1
  130. package/src/pairing/index.ts +3 -0
  131. package/src/presentation/http/ApiPaths.ts +14 -0
  132. package/src/presentation/http/hono/HonoHttpAnonymousRoutePolicyRegistry.ts +4 -0
  133. package/src/presentation/http/hono/registrars/CredentialHonoApiRouteRegistrar.ts +1 -0
  134. package/src/presentation/http/hono/registrars/HitlDecideHonoApiRouteRegistrar.ts +54 -0
  135. package/src/presentation/http/hono/registrars/HitlInternalCallbackHonoApiRouteRegistrar.ts +33 -0
  136. package/src/presentation/http/hono/registrars/HitlResumeHonoApiRouteRegistrar.ts +43 -0
  137. package/src/presentation/http/routeHandlers/CredentialHttpRouteHandler.ts +9 -0
  138. package/src/presentation/http/routeHandlers/OAuth2HttpRouteHandlerFactory.ts +1 -1
  139. package/src/server.ts +7 -2
  140. package/src/workflows/InternalWorkflowTestRunRegistrar.ts +9 -0
  141. package/tsconfig.json +1 -0
  142. package/dist/AppConfigFactory-Cx4qQvRk.js.map +0 -1
  143. package/dist/AppContainerFactory-DRTjG7nG.js.map +0 -1
  144. package/dist/CredentialServices-Dk8yypeL.js.map +0 -1
  145. package/dist/InternalPingRegistrar-DY3kSfxP.js.map +0 -1
  146. package/dist/persistenceServer-B71RGvSj.d.ts +0 -30
  147. package/dist/persistenceServer-C-hH4z6l.js.map +0 -1
  148. package/src/credentials/catalogTypes.ts +0 -4
@@ -0,0 +1,37 @@
1
+ import { createHash } from "node:crypto";
2
+ import { inject, injectable } from "@codemation/core";
3
+ import type { InboxChannel, InboxDeliverArgs, InboxDelivery } from "@codemation/core";
4
+ import { ServerLoggerFactory } from "../infrastructure/logging/ServerLoggerFactory";
5
+ import type { Logger } from "../application/logging/Logger";
6
+
7
+ /**
8
+ * Local inbox channel for non-managed (dev) mode.
9
+ *
10
+ * `deliver` logs the pending task so developers without the UI can see the
11
+ * taskId / resumeUrl in the console, then returns an `InboxDelivery` where
12
+ * `inboxItemId === task.taskId`. The local channel has no separate inbox
13
+ * concept — the dev inbox UI queries `HumanTaskStore.findAllPending()` directly.
14
+ *
15
+ * Security (T4): The raw resume token is NOT logged. Only the first 8 hex characters of
16
+ * sha256(rawToken) are emitted as a fingerprint to enable log-correlation without leaking
17
+ * the workspace-bound authority token. The local dev inbox UI decides via
18
+ * POST /api/hitl/tasks/:taskId/decide (session-auth), so the token URL is not needed at
19
+ * the log level.
20
+ */
21
+ @injectable()
22
+ export class LocalInboxChannel implements InboxChannel {
23
+ readonly kind = "local" as const;
24
+ private readonly logger: Logger;
25
+
26
+ constructor(@inject(ServerLoggerFactory) loggerFactory: ServerLoggerFactory) {
27
+ this.logger = loggerFactory.create("codemation.hitl.local-inbox");
28
+ }
29
+
30
+ async deliver(args: InboxDeliverArgs): Promise<InboxDelivery> {
31
+ const tokenFingerprint = createHash("sha256").update(args.task.resumeUrl, "utf8").digest("hex").slice(0, 8);
32
+ this.logger.info(
33
+ `HITL task pending in local inbox — taskId=${args.task.taskId} title="${args.subject.title}" tokenFingerprint=${tokenFingerprint}`,
34
+ );
35
+ return { kind: "local", inboxItemId: args.task.taskId };
36
+ }
37
+ }
@@ -46,6 +46,8 @@ export class PrismaCredentialStore implements CredentialStore {
46
46
  setupStatus: args.instance.setupStatus,
47
47
  createdAt: args.instance.createdAt,
48
48
  updatedAt: args.instance.updatedAt,
49
+ materialSource: args.instance.material.source,
50
+ materialRef: args.instance.material.ref,
49
51
  },
50
52
  update: {
51
53
  typeId: args.instance.typeId,
@@ -56,6 +58,8 @@ export class PrismaCredentialStore implements CredentialStore {
56
58
  tagsJson: JSON.stringify(args.instance.tags),
57
59
  setupStatus: args.instance.setupStatus,
58
60
  updatedAt: args.instance.updatedAt,
61
+ materialSource: args.instance.material.source,
62
+ materialRef: args.instance.material.ref,
59
63
  },
60
64
  });
61
65
  if (args.secretMaterial) {
@@ -323,6 +327,8 @@ export class PrismaCredentialStore implements CredentialStore {
323
327
  setupStatus: string;
324
328
  createdAt: string;
325
329
  updatedAt: string;
330
+ materialSource: string;
331
+ materialRef: string;
326
332
  }>,
327
333
  ): CredentialInstanceRecord {
328
334
  return {
@@ -336,6 +342,10 @@ export class PrismaCredentialStore implements CredentialStore {
336
342
  setupStatus: row.setupStatus as CredentialInstanceRecord["setupStatus"],
337
343
  createdAt: row.createdAt,
338
344
  updatedAt: row.updatedAt,
345
+ material: {
346
+ source: row.materialSource as CredentialInstanceRecord["material"]["source"],
347
+ ref: row.materialRef === "" ? row.instanceId : row.materialRef,
348
+ },
339
349
  };
340
350
  }
341
351
 
@@ -0,0 +1,29 @@
1
+ import { inject, injectable } from "@codemation/core";
2
+ import type { HmacNonceStore } from "../../pairing/HmacNonceStore";
3
+ import { PrismaDatabaseClientToken, type PrismaDatabaseClient } from "./PrismaDatabaseClient";
4
+
5
+ /**
6
+ * Durable HMAC nonce store backed by the Prisma database (T6 security fix).
7
+ *
8
+ * Uses an upsert with `create`+unique constraint to achieve atomic
9
+ * "insert if not exists": a Prisma unique-constraint violation means the nonce
10
+ * was already present (replay). Expired nonces are pruned inline on each call —
11
+ * acceptable for v1 given the low write rate of the pairing channel.
12
+ */
13
+ @injectable()
14
+ export class PrismaHmacNonceStore implements HmacNonceStore {
15
+ constructor(@inject(PrismaDatabaseClientToken) private readonly prisma: PrismaDatabaseClient) {}
16
+
17
+ async recordIfNew(nonce: string, expiresAt: Date): Promise<boolean> {
18
+ // Prune expired nonces inline (v1 housekeeping; low write rate on this table)
19
+ await this.prisma.hmacNonce.deleteMany({ where: { expiresAt: { lt: new Date() } } });
20
+
21
+ try {
22
+ await this.prisma.hmacNonce.create({ data: { nonce, expiresAt } });
23
+ return true;
24
+ } catch {
25
+ // Unique constraint violation → nonce already present → replay
26
+ return false;
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,156 @@
1
+ import { inject, injectable } from "@codemation/core";
2
+ import type { HumanTaskActor, HumanTaskRecord, HumanTaskStore } from "@codemation/core";
3
+ import type { JsonValue } from "@codemation/core";
4
+ import { PrismaDatabaseClientToken, type PrismaDatabaseClient } from "./PrismaDatabaseClient";
5
+
6
+ @injectable()
7
+ export class PrismaHumanTaskStore implements HumanTaskStore {
8
+ constructor(@inject(PrismaDatabaseClientToken) private readonly prisma: PrismaDatabaseClient) {}
9
+
10
+ async create(record: HumanTaskRecord): Promise<void> {
11
+ await this.prisma.humanTask.create({
12
+ data: {
13
+ id: record.id,
14
+ runId: record.runId,
15
+ workflowId: record.workflowId,
16
+ workspaceId: record.workspaceId ?? null,
17
+ nodeId: record.nodeId,
18
+ activationId: record.activationId,
19
+ itemIndex: record.itemIndex,
20
+ status: record.status,
21
+ channel: record.channel,
22
+ subjectJson: JSON.stringify(record.subject),
23
+ metadataJson: JSON.stringify(record.metadata),
24
+ decisionSchemaJson: record.decisionSchemaJson,
25
+ decisionSchemaHash: record.decisionSchemaHash,
26
+ onTimeout: record.onTimeout,
27
+ deliveryRefJson: record.deliveryRef !== undefined ? JSON.stringify(record.deliveryRef) : null,
28
+ decisionJson: null,
29
+ decidedAt: null,
30
+ decidedByJson: null,
31
+ resumeTokenHash: record.resumeTokenHash,
32
+ expiresAt: record.expiresAt,
33
+ createdAt: record.createdAt,
34
+ },
35
+ });
36
+ }
37
+
38
+ async findById(taskId: string): Promise<HumanTaskRecord | undefined> {
39
+ const row = await this.prisma.humanTask.findUnique({ where: { id: taskId } });
40
+ return row ? this.toRecord(row) : undefined;
41
+ }
42
+
43
+ async findByResumeTokenHash(tokenHash: string): Promise<HumanTaskRecord | undefined> {
44
+ const row = await this.prisma.humanTask.findFirst({ where: { resumeTokenHash: tokenHash } });
45
+ return row ? this.toRecord(row) : undefined;
46
+ }
47
+
48
+ async findPendingForWorkspace(workspaceId: string): Promise<ReadonlyArray<HumanTaskRecord>> {
49
+ const rows = await this.prisma.humanTask.findMany({
50
+ where: { workspaceId, status: "pending" },
51
+ orderBy: { expiresAt: "asc" },
52
+ });
53
+ return rows.map((row) => this.toRecord(row));
54
+ }
55
+
56
+ async findAllPending(): Promise<ReadonlyArray<HumanTaskRecord>> {
57
+ const rows = await this.prisma.humanTask.findMany({
58
+ where: { status: "pending" },
59
+ orderBy: { expiresAt: "asc" },
60
+ });
61
+ return rows.map((row) => this.toRecord(row));
62
+ }
63
+
64
+ async markDecided(args: {
65
+ taskId: string;
66
+ decision: JsonValue;
67
+ decidedBy: HumanTaskActor;
68
+ decidedAt: Date;
69
+ }): Promise<void> {
70
+ await this.prisma.humanTask.update({
71
+ where: { id: args.taskId },
72
+ data: {
73
+ status: "decided",
74
+ decisionJson: JSON.stringify(args.decision),
75
+ decidedAt: args.decidedAt,
76
+ decidedByJson: JSON.stringify(args.decidedBy),
77
+ },
78
+ });
79
+ }
80
+
81
+ async markTimedOut(taskId: string): Promise<void> {
82
+ await this.prisma.humanTask.update({
83
+ where: { id: taskId },
84
+ data: { status: "timed_out" },
85
+ });
86
+ }
87
+
88
+ async markAutoAccepted(taskId: string): Promise<void> {
89
+ await this.prisma.humanTask.update({
90
+ where: { id: taskId },
91
+ data: { status: "auto_accepted" },
92
+ });
93
+ }
94
+
95
+ async markCancelled(taskId: string): Promise<void> {
96
+ await this.prisma.humanTask.update({
97
+ where: { id: taskId },
98
+ data: { status: "cancelled" },
99
+ });
100
+ }
101
+
102
+ async cancelPendingForRun(runId: string): Promise<void> {
103
+ await this.prisma.humanTask.updateMany({
104
+ where: { runId, status: "pending" },
105
+ data: { status: "cancelled" },
106
+ });
107
+ }
108
+
109
+ private toRecord(row: {
110
+ id: string;
111
+ runId: string;
112
+ workflowId: string;
113
+ workspaceId: string | null;
114
+ nodeId: string;
115
+ activationId: string;
116
+ itemIndex: number;
117
+ status: string;
118
+ channel: string;
119
+ subjectJson: string;
120
+ metadataJson: string;
121
+ decisionSchemaJson: string;
122
+ decisionSchemaHash: string;
123
+ onTimeout: string;
124
+ deliveryRefJson: string | null;
125
+ decisionJson: string | null;
126
+ decidedAt: Date | null;
127
+ decidedByJson: string | null;
128
+ resumeTokenHash: string;
129
+ expiresAt: Date;
130
+ createdAt: Date;
131
+ }): HumanTaskRecord {
132
+ return {
133
+ id: row.id,
134
+ runId: row.runId,
135
+ workflowId: row.workflowId,
136
+ workspaceId: row.workspaceId ?? undefined,
137
+ nodeId: row.nodeId,
138
+ activationId: row.activationId,
139
+ itemIndex: row.itemIndex,
140
+ status: row.status as HumanTaskRecord["status"],
141
+ channel: row.channel,
142
+ subject: JSON.parse(row.subjectJson),
143
+ metadata: JSON.parse(row.metadataJson),
144
+ decisionSchemaJson: row.decisionSchemaJson,
145
+ decisionSchemaHash: row.decisionSchemaHash,
146
+ onTimeout: row.onTimeout as "halt" | "auto-accept",
147
+ deliveryRef: row.deliveryRefJson !== null ? (JSON.parse(row.deliveryRefJson) as JsonValue) : undefined,
148
+ decision: row.decisionJson !== null ? (JSON.parse(row.decisionJson) as JsonValue) : undefined,
149
+ decidedAt: row.decidedAt ?? undefined,
150
+ decidedBy: row.decidedByJson !== null ? JSON.parse(row.decidedByJson) : undefined,
151
+ resumeTokenHash: row.resumeTokenHash,
152
+ expiresAt: row.expiresAt,
153
+ createdAt: row.createdAt,
154
+ };
155
+ }
156
+ }