@intx/db 0.1.2 → 0.3.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 (241) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +23 -4
  3. package/dist/approval-store.d.ts +52 -0
  4. package/dist/approval-store.js +79 -0
  5. package/dist/asset-resolution.d.ts +52 -0
  6. package/dist/asset-resolution.js +66 -0
  7. package/dist/catalog-resolution.d.ts +76 -0
  8. package/dist/catalog-resolution.js +151 -0
  9. package/dist/client.d.ts +19 -0
  10. package/dist/client.js +18 -0
  11. package/dist/config.d.ts +11 -0
  12. package/dist/config.js +16 -0
  13. package/dist/connection.d.ts +3 -0
  14. package/dist/connection.js +26 -0
  15. package/dist/credential-resolution.d.ts +222 -0
  16. package/dist/credential-resolution.js +255 -0
  17. package/dist/grant-store.d.ts +3 -0
  18. package/dist/grant-store.js +59 -0
  19. package/dist/index.d.ts +22 -0
  20. package/dist/index.js +20 -0
  21. package/dist/migrate.d.ts +36 -0
  22. package/dist/migrate.js +135 -0
  23. package/dist/model-source-resolution.d.ts +115 -0
  24. package/dist/model-source-resolution.js +291 -0
  25. package/dist/parse-row.d.ts +373 -0
  26. package/dist/parse-row.js +232 -0
  27. package/dist/pg-error.d.ts +3 -0
  28. package/dist/pg-error.js +19 -0
  29. package/dist/pricing.d.ts +15 -0
  30. package/dist/pricing.js +24 -0
  31. package/dist/rekey-credential-secrets.d.ts +13 -0
  32. package/dist/rekey-credential-secrets.js +61 -0
  33. package/dist/schema/approvals.d.ts +245 -0
  34. package/dist/schema/approvals.js +52 -0
  35. package/dist/schema/assets.d.ts +143 -0
  36. package/{src/schema/assets.ts → dist/schema/assets.js} +7 -16
  37. package/dist/schema/auth.d.ts +606 -0
  38. package/dist/schema/auth.js +47 -0
  39. package/dist/schema/catalog.d.ts +811 -0
  40. package/dist/schema/catalog.js +108 -0
  41. package/dist/schema/column-types.d.ts +26 -0
  42. package/dist/schema/column-types.js +15 -0
  43. package/dist/schema/credentials.d.ts +305 -0
  44. package/{src/schema/credentials.ts → dist/schema/credentials.js} +16 -22
  45. package/dist/schema/git-tokens.d.ts +256 -0
  46. package/{src/schema/git-tokens.ts → dist/schema/git-tokens.js} +16 -39
  47. package/dist/schema/grants.d.ts +211 -0
  48. package/dist/schema/grants.js +30 -0
  49. package/dist/schema/index.d.ts +25 -0
  50. package/dist/schema/index.js +25 -0
  51. package/dist/schema/messages.d.ts +414 -0
  52. package/dist/schema/messages.js +82 -0
  53. package/dist/schema/oauth-clients.d.ts +246 -0
  54. package/{src/schema/oauth-clients.ts → dist/schema/oauth-clients.js} +8 -14
  55. package/dist/schema/offerings.d.ts +160 -0
  56. package/dist/schema/offerings.js +24 -0
  57. package/dist/schema/principals.d.ts +128 -0
  58. package/dist/schema/principals.js +23 -0
  59. package/dist/schema/providers.d.ts +237 -0
  60. package/dist/schema/providers.js +22 -0
  61. package/dist/schema/roles.d.ts +242 -0
  62. package/dist/schema/roles.js +37 -0
  63. package/dist/schema/session-assets.d.ts +92 -0
  64. package/dist/schema/session-assets.js +45 -0
  65. package/dist/schema/sessions.d.ts +143 -0
  66. package/dist/schema/sessions.js +29 -0
  67. package/dist/schema/sidecar-allocation.d.ts +406 -0
  68. package/dist/schema/sidecar-allocation.js +57 -0
  69. package/dist/schema/sidecar.d.ts +145 -0
  70. package/dist/schema/sidecar.js +28 -0
  71. package/dist/schema/signal-correlations.d.ts +177 -0
  72. package/dist/schema/signal-correlations.js +31 -0
  73. package/dist/schema/tenants.d.ts +235 -0
  74. package/{src/schema/tenants.ts → dist/schema/tenants.js} +10 -27
  75. package/dist/schema/wallets.d.ts +354 -0
  76. package/dist/schema/wallets.js +43 -0
  77. package/dist/schema/workflow-definitions.d.ts +371 -0
  78. package/dist/schema/workflow-definitions.js +97 -0
  79. package/dist/schema/workflow-run-dispatch.d.ts +328 -0
  80. package/dist/schema/workflow-run-dispatch.js +56 -0
  81. package/dist/schema/workflow-run-execution.d.ts +126 -0
  82. package/dist/schema/workflow-run-execution.js +48 -0
  83. package/dist/schema/workflow-run-launch-spec.d.ts +200 -0
  84. package/dist/schema/workflow-run-launch-spec.js +26 -0
  85. package/dist/schema/workflow-run.d.ts +230 -0
  86. package/dist/schema/workflow-run.js +102 -0
  87. package/dist/sidecar-allocation-store.d.ts +171 -0
  88. package/dist/sidecar-allocation-store.js +512 -0
  89. package/dist/signal-correlation-store.d.ts +36 -0
  90. package/dist/signal-correlation-store.js +63 -0
  91. package/dist/tenant-hierarchy.d.ts +23 -0
  92. package/dist/tenant-hierarchy.js +60 -0
  93. package/dist/workflow-definition-store.d.ts +55 -0
  94. package/dist/workflow-definition-store.js +93 -0
  95. package/dist/workflow-run-dispatch-store.d.ts +80 -0
  96. package/dist/workflow-run-dispatch-store.js +298 -0
  97. package/dist/workflow-run-launch-spec-store.d.ts +13 -0
  98. package/dist/workflow-run-launch-spec-store.js +30 -0
  99. package/dist/workflow-run-store.d.ts +54 -0
  100. package/dist/workflow-run-store.js +85 -0
  101. package/migrations/0029_loose_warlock.sql +1 -0
  102. package/migrations/0030_session_asset_audit_split.sql +5 -0
  103. package/migrations/0031_gigantic_nicolaos.sql +68 -0
  104. package/migrations/0032_lethal_misty_knight.sql +1 -0
  105. package/migrations/0033_old_payback.sql +1 -0
  106. package/migrations/0034_sleepy_songbird.sql +11 -0
  107. package/migrations/0035_violet_giant_man.sql +3 -0
  108. package/migrations/0036_sharp_the_executioner.sql +2 -0
  109. package/migrations/0037_credential_use_backfill.sql +39 -0
  110. package/migrations/0038_chilly_blacklash.sql +37 -0
  111. package/migrations/0039_quick_carnage.sql +2 -0
  112. package/migrations/0040_reshape_approval_origin.sql +24 -0
  113. package/migrations/0041_make_approval_timeout_at_nullable.sql +1 -0
  114. package/migrations/0042_youthful_mantis.sql +2 -0
  115. package/migrations/0043_signal_correlation_deployment_fk.sql +1 -0
  116. package/migrations/0044_model_offering_quirks.sql +1 -0
  117. package/migrations/0045_create_workflow_run.sql +13 -0
  118. package/migrations/0046_approval_signal_correlation_run_fk.sql +2 -0
  119. package/migrations/0047_create_workflow_definition.sql +30 -0
  120. package/migrations/0048_workflow_definition_origin_agent_id.sql +2 -0
  121. package/migrations/0049_curly_omega_flight.sql +2 -0
  122. package/migrations/0050_late_crystal.sql +10 -0
  123. package/migrations/0051_funny_madelyne_pryor.sql +1 -0
  124. package/migrations/0052_drop_inference_turn_instance_fk.sql +1 -0
  125. package/migrations/0053_session_mail_session_id_index.sql +1 -0
  126. package/migrations/0054_rekey_instance_grants_to_workflow_run.sql +10 -0
  127. package/migrations/0055_backfill_anchor_workflow_runs.sql +69 -0
  128. package/migrations/0056_repoint_deployment_fks_to_anchor_run.sql +32 -0
  129. package/migrations/0057_drop_workflow_deployment_projection.sql +22 -0
  130. package/migrations/0058_repoint_offering_fk_to_definition.sql +41 -0
  131. package/migrations/0059_repoint_transaction_fk_to_run.sql +10 -0
  132. package/migrations/0060_drop_session_mail_instance_fk.sql +6 -0
  133. package/migrations/0061_drop_session_asset_instance_fk.sql +7 -0
  134. package/migrations/0062_drop_agent_asset_table.sql +11 -0
  135. package/migrations/0063_repoint_agent_role_fk_to_definition.sql +39 -0
  136. package/migrations/0064_repoint_agent_session_fk_to_definition.sql +47 -0
  137. package/migrations/0065_add_workflow_definition_model_requirements.sql +37 -0
  138. package/migrations/0066_rekey_agent_definition_principals_to_workflow.sql +27 -0
  139. package/migrations/0067_add_workflow_definition_kind.sql +10 -0
  140. package/migrations/0068_drop_agent_tables_and_origin_agent_id.sql +31 -0
  141. package/migrations/0069_drop_workflow_definition_kind.sql +10 -0
  142. package/migrations/0070_flashy_proteus.sql +13 -0
  143. package/migrations/0072_add_workflow_definition_credential_bindings.sql +1 -0
  144. package/migrations/0073_grant_target_exactly_one_check.sql +13 -0
  145. package/migrations/0074_add_provider_api_base_url.sql +1 -0
  146. package/migrations/0075_workflow_run_launch_spec.sql +17 -0
  147. package/migrations/0076_sidecar_allocation.sql +40 -0
  148. package/migrations/0077_workflow_run_dispatch.sql +29 -0
  149. package/migrations/0078_workflow_run_dispatch_kind.sql +2 -0
  150. package/migrations/0079_rename_workflow_run_deployment_id_to_anchor_run_id.sql +4 -0
  151. package/migrations/0080_rename_correlation_approval_deployment_id_to_anchor_run_id.sql +9 -0
  152. package/migrations/0081_workflow_definition_content_hash_and_approved_wire_hash.sql +4 -0
  153. package/migrations/0082_blue_black_queen.sql +1 -0
  154. package/migrations/0083_replace_launch_spec_snapshot_with_frozen_bundle.sql +3 -0
  155. package/migrations/0084_delete_orphaned_credential_grants.sql +22 -0
  156. package/migrations/meta/0029_snapshot.json +2664 -0
  157. package/migrations/meta/0030_snapshot.json +2670 -0
  158. package/migrations/meta/0031_snapshot.json +3100 -0
  159. package/migrations/meta/0032_snapshot.json +3106 -0
  160. package/migrations/meta/0033_snapshot.json +3112 -0
  161. package/migrations/meta/0034_snapshot.json +3198 -0
  162. package/migrations/meta/0035_snapshot.json +3225 -0
  163. package/migrations/meta/0036_snapshot.json +3237 -0
  164. package/migrations/meta/0037_snapshot.json +3237 -0
  165. package/migrations/meta/0038_snapshot.json +3470 -0
  166. package/migrations/meta/0039_snapshot.json +3470 -0
  167. package/migrations/meta/0040_snapshot.json +3489 -0
  168. package/migrations/meta/0041_snapshot.json +3489 -0
  169. package/migrations/meta/0042_snapshot.json +3489 -0
  170. package/migrations/meta/0043_snapshot.json +3498 -0
  171. package/migrations/meta/0044_snapshot.json +3504 -0
  172. package/migrations/meta/0045_snapshot.json +3589 -0
  173. package/migrations/meta/0046_snapshot.json +3607 -0
  174. package/migrations/meta/0047_snapshot.json +3832 -0
  175. package/migrations/meta/0048_snapshot.json +3853 -0
  176. package/migrations/meta/0049_snapshot.json +3854 -0
  177. package/migrations/meta/0050_snapshot.json +3924 -0
  178. package/migrations/meta/0051_snapshot.json +3940 -0
  179. package/migrations/meta/0052_snapshot.json +3931 -0
  180. package/migrations/meta/0053_snapshot.json +3952 -0
  181. package/migrations/meta/0054_snapshot.json +3952 -0
  182. package/migrations/meta/0055_snapshot.json +3952 -0
  183. package/migrations/meta/0056_snapshot.json +3952 -0
  184. package/migrations/meta/0057_snapshot.json +3839 -0
  185. package/migrations/meta/0058_snapshot.json +3839 -0
  186. package/migrations/meta/0059_snapshot.json +3839 -0
  187. package/migrations/meta/0060_snapshot.json +3830 -0
  188. package/migrations/meta/0061_snapshot.json +3821 -0
  189. package/migrations/meta/0062_snapshot.json +3723 -0
  190. package/migrations/meta/0063_snapshot.json +3723 -0
  191. package/migrations/meta/0064_snapshot.json +3723 -0
  192. package/migrations/meta/0065_snapshot.json +3729 -0
  193. package/migrations/meta/0066_snapshot.json +3729 -0
  194. package/migrations/meta/0067_snapshot.json +3736 -0
  195. package/migrations/meta/0068_snapshot.json +3347 -0
  196. package/migrations/meta/0069_snapshot.json +3340 -0
  197. package/migrations/meta/0070_snapshot.json +3444 -0
  198. package/migrations/meta/0072_snapshot.json +3450 -0
  199. package/migrations/meta/0073_snapshot.json +3455 -0
  200. package/migrations/meta/0074_snapshot.json +3461 -0
  201. package/migrations/meta/0075_snapshot.json +3567 -0
  202. package/migrations/meta/0076_snapshot.json +3851 -0
  203. package/migrations/meta/0077_snapshot.json +4067 -0
  204. package/migrations/meta/0078_snapshot.json +4078 -0
  205. package/migrations/meta/0079_snapshot.json +4078 -0
  206. package/migrations/meta/0080_snapshot.json +4078 -0
  207. package/migrations/meta/0081_snapshot.json +4096 -0
  208. package/migrations/meta/0082_snapshot.json +4102 -0
  209. package/migrations/meta/0083_snapshot.json +4096 -0
  210. package/migrations/meta/0084_snapshot.json +4096 -0
  211. package/migrations/meta/_journal.json +385 -0
  212. package/package.json +25 -11
  213. package/drizzle.config.ts +0 -23
  214. package/src/client.ts +0 -24
  215. package/src/config.ts +0 -19
  216. package/src/connection.ts +0 -27
  217. package/src/credential-resolution.ts +0 -378
  218. package/src/grant-store.ts +0 -51
  219. package/src/index.ts +0 -32
  220. package/src/migrate.test.ts +0 -35
  221. package/src/migrate.ts +0 -168
  222. package/src/parse-row.test.ts +0 -113
  223. package/src/parse-row.ts +0 -185
  224. package/src/schema/agent-assets.ts +0 -21
  225. package/src/schema/agents.ts +0 -49
  226. package/src/schema/auth.ts +0 -51
  227. package/src/schema/grants.ts +0 -26
  228. package/src/schema/index.ts +0 -19
  229. package/src/schema/instances.ts +0 -36
  230. package/src/schema/messages.ts +0 -108
  231. package/src/schema/offerings.ts +0 -20
  232. package/src/schema/principals.ts +0 -28
  233. package/src/schema/providers.ts +0 -23
  234. package/src/schema/roles.ts +0 -51
  235. package/src/schema/session-assets.ts +0 -49
  236. package/src/schema/sessions.ts +0 -26
  237. package/src/schema/sidecar.ts +0 -14
  238. package/src/schema/wallets.ts +0 -44
  239. package/src/tenant-hierarchy.ts +0 -34
  240. package/tsconfig.json +0 -4
  241. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,291 @@
1
+ import { and, eq } from "drizzle-orm";
2
+ import { createNoopCredentialCipher } from "@intx/crypto";
3
+ import { InvokerModelPreferences, ModelRequirements, credentialAad, } from "@intx/types";
4
+ import { listVisibleOfferings, } from "./catalog-resolution.js";
5
+ import { resolveCredentialById } from "./credential-resolution.js";
6
+ import { parseModelOfferingRow } from "./parse-row.js";
7
+ import { workflowDefinition } from "./schema/workflow-definitions.js";
8
+ function byPriority(a, b) {
9
+ if (a.offering.priority !== b.offering.priority) {
10
+ return a.offering.priority - b.offering.priority;
11
+ }
12
+ // Deterministic tiebreak so the head/defaultSource never flaps across
13
+ // resolutions. Equal-priority load balancing is future work.
14
+ return a.offering.id < b.offering.id ? -1 : 1;
15
+ }
16
+ /**
17
+ * Applies a provider preference over an already-priority-sorted candidate
18
+ * list. `pin` restricts to the named providers (ordered by the preference);
19
+ * `prefer` fronts the named providers in preference order and keeps the rest
20
+ * as fallback. With no preference the priority order stands.
21
+ */
22
+ function applyPreference(candidates, preference) {
23
+ if (preference === undefined)
24
+ return candidates;
25
+ const rank = new Map(preference.order.map((name, i) => [name, i]));
26
+ // Partition by whether the preference names the provider, capturing the
27
+ // rank as we go. `ranked` only ever holds named providers, so the sort
28
+ // reads a concrete rank and never needs a fallback for a missing one.
29
+ const ranked = [];
30
+ const rest = [];
31
+ for (const offering of candidates) {
32
+ const position = rank.get(offering.provider.name);
33
+ if (position === undefined) {
34
+ rest.push(offering);
35
+ }
36
+ else {
37
+ ranked.push({ offering, rank: position });
38
+ }
39
+ }
40
+ ranked.sort((a, b) => a.rank - b.rank);
41
+ const named = ranked.map((entry) => entry.offering);
42
+ // `pin` drops every provider the preference did not name; `prefer` keeps
43
+ // the rest as fallback after the named providers.
44
+ return preference.mode === "pin" ? named : [...named, ...rest];
45
+ }
46
+ async function buildSource(db, tenantId, resolved, credentialCipher) {
47
+ const { provider, model, offering } = resolved;
48
+ if (provider.credentialId === null) {
49
+ // No credential reference. A wallet-backed provider is a valid catalog
50
+ // row but not launchable in this credential-backed-only release;
51
+ // anything else is a misconfigured row.
52
+ if (provider.walletId !== null) {
53
+ return {
54
+ ok: false,
55
+ skip: { reason: "wallet_backed", provider: provider.name },
56
+ };
57
+ }
58
+ return {
59
+ ok: false,
60
+ skip: { reason: "provider_misconfigured", provider: provider.name },
61
+ };
62
+ }
63
+ // Resolve the secret through the tenant-scoped credential resolver so a
64
+ // provider row referencing a credential outside the tenant's ancestor
65
+ // chain cannot leak that secret (the chain is the authority).
66
+ const credential = await resolveCredentialById(db, tenantId, provider.credentialId);
67
+ if (credential === null) {
68
+ return {
69
+ ok: false,
70
+ skip: { reason: "credential_unresolved", provider: provider.name },
71
+ };
72
+ }
73
+ // Authority to use a credential through a catalog provider is ownership
74
+ // within the tenant hierarchy -- the same rule the tool-binding path resolves
75
+ // (`source: "tenant"`). `resolveCredentialById` above already proved the
76
+ // credential is reachable in this tenant's ancestor chain, so a tenant-owned
77
+ // credential (`principalId IS NULL`) is authorized here by ownership alone,
78
+ // with no personal grant and no role grant consulted. This runs live on every
79
+ // launch and rotation, so a catalog edit that adds an eligible credential is
80
+ // picked up without re-materializing any grant. A principal-owned credential
81
+ // is not usable through a shared catalog provider; fail closed.
82
+ if (credential.principalId !== null) {
83
+ return {
84
+ ok: false,
85
+ skip: { reason: "credential_unauthorized", provider: provider.name },
86
+ };
87
+ }
88
+ // Validate the row once at this DB-to-runtime boundary. This narrows the
89
+ // jsonb `quirks` from `unknown` to a `Record | null` and checks
90
+ // `capabilities` against the curated enum. `quirks` is spread in only when
91
+ // non-null so a source with no accommodations omits the key entirely
92
+ // (InferenceSource.quirks is present-or-absent, never null). The capability
93
+ // filter in resolveModelSources still reads the raw row's `capabilities`
94
+ // for its `.includes` check; that read-only comparison cannot be corrupted
95
+ // into a wrong routing decision, so it is left as-is.
96
+ const parsed = parseModelOfferingRow(offering);
97
+ return {
98
+ ok: true,
99
+ source: {
100
+ id: offering.id,
101
+ provider: provider.plugin,
102
+ baseURL: provider.baseURL,
103
+ // Decrypt the stored secret at the one point it is used. Strict: a
104
+ // non-ciphertext value (a row not yet re-keyed, or a write that failed to
105
+ // encrypt) throws rather than delivering a bad key -- fail closed.
106
+ apiKey: await credentialCipher.decrypt(credential.secret, credentialAad(credential.id, "secret")),
107
+ model: model.canonicalName,
108
+ capabilities: parsed.capabilities,
109
+ ...(parsed.quirks !== null ? { quirks: parsed.quirks } : {}),
110
+ },
111
+ };
112
+ }
113
+ /**
114
+ * Rebuild an exact, ordered source chain from durable catalog offering ids.
115
+ * This is the recovery counterpart to launch-time source selection: it keeps
116
+ * secrets out of persisted launch specs and rechecks tenant visibility and
117
+ * credential ownership on every launch.
118
+ */
119
+ export async function resolveSourcesByOfferingIds(db, tenantId, offeringIds, credentialCipher = createNoopCredentialCipher()) {
120
+ const visible = await listVisibleOfferings(db, tenantId);
121
+ const byId = new Map(visible.map((entry) => [entry.offering.id, entry]));
122
+ const sources = [];
123
+ for (const offeringId of offeringIds) {
124
+ const offering = byId.get(offeringId);
125
+ if (offering === undefined) {
126
+ return { ok: false, reason: "offering_unavailable", offeringId };
127
+ }
128
+ const built = await buildSource(db, tenantId, offering, credentialCipher);
129
+ if (!built.ok) {
130
+ return {
131
+ ok: false,
132
+ reason: "offering_unavailable",
133
+ offeringId,
134
+ skip: built.skip,
135
+ };
136
+ }
137
+ sources.push(built.source);
138
+ }
139
+ return { ok: true, sources };
140
+ }
141
+ /**
142
+ * Resolves an agent's model requirements against the tenant catalog into an
143
+ * ordered `InferenceSource[]` for the harness.
144
+ *
145
+ * For each requirement, the tenant-visible offerings for the named model are
146
+ * filtered by the required capabilities, ordered by catalog priority, then
147
+ * reordered by the creator preference and finally the invoker preference
148
+ * (invoker preferences key on the canonical model name). Each surviving
149
+ * offering is resolved to a credential-backed source; offerings that cannot
150
+ * produce one are skipped. A required model that yields no source makes the
151
+ * agent unlaunchable.
152
+ *
153
+ * A credential-backed source is emitted only when the launching tenant owns
154
+ * the referenced credential within its hierarchy (ownership is the authority);
155
+ * otherwise the offering is skipped (`credential_unauthorized`) and its secret
156
+ * is withheld.
157
+ */
158
+ export async function resolveModelSources(db, tenantId, requirements, opts) {
159
+ if (requirements.length === 0) {
160
+ return { ok: false, reason: "no_requirements" };
161
+ }
162
+ const credentialCipher = opts?.credentialCipher ?? createNoopCredentialCipher();
163
+ const visible = await listVisibleOfferings(db, tenantId);
164
+ const sources = [];
165
+ for (const requirement of requirements) {
166
+ let candidates = visible
167
+ .filter((o) => o.model.canonicalName === requirement.model)
168
+ .sort(byPriority);
169
+ if (requirement.capabilities && requirement.capabilities.length > 0) {
170
+ const required = requirement.capabilities;
171
+ candidates = candidates.filter((o) => required.every((c) => o.offering.capabilities.includes(c)));
172
+ }
173
+ candidates = applyPreference(candidates, requirement.providers);
174
+ candidates = applyPreference(candidates, opts?.invokerPreferences?.[requirement.model]);
175
+ const skips = [];
176
+ const modelSources = [];
177
+ for (const candidate of candidates) {
178
+ const built = await buildSource(db, tenantId, candidate, credentialCipher);
179
+ if (built.ok) {
180
+ modelSources.push(built.source);
181
+ }
182
+ else {
183
+ skips.push(built.skip);
184
+ }
185
+ }
186
+ if (modelSources.length === 0) {
187
+ return {
188
+ ok: false,
189
+ reason: "model_unavailable",
190
+ model: requirement.model,
191
+ skips,
192
+ };
193
+ }
194
+ sources.push(...modelSources);
195
+ }
196
+ return { ok: true, sources };
197
+ }
198
+ /**
199
+ * Resolve an agent's model requirements to the credential-free
200
+ * `{ provider, model }` inference preferences a folded workflow definition
201
+ * carries on its step agent. Reuses `resolveModelSources` -- the same catalog
202
+ * + creator-grant resolution the instance launch path runs -- and projects its
203
+ * ordered sources to their `{ provider (plugin), model }` identity, dropping
204
+ * the credentials: a definition's inference preference is hash-only, and the
205
+ * credential-bearing sources are supplied per deploy on the workflow path.
206
+ *
207
+ * A requirement set that resolves to no source is a hard failure. A folded
208
+ * agent whose model is unresolvable is undeployable, so this raises rather
209
+ * than synthesizing an empty preference list that would silently strip the
210
+ * agent's inference. It also raises when the resolved sources are not
211
+ * injective on `(provider, model)` -- the projection would otherwise freeze
212
+ * an ambiguous preference list whose lost `offering.id` distinctions cannot be
213
+ * recovered after the agent's columns are dropped.
214
+ */
215
+ export async function resolveInferencePreferences(db, tenantId, requirements) {
216
+ const resolution = await resolveModelSources(db, tenantId, requirements);
217
+ if (!resolution.ok) {
218
+ throw new Error(`cannot resolve inference preferences for the folded definition: ${resolution.reason}`);
219
+ }
220
+ // The projection to `{ provider (plugin), model }` drops the `offering.id`
221
+ // that keys each resolved source. Two distinct offerings can share a
222
+ // provider plugin and canonical model -- the catalog permits two
223
+ // `model_provider` rows on the same plugin (uniqueness is on the provider
224
+ // name), each offering the same model -- so the projection is only
225
+ // well-defined when the resolved sources are injective on `(provider,
226
+ // model)`. If they collapse, the folded definition's inference preferences
227
+ // cannot distinguish which offering each entry meant, and once the agent's
228
+ // columns are dropped that distinction is unrecoverable. Refuse to
229
+ // synthesize an ambiguous preference list: fail loud so the collapse
230
+ // surfaces in the fold/materialize manifest rather than freezing silently.
231
+ const preferences = resolution.sources.map((source) => ({
232
+ provider: source.provider,
233
+ model: source.model,
234
+ }));
235
+ const seen = new Set();
236
+ for (const preference of preferences) {
237
+ // Join on a NUL: it cannot appear in a provider plugin or canonical model
238
+ // name, so the composite key is collision-free. Written as an explicit
239
+ // unicode escape rather than an invisible raw control byte in the source.
240
+ const key = `${preference.provider}\u0000${preference.model}`;
241
+ if (seen.has(key)) {
242
+ throw new Error(`cannot resolve inference preferences for the folded definition: ` +
243
+ `the model resolution is not injective on (provider, model) -- ` +
244
+ `two offerings collapse to (${preference.provider}, ${preference.model}); ` +
245
+ `a folded agent requires a distinguishable inference preference per source`);
246
+ }
247
+ seen.add(key);
248
+ }
249
+ return preferences;
250
+ }
251
+ /**
252
+ * Resolves the ordered sources for a running instance from persisted state:
253
+ * the definition's model requirements and the invoker's launch-time
254
+ * preferences stored on the instance row. The credential-rotation and
255
+ * catalog-edit source push resolves through this, so a running instance's
256
+ * source list is a pure function of persisted state — re-resolution
257
+ * reproduces the launch ordering, including the invoker's reorder/restrict.
258
+ */
259
+ export async function resolveInstanceModelSources(db, tenantId, instance, credentialCipher) {
260
+ // Resolve from the run's own definition by primary key. This is the SAME row
261
+ // the launch resolves its requirements from, so a rotation or catalog edit
262
+ // reproduces the launch's model resolution rather than drifting. Scope to the
263
+ // resolving tenant, matching the launch route: a definition in another tenant
264
+ // resolves to nothing rather than contributing another tenant's models. A
265
+ // definition with no creator principal cannot authorize a credential-backed
266
+ // source, so it fails closed like a missing definition.
267
+ const definitionRow = await db.query.workflowDefinition.findFirst({
268
+ where: and(eq(workflowDefinition.id, instance.definitionId), eq(workflowDefinition.tenantId, tenantId)),
269
+ });
270
+ // A credential-backed source is authorized by tenant ownership of the
271
+ // credential (walked in `buildSource`), not by the definition's creator, so a
272
+ // definition with no creator can still launch a tenant-owned inference
273
+ // source. Only a missing definition (or one in another tenant) fails closed.
274
+ if (definitionRow === undefined) {
275
+ return { ok: false, reason: "no_requirements" };
276
+ }
277
+ const requirements = definitionRow.modelRequirements !== null
278
+ ? ModelRequirements.assert(definitionRow.modelRequirements)
279
+ : [];
280
+ const preferences = instance.modelPreferences !== null
281
+ ? InvokerModelPreferences.assert(instance.modelPreferences)
282
+ : [];
283
+ const invokerPreferences = {};
284
+ for (const preference of preferences) {
285
+ invokerPreferences[preference.model] = preference.providers;
286
+ }
287
+ return resolveModelSources(db, tenantId, requirements, {
288
+ invokerPreferences,
289
+ ...(credentialCipher !== undefined ? { credentialCipher } : {}),
290
+ });
291
+ }
@@ -0,0 +1,373 @@
1
+ import type { approval, credential, gitToken, grant, modelOffering, modelProvider, oauthClient, offering, principal, provider, signalCorrelation, tenant, transaction, turnPart, wallet, workflowDefinition, workflowDefinitionVersion, workflowRun, workflowRunDispatch, workflowRunLaunchSpec } from "./schema/index.js";
2
+ export declare const GitTokenKindValidator: import("arktype/internal/variants/string.ts").StringType<"pat" | "svc", {}>;
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;
11
+ }[] | null;
12
+ modelRequirements: {
13
+ model: string;
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")[];
15
+ providers?: {
16
+ mode: "prefer" | "pin";
17
+ order: string[];
18
+ };
19
+ }[] | null;
20
+ credentialBindings: {
21
+ package: string;
22
+ handle: string;
23
+ provider: string;
24
+ locator: "tenant";
25
+ name?: string;
26
+ }[] | null;
27
+ id: string;
28
+ name: string;
29
+ createdAt: Date;
30
+ updatedAt: Date;
31
+ tenantId: string;
32
+ creatorPrincipalId: string | null;
33
+ assetId: string | null;
34
+ wireHash: string | null;
35
+ description: string | null;
36
+ currentVersion: string;
37
+ };
38
+ export declare function parseWorkflowDefinitionVersionRow(row: typeof workflowDefinitionVersion.$inferSelect): {
39
+ status: "active" | "inactive" | "failed";
40
+ id: string;
41
+ createdAt: Date;
42
+ definitionId: string;
43
+ version: string;
44
+ approvedWireHash: string | null;
45
+ grantSnapshot: unknown;
46
+ };
47
+ export declare function parseGrantRow(row: typeof grant.$inferSelect): {
48
+ effect: "allow" | "deny" | "ask";
49
+ origin: "role" | "system" | "creator" | "invoker";
50
+ conditions: Record<string, unknown> | null;
51
+ id: string;
52
+ createdAt: Date;
53
+ updatedAt: Date;
54
+ expiresAt: Date | null;
55
+ tenantId: string;
56
+ roleId: string | null;
57
+ principalId: string | null;
58
+ resource: string;
59
+ action: string;
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 parseSignalCorrelationRow(row: typeof signalCorrelation.$inferSelect): {
87
+ kind: "approval";
88
+ createdAt: Date;
89
+ tenantId: string;
90
+ anchorRunId: string;
91
+ runId: string;
92
+ agentAddress: string;
93
+ correlationId: string;
94
+ resolvedAt: Date | null;
95
+ signalName: string;
96
+ signalId: string | null;
97
+ };
98
+ export declare function parseWorkflowRunRow(row: typeof workflowRun.$inferSelect): {
99
+ status: "deployed" | "failed" | "running" | "completed" | "cancelled";
100
+ modelPreferences: {
101
+ model: string;
102
+ providers: {
103
+ mode: "prefer" | "pin";
104
+ order: string[];
105
+ };
106
+ }[] | null;
107
+ id: string;
108
+ createdAt: Date;
109
+ tenantId: string;
110
+ definitionId: string;
111
+ principalId: string | null;
112
+ anchorRunId: string | null;
113
+ address: string | null;
114
+ publicKey: string | null;
115
+ sidecarId: string | null;
116
+ kernelId: string | null;
117
+ endedAt: Date | null;
118
+ };
119
+ export declare function parseWorkflowRunLaunchSpecRow(row: typeof workflowRunLaunchSpec.$inferSelect): {
120
+ frozenApprovalBundle: {
121
+ source: {
122
+ kind: "registry";
123
+ registry: string;
124
+ } | {
125
+ kind: "asset";
126
+ assetId: string;
127
+ package: {
128
+ format: "tarball";
129
+ } | {
130
+ format: "source";
131
+ commitSha: string;
132
+ packageName?: string;
133
+ };
134
+ };
135
+ entry: string;
136
+ projection: {
137
+ id: string;
138
+ triggers: unknown[];
139
+ stepOrder: string[];
140
+ steps: {
141
+ [x: string]: unknown;
142
+ };
143
+ state?: Record<string, unknown>;
144
+ credentialBindings?: {
145
+ package: string;
146
+ handle: string;
147
+ provider: string;
148
+ locator: "tenant";
149
+ name?: string;
150
+ }[];
151
+ };
152
+ closure: {
153
+ schemaVersion: "1";
154
+ topLevel: {
155
+ name: string;
156
+ version: string;
157
+ credentials?: {
158
+ handle: string;
159
+ scopes?: string[];
160
+ }[];
161
+ }[];
162
+ entries: {
163
+ name: string;
164
+ version: string;
165
+ source: {
166
+ kind: "registry";
167
+ registry: string;
168
+ integrity: string;
169
+ } | {
170
+ kind: "asset";
171
+ assetId: string;
172
+ package: {
173
+ format: "tarball";
174
+ path: string;
175
+ integrity: string;
176
+ } | {
177
+ format: "source";
178
+ commitSha: string;
179
+ packageDir: string;
180
+ treeOid: string;
181
+ };
182
+ };
183
+ os?: string[];
184
+ cpu?: string[];
185
+ tarballUrl?: string;
186
+ }[];
187
+ };
188
+ approvedWireHash: string;
189
+ approvedGrants: string[];
190
+ };
191
+ sourceOfferingIds: string[];
192
+ deployContent: Record<string, unknown>;
193
+ toolPackagePins: {
194
+ name: string;
195
+ version: string;
196
+ }[] | null;
197
+ createdAt: Date;
198
+ anchorRunId: string;
199
+ sessionId: string;
200
+ schemaVersion: number;
201
+ deploymentDomain: string;
202
+ sourceAuthorityPrincipalId: string;
203
+ defaultSourceOfferingId: string;
204
+ };
205
+ export declare function parseWorkflowRunDispatchRow(row: typeof workflowRunDispatch.$inferSelect): {
206
+ kind: "signal" | "mail";
207
+ status: "failed" | "pending" | "acknowledged" | "settled";
208
+ stepGrants: {
209
+ id: string;
210
+ resource: string;
211
+ action: string;
212
+ effect: "allow" | "deny" | "ask";
213
+ origin: "role" | "system" | "creator" | "invoker";
214
+ conditions: {
215
+ [x: string]: unknown;
216
+ } | null;
217
+ expiresAt: Date | null;
218
+ roleId: string | null;
219
+ principalId: string | null;
220
+ }[];
221
+ id: string;
222
+ createdAt: Date;
223
+ updatedAt: Date;
224
+ anchorRunId: string;
225
+ messageId: string;
226
+ nextAttemptAt: Date | null;
227
+ failureCode: string | null;
228
+ failureMessage: string | null;
229
+ rawMessage: Uint8Array<ArrayBufferLike>;
230
+ acknowledgedGeneration: number | null;
231
+ attemptCount: number;
232
+ deliveryLeaseId: string | null;
233
+ deliveryLeaseExpiresAt: Date | null;
234
+ acknowledgedAt: Date | null;
235
+ settledAt: Date | null;
236
+ };
237
+ export declare function parseOfferingRow(row: typeof offering.$inferSelect): {
238
+ pricing: Record<string, unknown> | null;
239
+ schema: Record<string, unknown> | null;
240
+ id: string;
241
+ name: string;
242
+ createdAt: Date;
243
+ updatedAt: Date;
244
+ tenantId: string;
245
+ description: string | null;
246
+ agentId: string;
247
+ };
248
+ export declare function parseCredentialRow(row: typeof credential.$inferSelect): {
249
+ type: "api_key" | "oauth_token" | "certificate" | "other";
250
+ status: "active" | "error" | "expired" | "revoked";
251
+ metadata: Record<string, unknown> | null;
252
+ id: string;
253
+ name: string;
254
+ createdAt: Date;
255
+ updatedAt: Date;
256
+ expiresAt: Date | null;
257
+ providerId: string;
258
+ tenantId: string;
259
+ description: string | null;
260
+ principalId: string | null;
261
+ scopes: string[] | null;
262
+ oauthClientId: string | null;
263
+ secret: string;
264
+ refreshSecret: string | null;
265
+ };
266
+ export declare function parseProviderRow(row: typeof provider.$inferSelect): {
267
+ metadata: Record<string, unknown> | null;
268
+ id: string;
269
+ name: string;
270
+ createdAt: Date;
271
+ updatedAt: Date;
272
+ tenantId: string;
273
+ plugin: string;
274
+ apiBaseUrl: string | null;
275
+ authorizationUrl: string | null;
276
+ tokenUrl: string | null;
277
+ userInfoUrl: string | null;
278
+ scopes: string[] | null;
279
+ };
280
+ export declare function parseModelProviderRow(row: typeof modelProvider.$inferSelect): {
281
+ plugin: "anthropic" | "openai" | "openai-compatible" | "google-genai";
282
+ id: string;
283
+ name: string;
284
+ createdAt: Date;
285
+ updatedAt: Date;
286
+ tenantId: string;
287
+ walletId: string | null;
288
+ disabled: boolean;
289
+ baseURL: string;
290
+ credentialId: string | null;
291
+ };
292
+ export declare function parseModelOfferingRow(row: typeof modelOffering.$inferSelect): {
293
+ 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")[];
294
+ quirks: Record<string, unknown> | null;
295
+ id: string;
296
+ createdAt: Date;
297
+ updatedAt: Date;
298
+ providerId: string;
299
+ tenantId: string;
300
+ disabled: boolean;
301
+ modelId: string;
302
+ priority: number;
303
+ deploymentTags: string[];
304
+ };
305
+ export declare function parseTenantRow(row: typeof tenant.$inferSelect): {
306
+ config: {
307
+ [x: string]: unknown;
308
+ sidecarPlacement?: {
309
+ sharing: "exclusive";
310
+ reuse?: "never" | "same-deployment";
311
+ };
312
+ } | null;
313
+ domain: string;
314
+ id: string;
315
+ name: string;
316
+ createdAt: Date;
317
+ updatedAt: Date;
318
+ slug: string;
319
+ parentId: string | null;
320
+ };
321
+ export declare function parseWalletRow(row: typeof wallet.$inferSelect): {
322
+ backendType: "crypto" | "fiat" | "credits";
323
+ config: Record<string, unknown> | null;
324
+ id: string;
325
+ name: string;
326
+ createdAt: Date;
327
+ updatedAt: Date;
328
+ tenantId: string;
329
+ currency: string;
330
+ balance: string;
331
+ };
332
+ export declare function parseTransactionRow(row: typeof transaction.$inferSelect): {
333
+ direction: "inbound" | "outbound";
334
+ status: "failed" | "completed" | "pending";
335
+ id: string;
336
+ createdAt: Date;
337
+ runId: string | null;
338
+ currency: string;
339
+ walletId: string;
340
+ amount: string;
341
+ recipientId: string | null;
342
+ senderId: string | null;
343
+ requestId: string | null;
344
+ };
345
+ export declare function parseOAuthClientRow(row: typeof oauthClient.$inferSelect): {
346
+ metadata: Record<string, unknown> | null;
347
+ id: string;
348
+ name: string;
349
+ createdAt: Date;
350
+ updatedAt: Date;
351
+ providerId: string;
352
+ tenantId: string;
353
+ clientId: string;
354
+ clientSecret: string;
355
+ redirectUris: string[] | null;
356
+ defaultScopes: string[] | null;
357
+ };
358
+ export declare function parseGitTokenRow(row: typeof gitToken.$inferSelect): {
359
+ kind: "pat" | "svc";
360
+ actions: ("init" | "writeTree" | "receivePack" | "createPack" | "resolveRef")[];
361
+ id: string;
362
+ name: string;
363
+ createdAt: Date;
364
+ userId: string;
365
+ expiresAt: Date;
366
+ tenantId: string | null;
367
+ principalId: string | null;
368
+ resource: string;
369
+ tokenHashSha256: Uint8Array<ArrayBufferLike>;
370
+ refPattern: string;
371
+ revokedAt: Date | null;
372
+ };
373
+ export declare function parseTurnPartType(partType: string): (typeof turnPart.$inferInsert)["type"];