@opengeni/db 0.28.9 → 0.36.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 (225) hide show
  1. package/dist/attached-browser-devices.d.ts +42 -0
  2. package/dist/attempt-tool-catalogs.d.ts +16 -0
  3. package/dist/browser-auth.d.ts +299 -0
  4. package/dist/browser-downloads.d.ts +51 -0
  5. package/dist/browser-identities.d.ts +137 -0
  6. package/dist/browser-sessions.d.ts +354 -0
  7. package/dist/browser-state-artifacts.d.ts +91 -0
  8. package/dist/canonical-human-identities.d.ts +48 -0
  9. package/dist/canonical-human-identities.js +28 -0
  10. package/dist/canonical-human-identities.js.map +1 -0
  11. package/dist/capability-components.d.ts +37 -0
  12. package/dist/capability-integrations.d.ts +194 -0
  13. package/dist/chunk-5KTIEDX7.js +13200 -0
  14. package/dist/chunk-5KTIEDX7.js.map +1 -0
  15. package/dist/chunk-CQZ4QSP5.js +995 -0
  16. package/dist/chunk-CQZ4QSP5.js.map +1 -0
  17. package/dist/chunk-F32YT7MP.js +117 -0
  18. package/dist/chunk-F32YT7MP.js.map +1 -0
  19. package/dist/chunk-JC5QPLUM.js +666 -0
  20. package/dist/chunk-JC5QPLUM.js.map +1 -0
  21. package/dist/chunk-P3RE4D2B.js +3535 -0
  22. package/dist/chunk-P3RE4D2B.js.map +1 -0
  23. package/dist/chunk-TAUTWOS5.js +259 -0
  24. package/dist/chunk-TAUTWOS5.js.map +1 -0
  25. package/dist/chunk-VKLB2ZGI.js +2389 -0
  26. package/dist/chunk-VKLB2ZGI.js.map +1 -0
  27. package/dist/codemode-operations.d.ts +117 -0
  28. package/dist/codex-token-resolver.d.ts +2 -2
  29. package/dist/company-profile-schema.d.ts +798 -0
  30. package/dist/company-profile.d.ts +268 -0
  31. package/dist/computer-sessions.d.ts +109 -0
  32. package/dist/connection-token-resolver.d.ts +9 -4
  33. package/dist/database.d.ts +57 -3
  34. package/dist/editable-artifact-durable-export.d.ts +153 -0
  35. package/dist/editable-artifact-durable-export.js +788 -0
  36. package/dist/editable-artifact-durable-export.js.map +1 -0
  37. package/dist/editable-artifact-materialization.d.ts +123 -0
  38. package/dist/editable-artifacts-schema.d.ts +4152 -0
  39. package/dist/editable-artifacts.d.ts +610 -0
  40. package/dist/editable-artifacts.js +26 -0
  41. package/dist/editable-artifacts.js.map +1 -0
  42. package/dist/generated-images.d.ts +139 -0
  43. package/dist/index.d.ts +1122 -126
  44. package/dist/index.js +42329 -18407
  45. package/dist/index.js.map +1 -1
  46. package/dist/insights.d.ts +73 -10
  47. package/dist/integration-bindings.d.ts +70 -0
  48. package/dist/integration-facets.d.ts +99 -0
  49. package/dist/interaction-revisions.d.ts +14 -0
  50. package/dist/interaction-schema.d.ts +6036 -0
  51. package/dist/knowledge-source-sync-schema.d.ts +1679 -0
  52. package/dist/knowledge-source-sync.d.ts +274 -0
  53. package/dist/memory-governance.d.ts +2 -2
  54. package/dist/memory-slack-delivery.d.ts +101 -0
  55. package/dist/new-session-drafts.d.ts +2 -2
  56. package/dist/pack-components.d.ts +76 -0
  57. package/dist/pack-installations.d.ts +86 -0
  58. package/dist/persistence-errors.d.ts +3 -3
  59. package/dist/plugin-packages.d.ts +126 -0
  60. package/dist/preference-registry.d.ts +1 -1
  61. package/dist/provision-roles.d.ts +9 -1
  62. package/dist/provision-roles.js +1 -1
  63. package/dist/runtime-posture.d.ts +19 -6
  64. package/dist/schema.d.ts +22654 -9359
  65. package/dist/schema.js +209 -3
  66. package/dist/scoped-knowledge.d.ts +97 -1
  67. package/dist/session-control.d.ts +33 -7
  68. package/dist/session-queue-commands.d.ts +19 -11
  69. package/dist/session-realtime-context.d.ts +2 -2
  70. package/dist/session-realtime-ledger.d.ts +4 -4
  71. package/dist/session-realtime-mirror.d.ts +2 -2
  72. package/dist/session-realtime-state.d.ts +1 -1
  73. package/dist/session-realtime-terminal.d.ts +1 -1
  74. package/dist/session-realtime.d.ts +7 -7
  75. package/dist/session-tenancy.d.ts +44 -0
  76. package/dist/session-tenancy.js +16 -0
  77. package/dist/session-tenancy.js.map +1 -0
  78. package/dist/session-tool-call-settlement.d.ts +1 -1
  79. package/dist/slack-task-policy-schema.d.ts +847 -0
  80. package/dist/slack-task-policy.d.ts +65 -0
  81. package/dist/slack-user-link-access.d.ts +54 -0
  82. package/dist/task-notes-schema.d.ts +924 -0
  83. package/dist/task-notes.d.ts +85 -0
  84. package/dist/transcription-recordings.d.ts +2 -2
  85. package/dist/turn-initiator.d.ts +2 -2
  86. package/dist/video-generation.d.ts +225 -0
  87. package/dist/video-generation.js +68 -0
  88. package/dist/video-generation.js.map +1 -0
  89. package/dist/workspace-artifacts.d.ts +1 -1
  90. package/dist/workspace-instruction-policies.d.ts +1 -1
  91. package/dist/workspace-learning-policy-schema.d.ts +932 -0
  92. package/dist/workspace-learning-policy.d.ts +161 -0
  93. package/dist/xai-subscription.d.ts +326 -0
  94. package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
  95. package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
  96. package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
  97. package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
  98. package/drizzle/0187_generated_image_artifacts.sql +173 -0
  99. package/drizzle/0188_image_generation_retention_failure.sql +25 -0
  100. package/drizzle/0189_retained_session_image_formats.sql +13 -0
  101. package/drizzle/0190_timeline_annotations.sql +31 -0
  102. package/drizzle/0191_editable_artifact_engine.sql +3853 -0
  103. package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
  104. package/drizzle/0193_editable_artifact_authorization.sql +190 -0
  105. package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
  106. package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
  107. package/drizzle/0196_rig_provider_images.sql +152 -0
  108. package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
  109. package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
  110. package/drizzle/0199_workspace_learning_policy.sql +980 -0
  111. package/drizzle/0201_company_profile_authority.sql +849 -0
  112. package/drizzle/0202_document_index_checkpoints.sql +85 -0
  113. package/drizzle/0203_durable_video_generation.sql +353 -0
  114. package/drizzle/0204_video_generation_funding.sql +44 -0
  115. package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
  116. package/drizzle/0206_browser_sessions.sql +646 -0
  117. package/drizzle/0207_browser_identities.sql +379 -0
  118. package/drizzle/0208_attached_browser_devices.sql +165 -0
  119. package/drizzle/0209_computer_sessions.sql +575 -0
  120. package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
  121. package/drizzle/0211_editable_artifact_session_links.sql +58 -0
  122. package/drizzle/0212_browser_state_transfer_hardening.sql +560 -0
  123. package/drizzle/0212_slack_installation_bindings.sql +348 -0
  124. package/drizzle/0213_browser_interaction_authority.sql +67 -0
  125. package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
  126. package/drizzle/0214_browser_download_saves.sql +34 -0
  127. package/drizzle/0214_session_activity_commit_gate.sql +262 -0
  128. package/drizzle/0215_browser_controller_host.sql +385 -0
  129. package/drizzle/0215_capabilities_platform.sql +980 -0
  130. package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
  131. package/drizzle/0216_pack_component_ownership.sql +235 -0
  132. package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
  133. package/drizzle/0217_external_browser_auth_operations.sql +22 -0
  134. package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
  135. package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
  136. package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
  137. package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
  138. package/drizzle/0220_session_channels.sql +54 -0
  139. package/drizzle/0221_sessions_channel_index.sql +5 -0
  140. package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
  141. package/drizzle/0222_sessions_channel_fk.sql +89 -0
  142. package/drizzle/0223_pending_tool_event_output.sql +55 -0
  143. package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
  144. package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
  145. package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
  146. package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
  147. package/drizzle/0227_slack_native_actions.sql +145 -0
  148. package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
  149. package/drizzle/0228_slack_task_policy.sql +400 -0
  150. package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
  151. package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
  152. package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
  153. package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
  154. package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
  155. package/drizzle/0234_xai_subscription_authority.sql +1139 -0
  156. package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
  157. package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
  158. package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
  159. package/drizzle/0237_interaction_transition_reaper.sql +263 -0
  160. package/drizzle/0238_goal_persistence_policy.sql +471 -0
  161. package/drizzle/0238_supergrok_realtime_model.sql +29 -0
  162. package/drizzle/0239_supergrok_video_funding.sql +50 -0
  163. package/drizzle/0239_task_tree_notes.sql +990 -0
  164. package/package.json +26 -5
  165. package/src/attached-browser-devices.ts +371 -0
  166. package/src/attempt-tool-catalogs.ts +131 -0
  167. package/src/browser-auth.ts +4373 -0
  168. package/src/browser-downloads.ts +517 -0
  169. package/src/browser-identities.ts +1385 -0
  170. package/src/browser-sessions.ts +2294 -0
  171. package/src/browser-state-artifacts.ts +449 -0
  172. package/src/canonical-human-identities.ts +317 -0
  173. package/src/capability-components.ts +166 -0
  174. package/src/capability-integrations.ts +1571 -0
  175. package/src/codemode-operations.ts +674 -0
  176. package/src/company-profile-schema.ts +143 -0
  177. package/src/company-profile.ts +968 -0
  178. package/src/computer-sessions.ts +1130 -0
  179. package/src/connection-token-resolver.ts +270 -25
  180. package/src/database.ts +468 -24
  181. package/src/editable-artifact-durable-export.ts +1201 -0
  182. package/src/editable-artifact-materialization.ts +893 -0
  183. package/src/editable-artifacts-schema.ts +879 -0
  184. package/src/editable-artifacts.ts +5404 -0
  185. package/src/generated-images.ts +740 -0
  186. package/src/index.ts +9963 -1465
  187. package/src/insights.ts +251 -23
  188. package/src/integration-bindings.ts +455 -0
  189. package/src/integration-facets.ts +862 -0
  190. package/src/interaction-revisions.ts +70 -0
  191. package/src/interaction-schema.ts +1606 -0
  192. package/src/knowledge-source-sync-schema.ts +192 -0
  193. package/src/knowledge-source-sync.ts +1922 -0
  194. package/src/memory-slack-delivery.ts +801 -0
  195. package/src/pack-components.ts +954 -0
  196. package/src/pack-installations.ts +886 -0
  197. package/src/persistence-errors.ts +4 -7
  198. package/src/plugin-packages.ts +1186 -0
  199. package/src/provision-roles.ts +534 -1
  200. package/src/runtime-posture-cli.ts +9 -0
  201. package/src/runtime-posture.ts +721 -5
  202. package/src/schema.ts +6270 -3159
  203. package/src/scoped-knowledge-schema.ts +1 -1
  204. package/src/scoped-knowledge.ts +655 -3
  205. package/src/session-control.ts +130 -42
  206. package/src/session-queue-commands.ts +265 -15
  207. package/src/session-realtime-context.ts +2 -2
  208. package/src/session-realtime-ledger.ts +3 -3
  209. package/src/session-realtime.ts +7 -7
  210. package/src/session-tenancy.ts +188 -0
  211. package/src/session-tool-call-settlement.ts +18 -3
  212. package/src/slack-task-policy-schema.ts +130 -0
  213. package/src/slack-task-policy.ts +341 -0
  214. package/src/slack-user-link-access.ts +702 -0
  215. package/src/task-notes-schema.ts +135 -0
  216. package/src/task-notes.ts +178 -0
  217. package/src/video-generation.ts +1530 -0
  218. package/src/workspace-artifacts.ts +1 -1
  219. package/src/workspace-learning-policy-schema.ts +139 -0
  220. package/src/workspace-learning-policy.ts +595 -0
  221. package/src/xai-subscription.ts +1887 -0
  222. package/dist/chunk-P6CUHXQZ.js +0 -7248
  223. package/dist/chunk-P6CUHXQZ.js.map +0 -1
  224. package/dist/chunk-VHBFHMPC.js +0 -1219
  225. package/dist/chunk-VHBFHMPC.js.map +0 -1
@@ -0,0 +1,954 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import {
4
+ stableJson,
5
+ type CapabilityPackComponentReference,
6
+ type PackComponentResolution,
7
+ } from "@opengeni/contracts";
8
+ import { and, asc, eq, inArray, ne, notInArray, or, sql } from "drizzle-orm";
9
+
10
+ import {
11
+ cleanupOrphanedCapabilityComponents,
12
+ effectiveCapabilityOwnerSql,
13
+ } from "./capability-components";
14
+ import {
15
+ listInstalledApiIntegrationsInRlsContext,
16
+ type ApiIntegrationRuntime,
17
+ } from "./capability-integrations";
18
+ import { withRlsContext, withWorkspaceRls, type Database } from "./database";
19
+ import {
20
+ addIntegrationFacetBindingOwner,
21
+ removeIntegrationFacetBindingOwner,
22
+ removeIntegrationFacetBindingOwnersForOwner,
23
+ } from "./integration-bindings";
24
+ import * as schema from "./schema";
25
+
26
+ type PackComponentKind = PackComponentResolution["kind"];
27
+
28
+ export type PackInlineSkillRequirement = {
29
+ key: string;
30
+ capabilityId: string;
31
+ name: string;
32
+ contentSha256: string;
33
+ };
34
+
35
+ type ResolvedPackComponentTarget = {
36
+ resolution: PackComponentResolution;
37
+ facetInstallationIds: string[];
38
+ bindingIds: string[];
39
+ metadata: Record<string, unknown>;
40
+ };
41
+
42
+ export type StoredPackInstallationComponent = {
43
+ id: string;
44
+ packInstallationId: string;
45
+ key: string;
46
+ kind: PackComponentKind;
47
+ capabilityId: string;
48
+ resolvedId: string;
49
+ digest: string;
50
+ metadata: Record<string, unknown>;
51
+ };
52
+
53
+ export class PackComponentResolutionError extends Error {
54
+ readonly name = "PackComponentResolutionError";
55
+
56
+ constructor(
57
+ readonly componentKey: string,
58
+ readonly status: "missing" | "mismatch",
59
+ ) {
60
+ super(`Pack component ${componentKey} is ${status}`);
61
+ }
62
+ }
63
+
64
+ export async function resolvePackComponentReferences(
65
+ db: Database,
66
+ workspaceId: string,
67
+ references: readonly CapabilityPackComponentReference[],
68
+ ): Promise<PackComponentResolution[]> {
69
+ return await withWorkspaceRls(
70
+ db,
71
+ workspaceId,
72
+ async (scopedDb) =>
73
+ await resolvePackComponentTargets(scopedDb, workspaceId, references).then((targets) =>
74
+ targets.map((target) => target.resolution),
75
+ ),
76
+ );
77
+ }
78
+
79
+ /**
80
+ * Resolve inline Pack Skills against effective owners by case-insensitive
81
+ * runtime name. Exact content is shareable; different content under the same
82
+ * active name is a conflict. The current Pack owner may be excluded so an
83
+ * update can replace its own previous inline Skill safely.
84
+ */
85
+ export async function resolvePackInlineSkillReferences(
86
+ db: Database,
87
+ workspaceId: string,
88
+ requirements: readonly PackInlineSkillRequirement[],
89
+ excludePackInstallationId?: string,
90
+ ): Promise<PackComponentResolution[]> {
91
+ if (requirements.length === 0) return [];
92
+ return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
93
+ const normalizedNames = [
94
+ ...new Set(requirements.map((requirement) => requirement.name.toLowerCase())),
95
+ ];
96
+ const rows = await scopedDb
97
+ .select({
98
+ facetInstallationId: schema.capabilityFacetInstallations.id,
99
+ name: schema.capabilitySkillFacets.name,
100
+ contentSha256: schema.capabilitySkillFacets.contentSha256,
101
+ })
102
+ .from(schema.capabilitySkillFacets)
103
+ .innerJoin(
104
+ schema.capabilityFacetInstallations,
105
+ eq(schema.capabilityFacetInstallations.facetId, schema.capabilitySkillFacets.facetId),
106
+ )
107
+ .innerJoin(
108
+ schema.capabilityPluginInstallations,
109
+ eq(
110
+ schema.capabilityPluginInstallations.id,
111
+ schema.capabilityFacetInstallations.pluginInstallationId,
112
+ ),
113
+ )
114
+ .where(
115
+ and(
116
+ eq(schema.capabilityFacetInstallations.workspaceId, workspaceId),
117
+ eq(schema.capabilityFacetInstallations.status, "active"),
118
+ eq(schema.capabilityPluginInstallations.status, "active"),
119
+ inArray(sql<string>`lower(${schema.capabilitySkillFacets.name})`, normalizedNames),
120
+ sql`exists (
121
+ select 1 from ${schema.capabilityComponentOwners} owner
122
+ where owner.facet_installation_id = ${schema.capabilityFacetInstallations.id}
123
+ and ${effectiveCapabilityOwnerSql(sql`owner.owner_kind`, sql`owner.owner_id`)}
124
+ and ${
125
+ excludePackInstallationId
126
+ ? sql`not (
127
+ owner.owner_kind = 'pack'
128
+ and owner.owner_id = ${excludePackInstallationId}
129
+ )`
130
+ : sql`true`
131
+ }
132
+ )`,
133
+ ),
134
+ )
135
+ .orderBy(
136
+ asc(schema.capabilitySkillFacets.name),
137
+ asc(schema.capabilitySkillFacets.contentSha256),
138
+ );
139
+ return requirements.map((requirement) => {
140
+ const candidates = rows.filter(
141
+ (row) => row.name.toLowerCase() === requirement.name.toLowerCase(),
142
+ );
143
+ const exact = candidates.find(
144
+ (candidate) => candidate.contentSha256 === requirement.contentSha256,
145
+ );
146
+ const mismatch = candidates[0] ?? null;
147
+ return {
148
+ key: requirement.key,
149
+ kind: "inline_skill" as const,
150
+ capabilityId: requirement.capabilityId,
151
+ required: true,
152
+ status: candidates.length === 0 || exact ? "ready" : "mismatch",
153
+ expectedDigest: requirement.contentSha256,
154
+ actualDigest: exact?.contentSha256 ?? mismatch?.contentSha256 ?? requirement.contentSha256,
155
+ resolvedId:
156
+ candidates.length === 0 ? requirement.capabilityId : (exact?.facetInstallationId ?? null),
157
+ label: requirement.name,
158
+ };
159
+ });
160
+ });
161
+ }
162
+
163
+ export async function adoptPackComponentReferences(
164
+ db: Database,
165
+ input: {
166
+ accountId: string;
167
+ workspaceId: string;
168
+ packInstallationId: string;
169
+ references: readonly CapabilityPackComponentReference[];
170
+ },
171
+ ): Promise<{
172
+ components: StoredPackInstallationComponent[];
173
+ retainedFacetInstallationIds: string[];
174
+ retainedBindingIds: string[];
175
+ }> {
176
+ return await withRlsContext(
177
+ db,
178
+ { accountId: input.accountId, workspaceId: input.workspaceId },
179
+ async (scopedDb) =>
180
+ await scopedDb.transaction(async (txRaw) => {
181
+ const tx = txRaw as unknown as Database;
182
+ await tx.execute(
183
+ sql`select pg_advisory_xact_lock(hashtextextended(${`pack-components:${input.workspaceId}:${input.packInstallationId}`}, 0))`,
184
+ );
185
+ const [installation] = await tx
186
+ .select({ id: schema.packInstallations.id })
187
+ .from(schema.packInstallations)
188
+ .where(
189
+ and(
190
+ eq(schema.packInstallations.workspaceId, input.workspaceId),
191
+ eq(schema.packInstallations.id, input.packInstallationId),
192
+ ),
193
+ )
194
+ .for("update")
195
+ .limit(1);
196
+ if (!installation) throw new Error("Pack installation was not found");
197
+
198
+ const targets = await resolvePackComponentTargets(tx, input.workspaceId, input.references);
199
+ const retainedFacetInstallationIds: string[] = [];
200
+ const retainedBindingIds: string[] = [];
201
+ const components: StoredPackInstallationComponent[] = [];
202
+ for (const target of targets) {
203
+ if (target.resolution.status !== "ready") {
204
+ if (target.resolution.required) {
205
+ throw new PackComponentResolutionError(
206
+ target.resolution.key,
207
+ target.resolution.status,
208
+ );
209
+ }
210
+ continue;
211
+ }
212
+ for (const facetInstallationId of target.facetInstallationIds) {
213
+ await tx
214
+ .insert(schema.capabilityComponentOwners)
215
+ .values({
216
+ accountId: input.accountId,
217
+ workspaceId: input.workspaceId,
218
+ facetInstallationId,
219
+ ownerKind: "pack",
220
+ ownerId: input.packInstallationId,
221
+ removable: true,
222
+ })
223
+ .onConflictDoNothing();
224
+ retainedFacetInstallationIds.push(facetInstallationId);
225
+ }
226
+ for (const bindingId of target.bindingIds) {
227
+ await addIntegrationFacetBindingOwner(tx, bindingId, {
228
+ accountId: input.accountId,
229
+ workspaceId: input.workspaceId,
230
+ owner: { kind: "pack", id: input.packInstallationId, removable: true },
231
+ });
232
+ retainedBindingIds.push(bindingId);
233
+ }
234
+ const [row] = await tx
235
+ .insert(schema.packInstallationComponents)
236
+ .values({
237
+ accountId: input.accountId,
238
+ workspaceId: input.workspaceId,
239
+ packInstallationId: input.packInstallationId,
240
+ componentKey: target.resolution.key,
241
+ kind: target.resolution.kind,
242
+ capabilityId: target.resolution.capabilityId,
243
+ resolvedId: target.resolution.resolvedId!,
244
+ digest: target.resolution.actualDigest!,
245
+ metadata: {
246
+ ...target.metadata,
247
+ facetInstallationIds: [...new Set(target.facetInstallationIds)],
248
+ bindingIds: [...new Set(target.bindingIds)],
249
+ },
250
+ updatedAt: new Date(),
251
+ })
252
+ .onConflictDoUpdate({
253
+ target: [
254
+ schema.packInstallationComponents.packInstallationId,
255
+ schema.packInstallationComponents.componentKey,
256
+ ],
257
+ set: {
258
+ kind: target.resolution.kind,
259
+ capabilityId: target.resolution.capabilityId,
260
+ resolvedId: target.resolution.resolvedId!,
261
+ digest: target.resolution.actualDigest!,
262
+ metadata: {
263
+ ...target.metadata,
264
+ facetInstallationIds: [...new Set(target.facetInstallationIds)],
265
+ bindingIds: [...new Set(target.bindingIds)],
266
+ },
267
+ updatedAt: new Date(),
268
+ },
269
+ })
270
+ .returning();
271
+ if (!row) throw new Error(`Failed to record Pack component ${target.resolution.key}`);
272
+ components.push(mapStoredPackComponent(row));
273
+ }
274
+ return {
275
+ components,
276
+ retainedFacetInstallationIds: [...new Set(retainedFacetInstallationIds)],
277
+ retainedBindingIds: [...new Set(retainedBindingIds)],
278
+ };
279
+ }),
280
+ );
281
+ }
282
+
283
+ export async function listPackInstallationComponents(
284
+ db: Database,
285
+ workspaceId: string,
286
+ packInstallationId: string,
287
+ ): Promise<StoredPackInstallationComponent[]> {
288
+ return await withWorkspaceRls(
289
+ db,
290
+ workspaceId,
291
+ async (scopedDb) =>
292
+ await listPackInstallationComponentsInRlsContext(scopedDb, workspaceId, packInstallationId),
293
+ );
294
+ }
295
+
296
+ export async function recordPackInlineSkillComponent(
297
+ db: Database,
298
+ input: {
299
+ accountId: string;
300
+ workspaceId: string;
301
+ packInstallationId: string;
302
+ componentKey: string;
303
+ capabilityId: string;
304
+ facetInstallationId: string;
305
+ contentSha256: string;
306
+ name: string;
307
+ },
308
+ ): Promise<StoredPackInstallationComponent> {
309
+ return await withRlsContext(
310
+ db,
311
+ { accountId: input.accountId, workspaceId: input.workspaceId },
312
+ async (scopedDb) => {
313
+ const [row] = await scopedDb
314
+ .insert(schema.packInstallationComponents)
315
+ .values({
316
+ accountId: input.accountId,
317
+ workspaceId: input.workspaceId,
318
+ packInstallationId: input.packInstallationId,
319
+ componentKey: input.componentKey,
320
+ kind: "inline_skill",
321
+ capabilityId: input.capabilityId,
322
+ resolvedId: input.facetInstallationId,
323
+ digest: input.contentSha256,
324
+ metadata: {
325
+ name: input.name,
326
+ facetInstallationIds: [input.facetInstallationId],
327
+ bindingIds: [],
328
+ },
329
+ updatedAt: new Date(),
330
+ })
331
+ .onConflictDoUpdate({
332
+ target: [
333
+ schema.packInstallationComponents.packInstallationId,
334
+ schema.packInstallationComponents.componentKey,
335
+ ],
336
+ set: {
337
+ kind: "inline_skill",
338
+ capabilityId: input.capabilityId,
339
+ resolvedId: input.facetInstallationId,
340
+ digest: input.contentSha256,
341
+ metadata: {
342
+ name: input.name,
343
+ facetInstallationIds: [input.facetInstallationId],
344
+ bindingIds: [],
345
+ },
346
+ updatedAt: new Date(),
347
+ },
348
+ })
349
+ .returning();
350
+ if (!row) throw new Error(`Failed to record inline Pack Skill ${input.componentKey}`);
351
+ return mapStoredPackComponent(row);
352
+ },
353
+ );
354
+ }
355
+
356
+ export async function finalizePackComponentOwnership(
357
+ db: Database,
358
+ input: {
359
+ accountId: string;
360
+ workspaceId: string;
361
+ packInstallationId: string;
362
+ retainedComponentKeys: string[];
363
+ retainedFacetInstallationIds: string[];
364
+ retainedBindingIds: string[];
365
+ },
366
+ ): Promise<void> {
367
+ await withRlsContext(
368
+ db,
369
+ { accountId: input.accountId, workspaceId: input.workspaceId },
370
+ async (scopedDb) =>
371
+ await scopedDb.transaction(async (txRaw) => {
372
+ const tx = txRaw as unknown as Database;
373
+ const retainedFacets = new Set(input.retainedFacetInstallationIds);
374
+ const ownedFacets = await tx
375
+ .select({
376
+ id: schema.capabilityComponentOwners.id,
377
+ facetInstallationId: schema.capabilityComponentOwners.facetInstallationId,
378
+ })
379
+ .from(schema.capabilityComponentOwners)
380
+ .where(
381
+ and(
382
+ eq(schema.capabilityComponentOwners.workspaceId, input.workspaceId),
383
+ eq(schema.capabilityComponentOwners.ownerKind, "pack"),
384
+ eq(schema.capabilityComponentOwners.ownerId, input.packInstallationId),
385
+ ),
386
+ );
387
+ const staleFacets = ownedFacets.filter(
388
+ (owner) => !retainedFacets.has(owner.facetInstallationId),
389
+ );
390
+ if (staleFacets.length > 0) {
391
+ await tx.delete(schema.capabilityComponentOwners).where(
392
+ inArray(
393
+ schema.capabilityComponentOwners.id,
394
+ staleFacets.map((owner) => owner.id),
395
+ ),
396
+ );
397
+ await cleanupOrphanedCapabilityComponents(
398
+ tx,
399
+ input.workspaceId,
400
+ staleFacets.map((owner) => owner.facetInstallationId),
401
+ );
402
+ }
403
+ const retainedBindings = new Set(input.retainedBindingIds);
404
+ const ownedBindings = await tx
405
+ .select({ bindingId: schema.integrationFacetBindingOwners.bindingId })
406
+ .from(schema.integrationFacetBindingOwners)
407
+ .where(
408
+ and(
409
+ eq(schema.integrationFacetBindingOwners.workspaceId, input.workspaceId),
410
+ eq(schema.integrationFacetBindingOwners.ownerKind, "pack"),
411
+ eq(schema.integrationFacetBindingOwners.ownerId, input.packInstallationId),
412
+ ),
413
+ );
414
+ for (const bindingId of [
415
+ ...new Set(
416
+ ownedBindings
417
+ .map((owner) => owner.bindingId)
418
+ .filter((ownedBindingId) => !retainedBindings.has(ownedBindingId)),
419
+ ),
420
+ ]) {
421
+ await removeIntegrationFacetBindingOwner(tx, {
422
+ workspaceId: input.workspaceId,
423
+ bindingId,
424
+ owner: { kind: "pack", id: input.packInstallationId },
425
+ });
426
+ }
427
+ const retainedKeys = [...new Set(input.retainedComponentKeys)];
428
+ await tx
429
+ .delete(schema.packInstallationComponents)
430
+ .where(
431
+ and(
432
+ eq(schema.packInstallationComponents.workspaceId, input.workspaceId),
433
+ eq(schema.packInstallationComponents.packInstallationId, input.packInstallationId),
434
+ retainedKeys.length > 0
435
+ ? notInArray(schema.packInstallationComponents.componentKey, retainedKeys)
436
+ : sql`true`,
437
+ ),
438
+ );
439
+ }),
440
+ );
441
+ }
442
+
443
+ export async function previewPackComponentRelease(
444
+ db: Database,
445
+ workspaceId: string,
446
+ packInstallationId: string,
447
+ ): Promise<
448
+ Array<{
449
+ key: string;
450
+ kind: PackComponentKind;
451
+ capabilityId: string;
452
+ retainedByOtherOwners: boolean;
453
+ }>
454
+ > {
455
+ return await withWorkspaceRls(
456
+ db,
457
+ workspaceId,
458
+ async (scopedDb) =>
459
+ await previewPackComponentReleaseInRlsContext(scopedDb, workspaceId, packInstallationId),
460
+ );
461
+ }
462
+
463
+ async function previewPackComponentReleaseInRlsContext(
464
+ scopedDb: Database,
465
+ workspaceId: string,
466
+ packInstallationId: string,
467
+ ): Promise<
468
+ Array<{
469
+ key: string;
470
+ kind: PackComponentKind;
471
+ capabilityId: string;
472
+ retainedByOtherOwners: boolean;
473
+ }>
474
+ > {
475
+ const components = await listPackInstallationComponentsInRlsContext(
476
+ scopedDb,
477
+ workspaceId,
478
+ packInstallationId,
479
+ );
480
+ const result = [];
481
+ for (const component of components) {
482
+ const facetIds = stringArray(component.metadata.facetInstallationIds);
483
+ const bindingIds = stringArray(component.metadata.bindingIds);
484
+ const [facetOwner] =
485
+ facetIds.length === 0
486
+ ? []
487
+ : await scopedDb
488
+ .select({ id: schema.capabilityComponentOwners.id })
489
+ .from(schema.capabilityComponentOwners)
490
+ .where(
491
+ and(
492
+ inArray(schema.capabilityComponentOwners.facetInstallationId, facetIds),
493
+ effectiveCapabilityOwnerSql(
494
+ schema.capabilityComponentOwners.ownerKind,
495
+ schema.capabilityComponentOwners.ownerId,
496
+ ),
497
+ or(
498
+ ne(schema.capabilityComponentOwners.ownerKind, "pack"),
499
+ ne(schema.capabilityComponentOwners.ownerId, packInstallationId),
500
+ ),
501
+ ),
502
+ )
503
+ .limit(1);
504
+ const [bindingOwner] =
505
+ bindingIds.length === 0
506
+ ? []
507
+ : await scopedDb
508
+ .select({ id: schema.integrationFacetBindingOwners.id })
509
+ .from(schema.integrationFacetBindingOwners)
510
+ .where(
511
+ and(
512
+ inArray(schema.integrationFacetBindingOwners.bindingId, bindingIds),
513
+ effectiveCapabilityOwnerSql(
514
+ schema.integrationFacetBindingOwners.ownerKind,
515
+ schema.integrationFacetBindingOwners.ownerId,
516
+ ),
517
+ or(
518
+ ne(schema.integrationFacetBindingOwners.ownerKind, "pack"),
519
+ ne(schema.integrationFacetBindingOwners.ownerId, packInstallationId),
520
+ ),
521
+ ),
522
+ )
523
+ .limit(1);
524
+ result.push({
525
+ key: component.key,
526
+ kind: component.kind,
527
+ capabilityId: component.capabilityId,
528
+ retainedByOtherOwners: Boolean(facetOwner || bindingOwner),
529
+ });
530
+ }
531
+ return result;
532
+ }
533
+
534
+ export async function releasePackComponents(
535
+ db: Database,
536
+ input: { accountId: string; workspaceId: string; packInstallationId: string },
537
+ ): Promise<{ retainedComponents: string[] }> {
538
+ return await withRlsContext(
539
+ db,
540
+ { accountId: input.accountId, workspaceId: input.workspaceId },
541
+ async (scopedDb) =>
542
+ await scopedDb.transaction(async (txRaw) => {
543
+ const tx = txRaw as unknown as Database;
544
+ const preview = await previewPackComponentReleaseInRlsContext(
545
+ tx,
546
+ input.workspaceId,
547
+ input.packInstallationId,
548
+ );
549
+ await removeIntegrationFacetBindingOwnersForOwner(tx, {
550
+ workspaceId: input.workspaceId,
551
+ owner: { kind: "pack", id: input.packInstallationId },
552
+ });
553
+ const deletedOwners = await tx
554
+ .delete(schema.capabilityComponentOwners)
555
+ .where(
556
+ and(
557
+ eq(schema.capabilityComponentOwners.workspaceId, input.workspaceId),
558
+ eq(schema.capabilityComponentOwners.ownerKind, "pack"),
559
+ eq(schema.capabilityComponentOwners.ownerId, input.packInstallationId),
560
+ ),
561
+ )
562
+ .returning({ facetInstallationId: schema.capabilityComponentOwners.facetInstallationId });
563
+ await cleanupOrphanedCapabilityComponents(
564
+ tx,
565
+ input.workspaceId,
566
+ deletedOwners.map((row) => row.facetInstallationId),
567
+ );
568
+ await tx
569
+ .delete(schema.packInstallationComponents)
570
+ .where(
571
+ and(
572
+ eq(schema.packInstallationComponents.workspaceId, input.workspaceId),
573
+ eq(schema.packInstallationComponents.packInstallationId, input.packInstallationId),
574
+ ),
575
+ );
576
+ return {
577
+ retainedComponents: preview
578
+ .filter((component) => component.retainedByOtherOwners)
579
+ .map((component) => component.capabilityId),
580
+ };
581
+ }),
582
+ );
583
+ }
584
+
585
+ async function resolvePackComponentTargets(
586
+ db: Database,
587
+ workspaceId: string,
588
+ references: readonly CapabilityPackComponentReference[],
589
+ ): Promise<ResolvedPackComponentTarget[]> {
590
+ const integrations = await listInstalledApiIntegrationsInRlsContext(db, workspaceId);
591
+ const targets: ResolvedPackComponentTarget[] = [];
592
+ for (const reference of references) {
593
+ if (reference.kind === "plugin") {
594
+ targets.push(await resolvePluginReference(db, workspaceId, reference));
595
+ } else if (reference.kind === "skill") {
596
+ targets.push(await resolveSkillReference(db, workspaceId, reference));
597
+ } else if (reference.kind === "integration") {
598
+ targets.push(await resolveIntegrationReference(db, workspaceId, reference, integrations));
599
+ } else {
600
+ targets.push(await resolveFacetReference(db, workspaceId, reference, integrations));
601
+ }
602
+ }
603
+ return targets;
604
+ }
605
+
606
+ async function resolvePluginReference(
607
+ db: Database,
608
+ workspaceId: string,
609
+ reference: Extract<CapabilityPackComponentReference, { kind: "plugin" }>,
610
+ ): Promise<ResolvedPackComponentTarget> {
611
+ const [row] = await db
612
+ .select({
613
+ pluginInstallationId: schema.capabilityPluginInstallations.id,
614
+ status: schema.capabilityPluginInstallations.status,
615
+ version: schema.capabilityPluginVersions.version,
616
+ manifestDigest: schema.capabilityPluginVersions.manifestDigest,
617
+ name: schema.capabilityPlugins.name,
618
+ })
619
+ .from(schema.capabilityPluginInstallations)
620
+ .innerJoin(
621
+ schema.capabilityPlugins,
622
+ eq(schema.capabilityPlugins.id, schema.capabilityPluginInstallations.pluginId),
623
+ )
624
+ .innerJoin(
625
+ schema.capabilityPluginVersions,
626
+ eq(schema.capabilityPluginVersions.id, schema.capabilityPluginInstallations.pluginVersionId),
627
+ )
628
+ .where(
629
+ and(
630
+ eq(schema.capabilityPluginInstallations.workspaceId, workspaceId),
631
+ eq(schema.capabilityPlugins.pluginKey, reference.pluginKey),
632
+ ),
633
+ )
634
+ .limit(1);
635
+ const expectedDigest = reference.manifestDigest;
636
+ const exact =
637
+ row?.status === "active" &&
638
+ row.version === reference.version &&
639
+ row.manifestDigest === reference.manifestDigest;
640
+ const facetInstallationIds = !row
641
+ ? []
642
+ : (
643
+ await db
644
+ .select({ id: schema.capabilityComponentOwners.facetInstallationId })
645
+ .from(schema.capabilityComponentOwners)
646
+ .where(
647
+ and(
648
+ eq(schema.capabilityComponentOwners.workspaceId, workspaceId),
649
+ eq(schema.capabilityComponentOwners.ownerKind, "plugin"),
650
+ eq(schema.capabilityComponentOwners.ownerId, row.pluginInstallationId),
651
+ ),
652
+ )
653
+ ).map((entry) => entry.id);
654
+ const bindingIds = !row
655
+ ? []
656
+ : (
657
+ await db
658
+ .select({ id: schema.integrationFacetBindingOwners.bindingId })
659
+ .from(schema.integrationFacetBindingOwners)
660
+ .where(
661
+ and(
662
+ eq(schema.integrationFacetBindingOwners.workspaceId, workspaceId),
663
+ eq(schema.integrationFacetBindingOwners.ownerKind, "plugin"),
664
+ eq(schema.integrationFacetBindingOwners.ownerId, row.pluginInstallationId),
665
+ ),
666
+ )
667
+ ).map((entry) => entry.id);
668
+ return {
669
+ resolution: {
670
+ key: reference.key,
671
+ kind: "plugin",
672
+ capabilityId: `plugin:${reference.pluginKey}`,
673
+ required: reference.required,
674
+ status: !row ? "missing" : exact ? "ready" : "mismatch",
675
+ expectedDigest,
676
+ actualDigest: row?.manifestDigest ?? null,
677
+ resolvedId: exact ? row.pluginInstallationId : null,
678
+ label: row?.name ?? reference.pluginKey,
679
+ },
680
+ facetInstallationIds: exact ? facetInstallationIds : [],
681
+ bindingIds: exact ? bindingIds : [],
682
+ metadata: { pluginKey: reference.pluginKey, version: reference.version },
683
+ };
684
+ }
685
+
686
+ async function resolveSkillReference(
687
+ db: Database,
688
+ workspaceId: string,
689
+ reference: Extract<CapabilityPackComponentReference, { kind: "skill" }>,
690
+ ): Promise<ResolvedPackComponentTarget> {
691
+ const rows = await db
692
+ .select({
693
+ facetInstallationId: schema.capabilityFacetInstallations.id,
694
+ contentSha256: schema.capabilitySkillFacets.contentSha256,
695
+ name: schema.capabilitySkillFacets.name,
696
+ })
697
+ .from(schema.capabilitySkillFacets)
698
+ .innerJoin(
699
+ schema.capabilityFacetInstallations,
700
+ eq(schema.capabilityFacetInstallations.facetId, schema.capabilitySkillFacets.facetId),
701
+ )
702
+ .innerJoin(
703
+ schema.capabilityPluginInstallations,
704
+ eq(
705
+ schema.capabilityPluginInstallations.id,
706
+ schema.capabilityFacetInstallations.pluginInstallationId,
707
+ ),
708
+ )
709
+ .where(
710
+ and(
711
+ eq(schema.capabilityFacetInstallations.workspaceId, workspaceId),
712
+ eq(schema.capabilitySkillFacets.capabilityId, reference.capabilityId),
713
+ eq(schema.capabilityFacetInstallations.status, "active"),
714
+ eq(schema.capabilityPluginInstallations.status, "active"),
715
+ sql`exists (
716
+ select 1 from ${schema.capabilityComponentOwners} owner
717
+ where owner.facet_installation_id = ${schema.capabilityFacetInstallations.id}
718
+ and ${effectiveCapabilityOwnerSql(sql`owner.owner_kind`, sql`owner.owner_id`)}
719
+ )`,
720
+ ),
721
+ )
722
+ .limit(8);
723
+ const exact = rows.find((row) => row.contentSha256 === reference.contentSha256);
724
+ const actual = exact ?? rows[0] ?? null;
725
+ return {
726
+ resolution: {
727
+ key: reference.key,
728
+ kind: "skill",
729
+ capabilityId: reference.capabilityId,
730
+ required: reference.required,
731
+ status: rows.length === 0 ? "missing" : exact ? "ready" : "mismatch",
732
+ expectedDigest: reference.contentSha256,
733
+ actualDigest: actual?.contentSha256 ?? null,
734
+ resolvedId: exact?.facetInstallationId ?? null,
735
+ label: actual?.name ?? reference.capabilityId,
736
+ },
737
+ facetInstallationIds: exact ? [exact.facetInstallationId] : [],
738
+ bindingIds: [],
739
+ metadata: {},
740
+ };
741
+ }
742
+
743
+ async function resolveIntegrationReference(
744
+ db: Database,
745
+ workspaceId: string,
746
+ reference: Extract<CapabilityPackComponentReference, { kind: "integration" }>,
747
+ integrations: readonly ApiIntegrationRuntime[],
748
+ ): Promise<ResolvedPackComponentTarget> {
749
+ const candidates = integrations.filter(
750
+ (integration) =>
751
+ integration.capabilityId === reference.capabilityId &&
752
+ integration.instanceKey === reference.instanceKey,
753
+ );
754
+ const exact = candidates.find(
755
+ (integration) =>
756
+ integration.revision.id === reference.revisionId &&
757
+ integration.revision.contentSha256 === reference.contentSha256,
758
+ );
759
+ const actual = exact ?? candidates[0] ?? null;
760
+ const facetInstallationIds = exact
761
+ ? await integrationFacetInstallationIds(db, exact.pluginInstallationId)
762
+ : [];
763
+ return {
764
+ resolution: {
765
+ key: reference.key,
766
+ kind: "integration",
767
+ capabilityId: reference.capabilityId,
768
+ required: reference.required,
769
+ status: candidates.length === 0 ? "missing" : exact ? "ready" : "mismatch",
770
+ expectedDigest: reference.contentSha256,
771
+ actualDigest: actual?.revision.contentSha256 ?? null,
772
+ resolvedId: exact?.instanceId ?? null,
773
+ label: actual?.displayName ?? reference.capabilityId,
774
+ },
775
+ facetInstallationIds,
776
+ bindingIds: exact ? [exact.instanceId] : [],
777
+ metadata: {
778
+ instanceKey: reference.instanceKey,
779
+ revisionId: reference.revisionId,
780
+ },
781
+ };
782
+ }
783
+
784
+ async function resolveFacetReference(
785
+ db: Database,
786
+ workspaceId: string,
787
+ reference: Extract<CapabilityPackComponentReference, { kind: "facet" }>,
788
+ integrations: readonly ApiIntegrationRuntime[],
789
+ ): Promise<ResolvedPackComponentTarget> {
790
+ const integration = integrations.find(
791
+ (candidate) =>
792
+ candidate.capabilityId === reference.capabilityId &&
793
+ candidate.instanceKey === reference.instanceKey,
794
+ );
795
+ if (!integration) {
796
+ return missingTarget(reference, "facet", reference.capabilityId, reference.configDigest);
797
+ }
798
+ const [integrationFacetInstallationId] = await integrationFacetInstallationIds(
799
+ db,
800
+ integration.pluginInstallationId,
801
+ "integration",
802
+ );
803
+ if (!integrationFacetInstallationId) {
804
+ return missingTarget(reference, "facet", reference.capabilityId, reference.configDigest);
805
+ }
806
+ const [binding] = await db
807
+ .select({
808
+ id: schema.integrationFacetBindings.id,
809
+ config: schema.integrationFacetBindings.config,
810
+ status: schema.integrationFacetBindings.status,
811
+ displayName: schema.integrationFacetBindings.displayName,
812
+ })
813
+ .from(schema.integrationFacetBindings)
814
+ .innerJoin(
815
+ schema.integrationFacetDefinitions,
816
+ eq(schema.integrationFacetDefinitions.id, schema.integrationFacetBindings.facetDefinitionId),
817
+ )
818
+ .where(
819
+ and(
820
+ eq(schema.integrationFacetBindings.workspaceId, workspaceId),
821
+ eq(
822
+ schema.integrationFacetBindings.integrationFacetInstallationId,
823
+ integrationFacetInstallationId,
824
+ ),
825
+ eq(schema.integrationFacetDefinitions.facetKey, reference.facetKey),
826
+ eq(schema.integrationFacetBindings.bindingKey, reference.bindingKey),
827
+ ),
828
+ )
829
+ .limit(1);
830
+ const actualDigest = binding ? sha256(stableJson(binding.config)) : null;
831
+ const exact =
832
+ binding?.status === "active" &&
833
+ actualDigest !== null &&
834
+ actualDigest === reference.configDigest;
835
+ return {
836
+ resolution: {
837
+ key: reference.key,
838
+ kind: "facet",
839
+ capabilityId: reference.capabilityId,
840
+ required: reference.required,
841
+ status: !binding ? "missing" : exact ? "ready" : "mismatch",
842
+ expectedDigest: reference.configDigest,
843
+ actualDigest,
844
+ resolvedId: exact ? binding.id : null,
845
+ label: binding?.displayName ?? reference.facetKey,
846
+ },
847
+ facetInstallationIds: exact ? [integrationFacetInstallationId] : [],
848
+ bindingIds: exact ? [binding.id] : [],
849
+ metadata: {
850
+ instanceKey: reference.instanceKey,
851
+ facetKey: reference.facetKey,
852
+ bindingKey: reference.bindingKey,
853
+ },
854
+ };
855
+ }
856
+
857
+ async function integrationFacetInstallationIds(
858
+ db: Database,
859
+ pluginInstallationId: string,
860
+ onlyKind?: "integration" | "api",
861
+ ): Promise<string[]> {
862
+ const rows = await db
863
+ .select({ id: schema.capabilityFacetInstallations.id, kind: schema.capabilityFacets.kind })
864
+ .from(schema.capabilityFacetInstallations)
865
+ .innerJoin(
866
+ schema.capabilityFacets,
867
+ eq(schema.capabilityFacets.id, schema.capabilityFacetInstallations.facetId),
868
+ )
869
+ .where(
870
+ and(
871
+ eq(schema.capabilityFacetInstallations.pluginInstallationId, pluginInstallationId),
872
+ onlyKind
873
+ ? eq(schema.capabilityFacets.kind, onlyKind)
874
+ : inArray(schema.capabilityFacets.kind, ["integration", "api"]),
875
+ ),
876
+ );
877
+ return rows.map((row) => row.id);
878
+ }
879
+
880
+ async function listPackInstallationComponentsInRlsContext(
881
+ scopedDb: Database,
882
+ workspaceId: string,
883
+ packInstallationId: string,
884
+ ): Promise<StoredPackInstallationComponent[]> {
885
+ const rows = await scopedDb
886
+ .select()
887
+ .from(schema.packInstallationComponents)
888
+ .where(
889
+ and(
890
+ eq(schema.packInstallationComponents.workspaceId, workspaceId),
891
+ eq(schema.packInstallationComponents.packInstallationId, packInstallationId),
892
+ ),
893
+ )
894
+ .orderBy(asc(schema.packInstallationComponents.componentKey));
895
+ return rows.map(mapStoredPackComponent);
896
+ }
897
+
898
+ function missingTarget(
899
+ reference: { key: string; required: boolean },
900
+ kind: PackComponentKind,
901
+ capabilityId: string,
902
+ expectedDigest: string,
903
+ ): ResolvedPackComponentTarget {
904
+ return {
905
+ resolution: {
906
+ key: reference.key,
907
+ kind,
908
+ capabilityId,
909
+ required: reference.required,
910
+ status: "missing",
911
+ expectedDigest,
912
+ actualDigest: null,
913
+ resolvedId: null,
914
+ label: capabilityId,
915
+ },
916
+ facetInstallationIds: [],
917
+ bindingIds: [],
918
+ metadata: {},
919
+ };
920
+ }
921
+
922
+ function mapStoredPackComponent(
923
+ row: typeof schema.packInstallationComponents.$inferSelect,
924
+ ): StoredPackInstallationComponent {
925
+ if (
926
+ row.kind !== "plugin" &&
927
+ row.kind !== "skill" &&
928
+ row.kind !== "integration" &&
929
+ row.kind !== "facet" &&
930
+ row.kind !== "inline_skill"
931
+ ) {
932
+ throw new Error(`Unknown Pack component kind: ${row.kind}`);
933
+ }
934
+ return {
935
+ id: row.id,
936
+ packInstallationId: row.packInstallationId,
937
+ key: row.componentKey,
938
+ kind: row.kind,
939
+ capabilityId: row.capabilityId,
940
+ resolvedId: row.resolvedId,
941
+ digest: row.digest,
942
+ metadata: row.metadata,
943
+ };
944
+ }
945
+
946
+ function stringArray(value: unknown): string[] {
947
+ return Array.isArray(value)
948
+ ? value.filter((entry): entry is string => typeof entry === "string")
949
+ : [];
950
+ }
951
+
952
+ function sha256(value: string): string {
953
+ return createHash("sha256").update(value).digest("hex");
954
+ }