@intx/db 0.2.2 → 0.4.0

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 (207) hide show
  1. package/README.md +13 -2
  2. package/dist/approval-store.d.ts +59 -0
  3. package/dist/approval-store.js +92 -0
  4. package/dist/asset-resolution.d.ts +2 -2
  5. package/dist/backfill-principal-keys.d.ts +9 -0
  6. package/dist/backfill-principal-keys.js +51 -0
  7. package/dist/client.d.ts +37 -2
  8. package/dist/client.js +6 -0
  9. package/dist/config.d.ts +1 -0
  10. package/dist/config.js +2 -0
  11. package/dist/connection.js +12 -8
  12. package/dist/credential-resolution.d.ts +181 -20
  13. package/dist/credential-resolution.js +251 -3
  14. package/dist/grant-store.js +35 -13
  15. package/dist/index.d.ts +20 -5
  16. package/dist/index.js +19 -5
  17. package/dist/model-source-resolution.d.ts +54 -7
  18. package/dist/model-source-resolution.js +177 -22
  19. package/dist/parse-row.d.ts +245 -34
  20. package/dist/parse-row.js +117 -18
  21. package/dist/principal-key-store.d.ts +51 -0
  22. package/dist/principal-key-store.js +82 -0
  23. package/dist/principal-store.d.ts +44 -0
  24. package/dist/principal-store.js +67 -0
  25. package/dist/rekey-credential-secrets.d.ts +13 -0
  26. package/dist/rekey-credential-secrets.js +61 -0
  27. package/dist/schema/approvals.d.ts +245 -0
  28. package/dist/schema/approvals.js +57 -0
  29. package/dist/schema/catalog.d.ts +17 -0
  30. package/dist/schema/catalog.js +7 -1
  31. package/dist/schema/credentials.d.ts +1 -1
  32. package/dist/schema/git-tokens.js +1 -1
  33. package/dist/schema/grants.js +8 -2
  34. package/dist/schema/index.d.ts +10 -4
  35. package/dist/schema/index.js +10 -4
  36. package/dist/schema/messages.d.ts +5 -5
  37. package/dist/schema/messages.js +20 -9
  38. package/dist/schema/offerings.js +8 -2
  39. package/dist/schema/{workflow-deployments.d.ts → principal-keys.d.ts} +29 -32
  40. package/dist/schema/principal-keys.js +28 -0
  41. package/dist/schema/principals.d.ts +2 -2
  42. package/dist/schema/principals.js +7 -5
  43. package/dist/schema/providers.d.ts +17 -0
  44. package/dist/schema/providers.js +5 -0
  45. package/dist/schema/roles.js +6 -2
  46. package/dist/schema/session-assets.d.ts +1 -38
  47. package/dist/schema/session-assets.js +36 -19
  48. package/dist/schema/sessions.js +7 -2
  49. package/dist/schema/sidecar-allocation.d.ts +385 -0
  50. package/dist/schema/sidecar-allocation.js +53 -0
  51. package/dist/schema/sidecar.d.ts +2 -2
  52. package/dist/schema/sidecar.js +3 -1
  53. package/dist/schema/signal-correlations.d.ts +177 -0
  54. package/dist/schema/signal-correlations.js +31 -0
  55. package/dist/schema/tenants.js +13 -3
  56. package/dist/schema/wallets.d.ts +3 -3
  57. package/dist/schema/wallets.js +4 -2
  58. package/dist/schema/{agents.d.ts → workflow-definitions.d.ts} +79 -113
  59. package/dist/schema/workflow-definitions.js +97 -0
  60. package/dist/schema/workflow-probe.d.ts +358 -0
  61. package/dist/schema/workflow-probe.js +51 -0
  62. package/dist/schema/workflow-run-dispatch.d.ts +345 -0
  63. package/dist/schema/workflow-run-dispatch.js +63 -0
  64. package/dist/schema/{agent-assets.d.ts → workflow-run-execution.d.ts} +44 -27
  65. package/dist/schema/workflow-run-execution.js +48 -0
  66. package/dist/schema/workflow-run-launch-spec.d.ts +200 -0
  67. package/dist/schema/workflow-run-launch-spec.js +26 -0
  68. package/dist/schema/{instances.d.ts → workflow-run.d.ts} +61 -66
  69. package/dist/schema/workflow-run.js +110 -0
  70. package/dist/sender-key-resolver.d.ts +91 -0
  71. package/dist/sender-key-resolver.js +184 -0
  72. package/dist/sidecar-allocation-store.d.ts +206 -0
  73. package/dist/sidecar-allocation-store.js +714 -0
  74. package/dist/signal-correlation-store.d.ts +36 -0
  75. package/dist/signal-correlation-store.js +63 -0
  76. package/dist/signer-identity.d.ts +12 -0
  77. package/dist/signer-identity.js +15 -0
  78. package/dist/tenant-hierarchy.d.ts +4 -1
  79. package/dist/tenant-hierarchy.js +37 -5
  80. package/dist/workflow-definition-store.d.ts +55 -0
  81. package/dist/workflow-definition-store.js +93 -0
  82. package/dist/workflow-probe-store.d.ts +47 -0
  83. package/dist/workflow-probe-store.js +124 -0
  84. package/dist/workflow-run-dispatch-store.d.ts +83 -0
  85. package/dist/workflow-run-dispatch-store.js +308 -0
  86. package/dist/workflow-run-launch-spec-store.d.ts +13 -0
  87. package/dist/workflow-run-launch-spec-store.js +30 -0
  88. package/dist/workflow-run-store.d.ts +54 -0
  89. package/dist/workflow-run-store.js +85 -0
  90. package/migrations/0037_credential_use_backfill.sql +39 -0
  91. package/migrations/0038_chilly_blacklash.sql +37 -0
  92. package/migrations/0039_quick_carnage.sql +2 -0
  93. package/migrations/0040_reshape_approval_origin.sql +24 -0
  94. package/migrations/0041_make_approval_timeout_at_nullable.sql +1 -0
  95. package/migrations/0042_youthful_mantis.sql +2 -0
  96. package/migrations/0043_signal_correlation_deployment_fk.sql +1 -0
  97. package/migrations/0044_model_offering_quirks.sql +1 -0
  98. package/migrations/0045_create_workflow_run.sql +13 -0
  99. package/migrations/0046_approval_signal_correlation_run_fk.sql +2 -0
  100. package/migrations/0047_create_workflow_definition.sql +30 -0
  101. package/migrations/0048_workflow_definition_origin_agent_id.sql +2 -0
  102. package/migrations/0049_curly_omega_flight.sql +2 -0
  103. package/migrations/0050_late_crystal.sql +10 -0
  104. package/migrations/0051_funny_madelyne_pryor.sql +1 -0
  105. package/migrations/0052_drop_inference_turn_instance_fk.sql +1 -0
  106. package/migrations/0053_session_mail_session_id_index.sql +1 -0
  107. package/migrations/0054_rekey_instance_grants_to_workflow_run.sql +10 -0
  108. package/migrations/0055_backfill_anchor_workflow_runs.sql +69 -0
  109. package/migrations/0056_repoint_deployment_fks_to_anchor_run.sql +32 -0
  110. package/migrations/0057_drop_workflow_deployment_projection.sql +22 -0
  111. package/migrations/0058_repoint_offering_fk_to_definition.sql +41 -0
  112. package/migrations/0059_repoint_transaction_fk_to_run.sql +10 -0
  113. package/migrations/0060_drop_session_mail_instance_fk.sql +6 -0
  114. package/migrations/0061_drop_session_asset_instance_fk.sql +7 -0
  115. package/migrations/0062_drop_agent_asset_table.sql +11 -0
  116. package/migrations/0063_repoint_agent_role_fk_to_definition.sql +39 -0
  117. package/migrations/0064_repoint_agent_session_fk_to_definition.sql +47 -0
  118. package/migrations/0065_add_workflow_definition_model_requirements.sql +37 -0
  119. package/migrations/0066_rekey_agent_definition_principals_to_workflow.sql +27 -0
  120. package/migrations/0067_add_workflow_definition_kind.sql +10 -0
  121. package/migrations/0068_drop_agent_tables_and_origin_agent_id.sql +31 -0
  122. package/migrations/0069_drop_workflow_definition_kind.sql +10 -0
  123. package/migrations/0070_flashy_proteus.sql +13 -0
  124. package/migrations/0072_add_workflow_definition_credential_bindings.sql +1 -0
  125. package/migrations/0073_grant_target_exactly_one_check.sql +13 -0
  126. package/migrations/0074_add_provider_api_base_url.sql +1 -0
  127. package/migrations/0075_workflow_run_launch_spec.sql +17 -0
  128. package/migrations/0076_sidecar_allocation.sql +40 -0
  129. package/migrations/0077_workflow_run_dispatch.sql +29 -0
  130. package/migrations/0078_workflow_run_dispatch_kind.sql +2 -0
  131. package/migrations/0079_rename_workflow_run_deployment_id_to_anchor_run_id.sql +4 -0
  132. package/migrations/0080_rename_correlation_approval_deployment_id_to_anchor_run_id.sql +9 -0
  133. package/migrations/0081_workflow_definition_content_hash_and_approved_wire_hash.sql +4 -0
  134. package/migrations/0082_blue_black_queen.sql +1 -0
  135. package/migrations/0083_replace_launch_spec_snapshot_with_frozen_bundle.sql +3 -0
  136. package/migrations/0084_delete_orphaned_credential_grants.sql +22 -0
  137. package/migrations/0085_add_approval_run_idx.sql +1 -0
  138. package/migrations/0086_cool_human_cannonball.sql +1 -0
  139. package/migrations/0087_drop_sidecar_placement.sql +3 -0
  140. package/migrations/0088_thick_sprite.sql +32 -0
  141. package/migrations/0089_tense_selene.sql +13 -0
  142. package/migrations/0090_tenant_domain_lower_unique.sql +2 -0
  143. package/migrations/0091_workflow_run_dispatch_sender_address.sql +21 -0
  144. package/migrations/0092_sidecar_destroy_failed.sql +4 -0
  145. package/migrations/0093_sidecar_initialization.sql +2 -0
  146. package/migrations/meta/0037_snapshot.json +3237 -0
  147. package/migrations/meta/0038_snapshot.json +3470 -0
  148. package/migrations/meta/0039_snapshot.json +3470 -0
  149. package/migrations/meta/0040_snapshot.json +3489 -0
  150. package/migrations/meta/0041_snapshot.json +3489 -0
  151. package/migrations/meta/0042_snapshot.json +3489 -0
  152. package/migrations/meta/0043_snapshot.json +3498 -0
  153. package/migrations/meta/0044_snapshot.json +3504 -0
  154. package/migrations/meta/0045_snapshot.json +3589 -0
  155. package/migrations/meta/0046_snapshot.json +3607 -0
  156. package/migrations/meta/0047_snapshot.json +3832 -0
  157. package/migrations/meta/0048_snapshot.json +3853 -0
  158. package/migrations/meta/0049_snapshot.json +3854 -0
  159. package/migrations/meta/0050_snapshot.json +3924 -0
  160. package/migrations/meta/0051_snapshot.json +3940 -0
  161. package/migrations/meta/0052_snapshot.json +3931 -0
  162. package/migrations/meta/0053_snapshot.json +3952 -0
  163. package/migrations/meta/0054_snapshot.json +3952 -0
  164. package/migrations/meta/0055_snapshot.json +3952 -0
  165. package/migrations/meta/0056_snapshot.json +3952 -0
  166. package/migrations/meta/0057_snapshot.json +3839 -0
  167. package/migrations/meta/0058_snapshot.json +3839 -0
  168. package/migrations/meta/0059_snapshot.json +3839 -0
  169. package/migrations/meta/0060_snapshot.json +3830 -0
  170. package/migrations/meta/0061_snapshot.json +3821 -0
  171. package/migrations/meta/0062_snapshot.json +3723 -0
  172. package/migrations/meta/0063_snapshot.json +3723 -0
  173. package/migrations/meta/0064_snapshot.json +3723 -0
  174. package/migrations/meta/0065_snapshot.json +3729 -0
  175. package/migrations/meta/0066_snapshot.json +3729 -0
  176. package/migrations/meta/0067_snapshot.json +3736 -0
  177. package/migrations/meta/0068_snapshot.json +3347 -0
  178. package/migrations/meta/0069_snapshot.json +3340 -0
  179. package/migrations/meta/0070_snapshot.json +3444 -0
  180. package/migrations/meta/0072_snapshot.json +3450 -0
  181. package/migrations/meta/0073_snapshot.json +3455 -0
  182. package/migrations/meta/0074_snapshot.json +3461 -0
  183. package/migrations/meta/0075_snapshot.json +3567 -0
  184. package/migrations/meta/0076_snapshot.json +3851 -0
  185. package/migrations/meta/0077_snapshot.json +4067 -0
  186. package/migrations/meta/0078_snapshot.json +4078 -0
  187. package/migrations/meta/0079_snapshot.json +4078 -0
  188. package/migrations/meta/0080_snapshot.json +4078 -0
  189. package/migrations/meta/0081_snapshot.json +4096 -0
  190. package/migrations/meta/0082_snapshot.json +4102 -0
  191. package/migrations/meta/0083_snapshot.json +4096 -0
  192. package/migrations/meta/0084_snapshot.json +4096 -0
  193. package/migrations/meta/0085_snapshot.json +4111 -0
  194. package/migrations/meta/0086_snapshot.json +4117 -0
  195. package/migrations/meta/0087_snapshot.json +4100 -0
  196. package/migrations/meta/0088_snapshot.json +4286 -0
  197. package/migrations/meta/0089_snapshot.json +4376 -0
  198. package/migrations/meta/0090_snapshot.json +4387 -0
  199. package/migrations/meta/0091_snapshot.json +4397 -0
  200. package/migrations/meta/0092_snapshot.json +4397 -0
  201. package/migrations/meta/0093_snapshot.json +4407 -0
  202. package/migrations/meta/_journal.json +392 -0
  203. package/package.json +7 -3
  204. package/dist/schema/agent-assets.js +0 -15
  205. package/dist/schema/agents.js +0 -57
  206. package/dist/schema/instances.js +0 -39
  207. package/dist/schema/workflow-deployments.js +0 -37
@@ -1,8 +1,9 @@
1
1
  import { and, eq } from "drizzle-orm";
2
- import { InvokerModelPreferences, ModelRequirements, } from "@intx/types";
2
+ import { InvokerModelPreferences, ModelRequirements, credentialAad, } from "@intx/types";
3
3
  import { listVisibleOfferings, } from "./catalog-resolution.js";
4
4
  import { resolveCredentialById } from "./credential-resolution.js";
5
- import { agent } from "./schema/agents.js";
5
+ import { parseModelOfferingRow } from "./parse-row.js";
6
+ import { workflowDefinition } from "./schema/workflow-definitions.js";
6
7
  function byPriority(a, b) {
7
8
  if (a.offering.priority !== b.offering.priority) {
8
9
  return a.offering.priority - b.offering.priority;
@@ -41,7 +42,7 @@ function applyPreference(candidates, preference) {
41
42
  // the rest as fallback after the named providers.
42
43
  return preference.mode === "pin" ? named : [...named, ...rest];
43
44
  }
44
- async function buildSource(db, tenantId, resolved) {
45
+ async function buildSource(db, tenantId, resolved, credentialCipher) {
45
46
  const { provider, model, offering } = resolved;
46
47
  if (provider.credentialId === null) {
47
48
  // No credential reference. A wallet-backed provider is a valid catalog
@@ -60,7 +61,7 @@ async function buildSource(db, tenantId, resolved) {
60
61
  }
61
62
  // Resolve the secret through the tenant-scoped credential resolver so a
62
63
  // provider row referencing a credential outside the tenant's ancestor
63
- // chain cannot leak that secret (INTR-203: the chain is the authority).
64
+ // chain cannot leak that secret (the chain is the authority).
64
65
  const credential = await resolveCredentialById(db, tenantId, provider.credentialId);
65
66
  if (credential === null) {
66
67
  return {
@@ -68,18 +69,84 @@ async function buildSource(db, tenantId, resolved) {
68
69
  skip: { reason: "credential_unresolved", provider: provider.name },
69
70
  };
70
71
  }
72
+ // Authority to use a credential through a catalog provider is ownership
73
+ // within the tenant hierarchy -- the same rule the tool-binding path resolves
74
+ // (`source: "tenant"`). `resolveCredentialById` above already proved the
75
+ // credential is reachable in this tenant's ancestor chain, so a tenant-owned
76
+ // credential (`principalId IS NULL`) is authorized here by ownership alone,
77
+ // with no personal grant and no role grant consulted. This runs live on every
78
+ // launch and rotation, so a catalog edit that adds an eligible credential is
79
+ // picked up without re-materializing any grant. A principal-owned credential
80
+ // is not usable through a shared catalog provider; fail closed.
81
+ if (credential.principalId !== null) {
82
+ return {
83
+ ok: false,
84
+ skip: { reason: "credential_unauthorized", provider: provider.name },
85
+ };
86
+ }
87
+ // Validate the row once at this DB-to-runtime boundary. This narrows the
88
+ // jsonb `quirks` from `unknown` to a `Record | null` and checks
89
+ // `capabilities` against the curated enum. `quirks` is spread in only when
90
+ // non-null so a source with no accommodations omits the key entirely
91
+ // (InferenceSource.quirks is present-or-absent, never null). The capability
92
+ // filter in resolveModelSources still reads the raw row's `capabilities`
93
+ // for its `.includes` check; that read-only comparison cannot be corrupted
94
+ // into a wrong routing decision, so it is left as-is.
95
+ // Decrypt the stored secret at the one point it is used. Strict: a
96
+ // non-ciphertext value (a row not yet re-keyed, or a write that failed to
97
+ // encrypt) throws rather than delivering a bad key -- fail closed. The secret
98
+ // rides the credential-material cell (keyed by `credentialId`), NOT the source
99
+ // config -- the source only references the credential by id, so no secret is
100
+ // inline in the pinned/persisted source.
101
+ const secret = await credentialCipher.decrypt(credential.secret, credentialAad(credential.id, "secret"));
102
+ const parsed = parseModelOfferingRow(offering);
71
103
  return {
72
104
  ok: true,
73
105
  source: {
74
106
  id: offering.id,
75
107
  provider: provider.plugin,
76
108
  baseURL: provider.baseURL,
77
- apiKey: credential.secret,
109
+ credentialId: provider.credentialId,
78
110
  model: model.canonicalName,
79
- capabilities: offering.capabilities,
111
+ capabilities: parsed.capabilities,
112
+ ...(parsed.quirks !== null ? { quirks: parsed.quirks } : {}),
113
+ },
114
+ material: {
115
+ credentialId: provider.credentialId,
116
+ providerKey: provider.plugin,
117
+ origin: provider.baseURL,
118
+ secret,
80
119
  },
81
120
  };
82
121
  }
122
+ /**
123
+ * Rebuild an exact, ordered source chain from durable catalog offering ids.
124
+ * This is the recovery counterpart to launch-time source selection: it keeps
125
+ * secrets out of persisted launch specs and rechecks tenant visibility and
126
+ * credential ownership on every launch.
127
+ */
128
+ export async function resolveSourcesByOfferingIds(db, tenantId, offeringIds, credentialCipher) {
129
+ const visible = await listVisibleOfferings(db, tenantId);
130
+ const byId = new Map(visible.map((entry) => [entry.offering.id, entry]));
131
+ const sources = [];
132
+ for (const offeringId of offeringIds) {
133
+ const offering = byId.get(offeringId);
134
+ if (offering === undefined) {
135
+ return { ok: false, reason: "offering_unavailable", offeringId };
136
+ }
137
+ const built = await buildSource(db, tenantId, offering, credentialCipher);
138
+ if (!built.ok) {
139
+ return {
140
+ ok: false,
141
+ reason: "offering_unavailable",
142
+ offeringId,
143
+ skip: built.skip,
144
+ };
145
+ }
146
+ sources.push(built.source);
147
+ }
148
+ return { ok: true, sources };
149
+ }
83
150
  /**
84
151
  * Resolves an agent's model requirements against the tenant catalog into an
85
152
  * ordered `InferenceSource[]` for the harness.
@@ -91,13 +158,27 @@ async function buildSource(db, tenantId, resolved) {
91
158
  * offering is resolved to a credential-backed source; offerings that cannot
92
159
  * produce one are skipped. A required model that yields no source makes the
93
160
  * agent unlaunchable.
161
+ *
162
+ * A credential-backed source is emitted only when the launching tenant owns
163
+ * the referenced credential within its hierarchy (ownership is the authority);
164
+ * otherwise the offering is skipped (`credential_unauthorized`) and its secret
165
+ * is withheld.
94
166
  */
95
- export async function resolveModelSources(db, tenantId, requirements, opts) {
167
+ export async function resolveModelSources(db, tenantId, requirements,
168
+ // Decrypts each resolved credential secret at its point of use in
169
+ // `buildSource`. Required: the edge (the launch route / rotation push /
170
+ // allocation service) owns the cipher and always supplies a real one; the
171
+ // noop fallback for a keyless composition is resolved once at that edge
172
+ // (`resolveCredentialCipher` in the hub app), never defaulted here.
173
+ credentialCipher, opts) {
96
174
  if (requirements.length === 0) {
97
175
  return { ok: false, reason: "no_requirements" };
98
176
  }
99
177
  const visible = await listVisibleOfferings(db, tenantId);
100
178
  const sources = [];
179
+ // Dedupe material by credentialId across every requirement's chain: one
180
+ // credential backing several offerings is delivered once.
181
+ const materials = new Map();
101
182
  for (const requirement of requirements) {
102
183
  let candidates = visible
103
184
  .filter((o) => o.model.canonicalName === requirement.model)
@@ -110,10 +191,12 @@ export async function resolveModelSources(db, tenantId, requirements, opts) {
110
191
  candidates = applyPreference(candidates, opts?.invokerPreferences?.[requirement.model]);
111
192
  const skips = [];
112
193
  const modelSources = [];
194
+ const modelMaterials = [];
113
195
  for (const candidate of candidates) {
114
- const built = await buildSource(db, tenantId, candidate);
196
+ const built = await buildSource(db, tenantId, candidate, credentialCipher);
115
197
  if (built.ok) {
116
198
  modelSources.push(built.source);
199
+ modelMaterials.push(built.material);
117
200
  }
118
201
  else {
119
202
  skips.push(built.skip);
@@ -128,29 +211,101 @@ export async function resolveModelSources(db, tenantId, requirements, opts) {
128
211
  };
129
212
  }
130
213
  sources.push(...modelSources);
214
+ for (const material of modelMaterials) {
215
+ if (!materials.has(material.credentialId)) {
216
+ materials.set(material.credentialId, material);
217
+ }
218
+ }
131
219
  }
132
- return { ok: true, sources };
220
+ return { ok: true, sources, materials: [...materials.values()] };
221
+ }
222
+ /**
223
+ * Resolve an agent's model requirements to the credential-free
224
+ * `{ provider, model }` inference preferences a folded workflow definition
225
+ * carries on its step agent. Reuses `resolveModelSources` -- the same catalog
226
+ * + creator-grant resolution the instance launch path runs -- and projects its
227
+ * ordered sources to their `{ provider (plugin), model }` identity, dropping
228
+ * the credentials: a definition's inference preference is hash-only, and the
229
+ * credential-bearing sources are supplied per deploy on the workflow path.
230
+ *
231
+ * A requirement set that resolves to no source is a hard failure. A folded
232
+ * agent whose model is unresolvable is undeployable, so this raises rather
233
+ * than synthesizing an empty preference list that would silently strip the
234
+ * agent's inference. It also raises when the resolved sources are not
235
+ * injective on `(provider, model)` -- the projection would otherwise freeze
236
+ * an ambiguous preference list whose lost `offering.id` distinctions cannot be
237
+ * recovered after the agent's columns are dropped.
238
+ */
239
+ export async function resolveInferencePreferences(db, tenantId, requirements,
240
+ // Required even though the result discards the credential: this reuses the
241
+ // full `resolveModelSources` path, which decrypts each secret at its point of
242
+ // use in `buildSource` before the `{provider, model}` projection drops it. So
243
+ // the caller must still supply the edge's real cipher; a noop here would throw
244
+ // the moment a tenant's secret is stored encrypted.
245
+ credentialCipher) {
246
+ const resolution = await resolveModelSources(db, tenantId, requirements, credentialCipher);
247
+ if (!resolution.ok) {
248
+ throw new Error(`cannot resolve inference preferences for the folded definition: ${resolution.reason}`);
249
+ }
250
+ // The projection to `{ provider (plugin), model }` drops the `offering.id`
251
+ // that keys each resolved source. Two distinct offerings can share a
252
+ // provider plugin and canonical model -- the catalog permits two
253
+ // `model_provider` rows on the same plugin (uniqueness is on the provider
254
+ // name), each offering the same model -- so the projection is only
255
+ // well-defined when the resolved sources are injective on `(provider,
256
+ // model)`. If they collapse, the folded definition's inference preferences
257
+ // cannot distinguish which offering each entry meant, and once the agent's
258
+ // columns are dropped that distinction is unrecoverable. Refuse to
259
+ // synthesize an ambiguous preference list: fail loud so the collapse
260
+ // surfaces in the fold/materialize manifest rather than freezing silently.
261
+ const preferences = resolution.sources.map((source) => ({
262
+ provider: source.provider,
263
+ model: source.model,
264
+ }));
265
+ const seen = new Set();
266
+ for (const preference of preferences) {
267
+ // Join on a NUL: it cannot appear in a provider plugin or canonical model
268
+ // name, so the composite key is collision-free. Written as an explicit
269
+ // unicode escape rather than an invisible raw control byte in the source.
270
+ const key = `${preference.provider}\u0000${preference.model}`;
271
+ if (seen.has(key)) {
272
+ throw new Error(`cannot resolve inference preferences for the folded definition: ` +
273
+ `the model resolution is not injective on (provider, model) -- ` +
274
+ `two offerings collapse to (${preference.provider}, ${preference.model}); ` +
275
+ `a folded agent requires a distinguishable inference preference per source`);
276
+ }
277
+ seen.add(key);
278
+ }
279
+ return preferences;
133
280
  }
134
281
  /**
135
282
  * Resolves the ordered sources for a running instance from persisted state:
136
- * the agent definition's model requirements and the invoker's launch-time
137
- * preferences stored on the instance row. Launch, credential-rotation push,
138
- * and sidecar reconnect all resolve through this, so a running instance's
283
+ * the definition's model requirements and the invoker's launch-time
284
+ * preferences stored on the instance row. The credential-rotation and
285
+ * catalog-edit source push resolves through this, so a running instance's
139
286
  * source list is a pure function of persisted state — re-resolution
140
287
  * reproduces the launch ordering, including the invoker's reorder/restrict.
141
288
  */
142
- export async function resolveInstanceModelSources(db, tenantId, instance) {
143
- // Scope the agent lookup to the resolving tenant, matching the launch
144
- // route. A cross-tenant agentId resolves to nothing rather than
145
- // contributing another tenant's model names or preferences.
146
- const agentRow = await db.query.agent.findFirst({
147
- where: and(eq(agent.id, instance.agentId), eq(agent.tenantId, tenantId)),
289
+ export async function resolveInstanceModelSources(db, tenantId, instance, credentialCipher) {
290
+ // Resolve from the run's own definition by primary key. This is the SAME row
291
+ // the launch resolves its requirements from, so a rotation or catalog edit
292
+ // reproduces the launch's model resolution rather than drifting. Scope to the
293
+ // resolving tenant, matching the launch route: a definition in another tenant
294
+ // resolves to nothing rather than contributing another tenant's models. A
295
+ // definition with no creator principal cannot authorize a credential-backed
296
+ // source, so it fails closed like a missing definition.
297
+ const definitionRow = await db.query.workflowDefinition.findFirst({
298
+ where: and(eq(workflowDefinition.id, instance.definitionId), eq(workflowDefinition.tenantId, tenantId)),
148
299
  });
149
- if (agentRow === undefined) {
300
+ // A credential-backed source is authorized by tenant ownership of the
301
+ // credential (walked in `buildSource`), not by the definition's creator, so a
302
+ // definition with no creator can still launch a tenant-owned inference
303
+ // source. Only a missing definition (or one in another tenant) fails closed.
304
+ if (definitionRow === undefined) {
150
305
  return { ok: false, reason: "no_requirements" };
151
306
  }
152
- const requirements = agentRow.modelRequirements !== null
153
- ? ModelRequirements.assert(agentRow.modelRequirements)
307
+ const requirements = definitionRow.modelRequirements !== null
308
+ ? ModelRequirements.assert(definitionRow.modelRequirements)
154
309
  : [];
155
310
  const preferences = instance.modelPreferences !== null
156
311
  ? InvokerModelPreferences.assert(instance.modelPreferences)
@@ -159,7 +314,7 @@ export async function resolveInstanceModelSources(db, tenantId, instance) {
159
314
  for (const preference of preferences) {
160
315
  invokerPreferences[preference.model] = preference.providers;
161
316
  }
162
- return resolveModelSources(db, tenantId, requirements, {
317
+ return resolveModelSources(db, tenantId, requirements, credentialCipher, {
163
318
  invokerPreferences,
164
319
  });
165
320
  }
@@ -1,52 +1,48 @@
1
- import type { agent, agentVersion, credential, gitToken, grant, modelOffering, modelProvider, oauthClient, offering, provider, tenant, transaction, turnPart, wallet } from "./schema/index.js";
1
+ import type { approval, credential, gitToken, grant, modelOffering, modelProvider, oauthClient, offering, principal, principalKey, provider, signalCorrelation, tenant, transaction, turnPart, wallet, workflowDefinition, workflowDefinitionVersion, workflowRun, workflowRunDispatch, workflowRunLaunchSpec } from "./schema/index.js";
2
2
  export declare const GitTokenKindValidator: import("arktype/internal/variants/string.ts").StringType<"pat" | "svc", {}>;
3
- export declare function parseAgentRow(row: typeof agent.$inferSelect): {
4
- contextConfig: Record<string, unknown> | null;
5
- initialState: Record<string, unknown> | null;
6
- modelConfig: Record<string, unknown> | null;
7
- capabilities: Record<string, unknown> | null;
8
- credentialRequirements: {
9
- providerName: string;
10
- source: "tenant" | "creator" | "invoker";
11
- scopes?: string[];
12
- name?: string;
3
+ export declare function parseWorkflowDefinitionRow(row: typeof workflowDefinition.$inferSelect): {
4
+ status: "deployed" | "stopped";
5
+ grantRequirements: {
6
+ resource: string;
7
+ action: string;
8
+ source: "creator" | "invoker";
9
+ effect?: "allow" | "deny" | "ask";
10
+ conditions?: Record<string, unknown> | null;
13
11
  }[] | null;
14
12
  modelRequirements: {
15
13
  model: string;
16
- capabilities?: ("vision" | "audio-input" | "tool-use" | "extended-thinking" | "structured-output" | "long-context" | "prompt-caching")[];
14
+ capabilities?: ("plain-text" | "plain-text-streaming" | "function-calling" | "function-calling-multi-turn" | "function-calling-multi-turn-streaming" | "function-calling-with-thinking" | "function-calling-with-thinking-streaming" | "vision-input" | "vision-input-streaming" | "audio-input" | "audio-input-streaming" | "video-input" | "video-input-streaming" | "document-input" | "document-input-streaming" | "image-output" | "image-output-streaming" | "code-execution" | "code-execution-streaming" | "reasoning-content" | "reasoning-content-streaming" | "grounding" | "grounding-streaming" | "files-api-reference" | "files-api-reference-streaming" | "redacted-thinking" | "redacted-thinking-streaming" | "structured-output" | "structured-output-streaming" | "long-context" | "prompt-caching")[];
17
15
  providers?: {
18
16
  mode: "prefer" | "pin";
19
17
  order: string[];
20
18
  };
21
19
  }[] | null;
22
- grantRequirements: {
23
- resource: string;
24
- action: string;
25
- source: "creator" | "invoker";
26
- effect?: "allow" | "deny" | "ask";
27
- conditions?: Record<string, unknown> | null;
20
+ credentialBindings: {
21
+ package: string;
22
+ handle: string;
23
+ provider: string;
24
+ locator: "tenant";
25
+ name?: string;
28
26
  }[] | null;
29
- toolPackages: {
30
- name: string;
31
- version: string;
32
- }[];
33
27
  id: string;
34
28
  name: string;
35
29
  createdAt: Date;
36
30
  updatedAt: Date;
37
31
  tenantId: string;
38
- status: "deployed" | "stopped";
39
- creatorPrincipalId: string;
32
+ creatorPrincipalId: string | null;
33
+ assetId: string | null;
34
+ wireHash: string | null;
40
35
  description: string | null;
41
- systemPrompt: string | null;
42
36
  currentVersion: string;
43
37
  };
44
- export declare function parseAgentVersionRow(row: typeof agentVersion.$inferSelect): {
38
+ export declare function parseWorkflowDefinitionVersionRow(row: typeof workflowDefinitionVersion.$inferSelect): {
45
39
  status: "active" | "inactive" | "failed";
46
40
  id: string;
47
41
  createdAt: Date;
48
- agentId: string;
42
+ definitionId: string;
49
43
  version: string;
44
+ approvedWireHash: string | null;
45
+ grantSnapshot: unknown;
50
46
  };
51
47
  export declare function parseGrantRow(row: typeof grant.$inferSelect): {
52
48
  effect: "allow" | "deny" | "ask";
@@ -57,11 +53,216 @@ export declare function parseGrantRow(row: typeof grant.$inferSelect): {
57
53
  updatedAt: Date;
58
54
  expiresAt: Date | null;
59
55
  tenantId: string;
60
- roleId: string | null;
61
56
  principalId: string | null;
57
+ roleId: string | null;
62
58
  resource: string;
63
59
  action: string;
64
60
  };
61
+ export declare function parseApprovalRow(row: typeof approval.$inferSelect): {
62
+ scope: "always" | "once" | null;
63
+ status: "pending" | "approved" | "rejected" | "timeout" | "expired";
64
+ toolDefinition: Record<string, unknown>;
65
+ toolArguments: Record<string, unknown>;
66
+ id: string;
67
+ createdAt: Date;
68
+ updatedAt: Date;
69
+ tenantId: string;
70
+ anchorRunId: string;
71
+ runId: string;
72
+ agentAddress: string;
73
+ correlationId: string;
74
+ timeoutAt: Date | null;
75
+ resolvedAt: Date | null;
76
+ };
77
+ export declare function parsePrincipalRow(row: typeof principal.$inferSelect): {
78
+ kind: "user" | "agent" | "workflow";
79
+ status: "active" | "suspended" | "invited" | "deactivated";
80
+ id: string;
81
+ createdAt: Date;
82
+ updatedAt: Date;
83
+ tenantId: string;
84
+ refId: string;
85
+ };
86
+ export declare function parsePrincipalKeyRow(row: typeof principalKey.$inferSelect): {
87
+ status: "active" | "retired";
88
+ id: string;
89
+ createdAt: Date;
90
+ updatedAt: Date;
91
+ principalId: string;
92
+ publicKey: string;
93
+ privateKey: string;
94
+ };
95
+ export declare function parseSignalCorrelationRow(row: typeof signalCorrelation.$inferSelect): {
96
+ kind: "approval";
97
+ createdAt: Date;
98
+ tenantId: string;
99
+ anchorRunId: string;
100
+ runId: string;
101
+ agentAddress: string;
102
+ correlationId: string;
103
+ resolvedAt: Date | null;
104
+ signalName: string;
105
+ signalId: string | null;
106
+ };
107
+ export declare function parseWorkflowRunRow(row: typeof workflowRun.$inferSelect): {
108
+ status: "deployed" | "failed" | "running" | "completed" | "cancelled";
109
+ modelPreferences: {
110
+ model: string;
111
+ providers: {
112
+ mode: "prefer" | "pin";
113
+ order: string[];
114
+ };
115
+ }[] | null;
116
+ id: string;
117
+ createdAt: Date;
118
+ tenantId: string;
119
+ principalId: string | null;
120
+ publicKey: string | null;
121
+ definitionId: string;
122
+ anchorRunId: string | null;
123
+ address: string | null;
124
+ sidecarId: string | null;
125
+ kernelId: string | null;
126
+ credentialRefs: import("./schema/index.js").WorkflowRunCredentialRefs | null;
127
+ endedAt: Date | null;
128
+ };
129
+ export declare function parseWorkflowRunLaunchSpecRow(row: typeof workflowRunLaunchSpec.$inferSelect): {
130
+ frozenApprovalBundle: {
131
+ source: {
132
+ kind: "registry";
133
+ registry: string;
134
+ } | {
135
+ kind: "asset";
136
+ assetId: string;
137
+ package: {
138
+ format: "tarball";
139
+ } | {
140
+ format: "source";
141
+ commitSha: string;
142
+ packageName?: string;
143
+ };
144
+ };
145
+ entry: string;
146
+ projection: {
147
+ id: string;
148
+ triggers: unknown[];
149
+ stepOrder: string[];
150
+ steps: {
151
+ [x: string]: unknown;
152
+ };
153
+ state?: Record<string, unknown>;
154
+ credentialBindings?: {
155
+ package: string;
156
+ handle: string;
157
+ provider: string;
158
+ locator: "tenant";
159
+ name?: string;
160
+ }[];
161
+ sidecarPlacement?: {
162
+ capabilities?: {
163
+ capability: string;
164
+ effect: "require" | "block";
165
+ }[];
166
+ };
167
+ inboundMailPolicy?: {
168
+ untrustedFrom?: "reject" | "admit";
169
+ invalid?: "reject" | "admit";
170
+ missing?: "reject" | "admit";
171
+ unknown?: "reject" | "admit";
172
+ };
173
+ };
174
+ closure: {
175
+ schemaVersion: "1";
176
+ topLevel: {
177
+ name: string;
178
+ version: string;
179
+ credentials?: {
180
+ handle: string;
181
+ scopes?: string[];
182
+ }[];
183
+ }[];
184
+ entries: {
185
+ name: string;
186
+ version: string;
187
+ source: {
188
+ kind: "registry";
189
+ registry: string;
190
+ integrity: string;
191
+ } | {
192
+ kind: "asset";
193
+ assetId: string;
194
+ package: {
195
+ format: "tarball";
196
+ path: string;
197
+ integrity: string;
198
+ } | {
199
+ format: "source";
200
+ commitSha: string;
201
+ packageDir: string;
202
+ treeOid: string;
203
+ };
204
+ };
205
+ os?: string[];
206
+ cpu?: string[];
207
+ tarballUrl?: string;
208
+ }[];
209
+ };
210
+ approvedWireHash: string;
211
+ approvedGrants: (string | {
212
+ resource: string;
213
+ action: string;
214
+ source: "creator" | "invoker";
215
+ effect?: "allow" | "deny" | "ask";
216
+ conditions?: Record<string, unknown> | null;
217
+ })[];
218
+ };
219
+ sourceOfferingIds: string[];
220
+ deployContent: Record<string, unknown>;
221
+ toolPackagePins: {
222
+ name: string;
223
+ version: string;
224
+ }[] | null;
225
+ createdAt: Date;
226
+ anchorRunId: string;
227
+ sessionId: string;
228
+ schemaVersion: number;
229
+ deploymentDomain: string;
230
+ sourceAuthorityPrincipalId: string;
231
+ defaultSourceOfferingId: string;
232
+ };
233
+ export declare function parseWorkflowRunDispatchRow(row: typeof workflowRunDispatch.$inferSelect): {
234
+ kind: "signal" | "mail";
235
+ status: "failed" | "pending" | "acknowledged" | "settled";
236
+ stepGrants: {
237
+ id: string;
238
+ resource: string;
239
+ action: string;
240
+ effect: "allow" | "deny" | "ask";
241
+ origin: "role" | "system" | "creator" | "invoker";
242
+ conditions: {
243
+ [x: string]: unknown;
244
+ } | null;
245
+ expiresAt: Date | null;
246
+ roleId: string | null;
247
+ principalId: string | null;
248
+ }[];
249
+ id: string;
250
+ createdAt: Date;
251
+ updatedAt: Date;
252
+ anchorRunId: string;
253
+ messageId: string;
254
+ nextAttemptAt: Date | null;
255
+ failureCode: string | null;
256
+ failureMessage: string | null;
257
+ senderAddress: string | null;
258
+ rawMessage: Uint8Array<ArrayBufferLike>;
259
+ acknowledgedGeneration: number | null;
260
+ attemptCount: number;
261
+ deliveryLeaseId: string | null;
262
+ deliveryLeaseExpiresAt: Date | null;
263
+ acknowledgedAt: Date | null;
264
+ settledAt: Date | null;
265
+ };
65
266
  export declare function parseOfferingRow(row: typeof offering.$inferSelect): {
66
267
  pricing: Record<string, unknown> | null;
67
268
  schema: Record<string, unknown> | null;
@@ -75,7 +276,7 @@ export declare function parseOfferingRow(row: typeof offering.$inferSelect): {
75
276
  };
76
277
  export declare function parseCredentialRow(row: typeof credential.$inferSelect): {
77
278
  type: "api_key" | "oauth_token" | "certificate" | "other";
78
- status: "active" | "expired" | "revoked" | "error";
279
+ status: "active" | "error" | "expired" | "revoked";
79
280
  metadata: Record<string, unknown> | null;
80
281
  id: string;
81
282
  name: string;
@@ -84,8 +285,8 @@ export declare function parseCredentialRow(row: typeof credential.$inferSelect):
84
285
  expiresAt: Date | null;
85
286
  providerId: string;
86
287
  tenantId: string;
87
- description: string | null;
88
288
  principalId: string | null;
289
+ description: string | null;
89
290
  scopes: string[] | null;
90
291
  oauthClientId: string | null;
91
292
  secret: string;
@@ -99,6 +300,7 @@ export declare function parseProviderRow(row: typeof provider.$inferSelect): {
99
300
  updatedAt: Date;
100
301
  tenantId: string;
101
302
  plugin: string;
303
+ apiBaseUrl: string | null;
102
304
  authorizationUrl: string | null;
103
305
  tokenUrl: string | null;
104
306
  userInfoUrl: string | null;
@@ -117,7 +319,8 @@ export declare function parseModelProviderRow(row: typeof modelProvider.$inferSe
117
319
  credentialId: string | null;
118
320
  };
119
321
  export declare function parseModelOfferingRow(row: typeof modelOffering.$inferSelect): {
120
- capabilities: ("vision" | "audio-input" | "tool-use" | "extended-thinking" | "structured-output" | "long-context" | "prompt-caching")[];
322
+ capabilities: ("plain-text" | "plain-text-streaming" | "function-calling" | "function-calling-multi-turn" | "function-calling-multi-turn-streaming" | "function-calling-with-thinking" | "function-calling-with-thinking-streaming" | "vision-input" | "vision-input-streaming" | "audio-input" | "audio-input-streaming" | "video-input" | "video-input-streaming" | "document-input" | "document-input-streaming" | "image-output" | "image-output-streaming" | "code-execution" | "code-execution-streaming" | "reasoning-content" | "reasoning-content-streaming" | "grounding" | "grounding-streaming" | "files-api-reference" | "files-api-reference-streaming" | "redacted-thinking" | "redacted-thinking-streaming" | "structured-output" | "structured-output-streaming" | "long-context" | "prompt-caching")[];
323
+ quirks: Record<string, unknown> | null;
121
324
  id: string;
122
325
  createdAt: Date;
123
326
  updatedAt: Date;
@@ -129,7 +332,15 @@ export declare function parseModelOfferingRow(row: typeof modelOffering.$inferSe
129
332
  deploymentTags: string[];
130
333
  };
131
334
  export declare function parseTenantRow(row: typeof tenant.$inferSelect): {
132
- config: Record<string, unknown> | null;
335
+ config: {
336
+ [x: string]: unknown;
337
+ sidecarPlacement?: {
338
+ capabilities?: {
339
+ capability: string;
340
+ effect: "require" | "block";
341
+ }[];
342
+ };
343
+ } | null;
133
344
  domain: string;
134
345
  id: string;
135
346
  name: string;
@@ -151,10 +362,10 @@ export declare function parseWalletRow(row: typeof wallet.$inferSelect): {
151
362
  };
152
363
  export declare function parseTransactionRow(row: typeof transaction.$inferSelect): {
153
364
  direction: "inbound" | "outbound";
154
- status: "failed" | "pending" | "completed";
365
+ status: "failed" | "completed" | "pending";
155
366
  id: string;
156
367
  createdAt: Date;
157
- agentId: string | null;
368
+ runId: string | null;
158
369
  currency: string;
159
370
  walletId: string;
160
371
  amount: string;