@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.
- package/LICENSE +176 -0
- package/README.md +23 -4
- package/dist/approval-store.d.ts +52 -0
- package/dist/approval-store.js +79 -0
- package/dist/asset-resolution.d.ts +52 -0
- package/dist/asset-resolution.js +66 -0
- package/dist/catalog-resolution.d.ts +76 -0
- package/dist/catalog-resolution.js +151 -0
- package/dist/client.d.ts +19 -0
- package/dist/client.js +18 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +16 -0
- package/dist/connection.d.ts +3 -0
- package/dist/connection.js +26 -0
- package/dist/credential-resolution.d.ts +222 -0
- package/dist/credential-resolution.js +255 -0
- package/dist/grant-store.d.ts +3 -0
- package/dist/grant-store.js +59 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +20 -0
- package/dist/migrate.d.ts +36 -0
- package/dist/migrate.js +135 -0
- package/dist/model-source-resolution.d.ts +115 -0
- package/dist/model-source-resolution.js +291 -0
- package/dist/parse-row.d.ts +373 -0
- package/dist/parse-row.js +232 -0
- package/dist/pg-error.d.ts +3 -0
- package/dist/pg-error.js +19 -0
- package/dist/pricing.d.ts +15 -0
- package/dist/pricing.js +24 -0
- package/dist/rekey-credential-secrets.d.ts +13 -0
- package/dist/rekey-credential-secrets.js +61 -0
- package/dist/schema/approvals.d.ts +245 -0
- package/dist/schema/approvals.js +52 -0
- package/dist/schema/assets.d.ts +143 -0
- package/{src/schema/assets.ts → dist/schema/assets.js} +7 -16
- package/dist/schema/auth.d.ts +606 -0
- package/dist/schema/auth.js +47 -0
- package/dist/schema/catalog.d.ts +811 -0
- package/dist/schema/catalog.js +108 -0
- package/dist/schema/column-types.d.ts +26 -0
- package/dist/schema/column-types.js +15 -0
- package/dist/schema/credentials.d.ts +305 -0
- package/{src/schema/credentials.ts → dist/schema/credentials.js} +16 -22
- package/dist/schema/git-tokens.d.ts +256 -0
- package/{src/schema/git-tokens.ts → dist/schema/git-tokens.js} +16 -39
- package/dist/schema/grants.d.ts +211 -0
- package/dist/schema/grants.js +30 -0
- package/dist/schema/index.d.ts +25 -0
- package/dist/schema/index.js +25 -0
- package/dist/schema/messages.d.ts +414 -0
- package/dist/schema/messages.js +82 -0
- package/dist/schema/oauth-clients.d.ts +246 -0
- package/{src/schema/oauth-clients.ts → dist/schema/oauth-clients.js} +8 -14
- package/dist/schema/offerings.d.ts +160 -0
- package/dist/schema/offerings.js +24 -0
- package/dist/schema/principals.d.ts +128 -0
- package/dist/schema/principals.js +23 -0
- package/dist/schema/providers.d.ts +237 -0
- package/dist/schema/providers.js +22 -0
- package/dist/schema/roles.d.ts +242 -0
- package/dist/schema/roles.js +37 -0
- package/dist/schema/session-assets.d.ts +92 -0
- package/dist/schema/session-assets.js +45 -0
- package/dist/schema/sessions.d.ts +143 -0
- package/dist/schema/sessions.js +29 -0
- package/dist/schema/sidecar-allocation.d.ts +406 -0
- package/dist/schema/sidecar-allocation.js +57 -0
- package/dist/schema/sidecar.d.ts +145 -0
- package/dist/schema/sidecar.js +28 -0
- package/dist/schema/signal-correlations.d.ts +177 -0
- package/dist/schema/signal-correlations.js +31 -0
- package/dist/schema/tenants.d.ts +235 -0
- package/{src/schema/tenants.ts → dist/schema/tenants.js} +10 -27
- package/dist/schema/wallets.d.ts +354 -0
- package/dist/schema/wallets.js +43 -0
- package/dist/schema/workflow-definitions.d.ts +371 -0
- package/dist/schema/workflow-definitions.js +97 -0
- package/dist/schema/workflow-run-dispatch.d.ts +328 -0
- package/dist/schema/workflow-run-dispatch.js +56 -0
- package/dist/schema/workflow-run-execution.d.ts +126 -0
- package/dist/schema/workflow-run-execution.js +48 -0
- package/dist/schema/workflow-run-launch-spec.d.ts +200 -0
- package/dist/schema/workflow-run-launch-spec.js +26 -0
- package/dist/schema/workflow-run.d.ts +230 -0
- package/dist/schema/workflow-run.js +102 -0
- package/dist/sidecar-allocation-store.d.ts +171 -0
- package/dist/sidecar-allocation-store.js +512 -0
- package/dist/signal-correlation-store.d.ts +36 -0
- package/dist/signal-correlation-store.js +63 -0
- package/dist/tenant-hierarchy.d.ts +23 -0
- package/dist/tenant-hierarchy.js +60 -0
- package/dist/workflow-definition-store.d.ts +55 -0
- package/dist/workflow-definition-store.js +93 -0
- package/dist/workflow-run-dispatch-store.d.ts +80 -0
- package/dist/workflow-run-dispatch-store.js +298 -0
- package/dist/workflow-run-launch-spec-store.d.ts +13 -0
- package/dist/workflow-run-launch-spec-store.js +30 -0
- package/dist/workflow-run-store.d.ts +54 -0
- package/dist/workflow-run-store.js +85 -0
- package/migrations/0029_loose_warlock.sql +1 -0
- package/migrations/0030_session_asset_audit_split.sql +5 -0
- package/migrations/0031_gigantic_nicolaos.sql +68 -0
- package/migrations/0032_lethal_misty_knight.sql +1 -0
- package/migrations/0033_old_payback.sql +1 -0
- package/migrations/0034_sleepy_songbird.sql +11 -0
- package/migrations/0035_violet_giant_man.sql +3 -0
- package/migrations/0036_sharp_the_executioner.sql +2 -0
- package/migrations/0037_credential_use_backfill.sql +39 -0
- package/migrations/0038_chilly_blacklash.sql +37 -0
- package/migrations/0039_quick_carnage.sql +2 -0
- package/migrations/0040_reshape_approval_origin.sql +24 -0
- package/migrations/0041_make_approval_timeout_at_nullable.sql +1 -0
- package/migrations/0042_youthful_mantis.sql +2 -0
- package/migrations/0043_signal_correlation_deployment_fk.sql +1 -0
- package/migrations/0044_model_offering_quirks.sql +1 -0
- package/migrations/0045_create_workflow_run.sql +13 -0
- package/migrations/0046_approval_signal_correlation_run_fk.sql +2 -0
- package/migrations/0047_create_workflow_definition.sql +30 -0
- package/migrations/0048_workflow_definition_origin_agent_id.sql +2 -0
- package/migrations/0049_curly_omega_flight.sql +2 -0
- package/migrations/0050_late_crystal.sql +10 -0
- package/migrations/0051_funny_madelyne_pryor.sql +1 -0
- package/migrations/0052_drop_inference_turn_instance_fk.sql +1 -0
- package/migrations/0053_session_mail_session_id_index.sql +1 -0
- package/migrations/0054_rekey_instance_grants_to_workflow_run.sql +10 -0
- package/migrations/0055_backfill_anchor_workflow_runs.sql +69 -0
- package/migrations/0056_repoint_deployment_fks_to_anchor_run.sql +32 -0
- package/migrations/0057_drop_workflow_deployment_projection.sql +22 -0
- package/migrations/0058_repoint_offering_fk_to_definition.sql +41 -0
- package/migrations/0059_repoint_transaction_fk_to_run.sql +10 -0
- package/migrations/0060_drop_session_mail_instance_fk.sql +6 -0
- package/migrations/0061_drop_session_asset_instance_fk.sql +7 -0
- package/migrations/0062_drop_agent_asset_table.sql +11 -0
- package/migrations/0063_repoint_agent_role_fk_to_definition.sql +39 -0
- package/migrations/0064_repoint_agent_session_fk_to_definition.sql +47 -0
- package/migrations/0065_add_workflow_definition_model_requirements.sql +37 -0
- package/migrations/0066_rekey_agent_definition_principals_to_workflow.sql +27 -0
- package/migrations/0067_add_workflow_definition_kind.sql +10 -0
- package/migrations/0068_drop_agent_tables_and_origin_agent_id.sql +31 -0
- package/migrations/0069_drop_workflow_definition_kind.sql +10 -0
- package/migrations/0070_flashy_proteus.sql +13 -0
- package/migrations/0072_add_workflow_definition_credential_bindings.sql +1 -0
- package/migrations/0073_grant_target_exactly_one_check.sql +13 -0
- package/migrations/0074_add_provider_api_base_url.sql +1 -0
- package/migrations/0075_workflow_run_launch_spec.sql +17 -0
- package/migrations/0076_sidecar_allocation.sql +40 -0
- package/migrations/0077_workflow_run_dispatch.sql +29 -0
- package/migrations/0078_workflow_run_dispatch_kind.sql +2 -0
- package/migrations/0079_rename_workflow_run_deployment_id_to_anchor_run_id.sql +4 -0
- package/migrations/0080_rename_correlation_approval_deployment_id_to_anchor_run_id.sql +9 -0
- package/migrations/0081_workflow_definition_content_hash_and_approved_wire_hash.sql +4 -0
- package/migrations/0082_blue_black_queen.sql +1 -0
- package/migrations/0083_replace_launch_spec_snapshot_with_frozen_bundle.sql +3 -0
- package/migrations/0084_delete_orphaned_credential_grants.sql +22 -0
- package/migrations/meta/0029_snapshot.json +2664 -0
- package/migrations/meta/0030_snapshot.json +2670 -0
- package/migrations/meta/0031_snapshot.json +3100 -0
- package/migrations/meta/0032_snapshot.json +3106 -0
- package/migrations/meta/0033_snapshot.json +3112 -0
- package/migrations/meta/0034_snapshot.json +3198 -0
- package/migrations/meta/0035_snapshot.json +3225 -0
- package/migrations/meta/0036_snapshot.json +3237 -0
- package/migrations/meta/0037_snapshot.json +3237 -0
- package/migrations/meta/0038_snapshot.json +3470 -0
- package/migrations/meta/0039_snapshot.json +3470 -0
- package/migrations/meta/0040_snapshot.json +3489 -0
- package/migrations/meta/0041_snapshot.json +3489 -0
- package/migrations/meta/0042_snapshot.json +3489 -0
- package/migrations/meta/0043_snapshot.json +3498 -0
- package/migrations/meta/0044_snapshot.json +3504 -0
- package/migrations/meta/0045_snapshot.json +3589 -0
- package/migrations/meta/0046_snapshot.json +3607 -0
- package/migrations/meta/0047_snapshot.json +3832 -0
- package/migrations/meta/0048_snapshot.json +3853 -0
- package/migrations/meta/0049_snapshot.json +3854 -0
- package/migrations/meta/0050_snapshot.json +3924 -0
- package/migrations/meta/0051_snapshot.json +3940 -0
- package/migrations/meta/0052_snapshot.json +3931 -0
- package/migrations/meta/0053_snapshot.json +3952 -0
- package/migrations/meta/0054_snapshot.json +3952 -0
- package/migrations/meta/0055_snapshot.json +3952 -0
- package/migrations/meta/0056_snapshot.json +3952 -0
- package/migrations/meta/0057_snapshot.json +3839 -0
- package/migrations/meta/0058_snapshot.json +3839 -0
- package/migrations/meta/0059_snapshot.json +3839 -0
- package/migrations/meta/0060_snapshot.json +3830 -0
- package/migrations/meta/0061_snapshot.json +3821 -0
- package/migrations/meta/0062_snapshot.json +3723 -0
- package/migrations/meta/0063_snapshot.json +3723 -0
- package/migrations/meta/0064_snapshot.json +3723 -0
- package/migrations/meta/0065_snapshot.json +3729 -0
- package/migrations/meta/0066_snapshot.json +3729 -0
- package/migrations/meta/0067_snapshot.json +3736 -0
- package/migrations/meta/0068_snapshot.json +3347 -0
- package/migrations/meta/0069_snapshot.json +3340 -0
- package/migrations/meta/0070_snapshot.json +3444 -0
- package/migrations/meta/0072_snapshot.json +3450 -0
- package/migrations/meta/0073_snapshot.json +3455 -0
- package/migrations/meta/0074_snapshot.json +3461 -0
- package/migrations/meta/0075_snapshot.json +3567 -0
- package/migrations/meta/0076_snapshot.json +3851 -0
- package/migrations/meta/0077_snapshot.json +4067 -0
- package/migrations/meta/0078_snapshot.json +4078 -0
- package/migrations/meta/0079_snapshot.json +4078 -0
- package/migrations/meta/0080_snapshot.json +4078 -0
- package/migrations/meta/0081_snapshot.json +4096 -0
- package/migrations/meta/0082_snapshot.json +4102 -0
- package/migrations/meta/0083_snapshot.json +4096 -0
- package/migrations/meta/0084_snapshot.json +4096 -0
- package/migrations/meta/_journal.json +385 -0
- package/package.json +25 -11
- package/drizzle.config.ts +0 -23
- package/src/client.ts +0 -24
- package/src/config.ts +0 -19
- package/src/connection.ts +0 -27
- package/src/credential-resolution.ts +0 -378
- package/src/grant-store.ts +0 -51
- package/src/index.ts +0 -32
- package/src/migrate.test.ts +0 -35
- package/src/migrate.ts +0 -168
- package/src/parse-row.test.ts +0 -113
- package/src/parse-row.ts +0 -185
- package/src/schema/agent-assets.ts +0 -21
- package/src/schema/agents.ts +0 -49
- package/src/schema/auth.ts +0 -51
- package/src/schema/grants.ts +0 -26
- package/src/schema/index.ts +0 -19
- package/src/schema/instances.ts +0 -36
- package/src/schema/messages.ts +0 -108
- package/src/schema/offerings.ts +0 -20
- package/src/schema/principals.ts +0 -28
- package/src/schema/providers.ts +0 -23
- package/src/schema/roles.ts +0 -51
- package/src/schema/session-assets.ts +0 -49
- package/src/schema/sessions.ts +0 -26
- package/src/schema/sidecar.ts +0 -14
- package/src/schema/wallets.ts +0 -44
- package/src/tenant-hierarchy.ts +0 -34
- package/tsconfig.json +0 -4
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { and, asc, eq, inArray, isNull, lte, or, sql, } from "drizzle-orm";
|
|
3
|
+
import { RunGrantsFrame, SignalDeliverFrame, } from "@intx/types/sidecar";
|
|
4
|
+
import { parseWorkflowRunDispatchRow } from "./parse-row.js";
|
|
5
|
+
import { sidecarAllocation, workflowRun, workflowRunDispatch } from "./schema/index.js";
|
|
6
|
+
export class WorkflowRunDispatchPayloadConflictError extends Error {
|
|
7
|
+
messageId;
|
|
8
|
+
payloadKind;
|
|
9
|
+
constructor(messageId, payloadKind, operation) {
|
|
10
|
+
super(`workflowRunDispatchStore.${operation}: ${payloadKind} ${messageId} conflicts with its durable payload`);
|
|
11
|
+
this.messageId = messageId;
|
|
12
|
+
this.payloadKind = payloadKind;
|
|
13
|
+
this.name = "WorkflowRunDispatchPayloadConflictError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function databaseTimestamp(override) {
|
|
17
|
+
return override ?? sql `now()`;
|
|
18
|
+
}
|
|
19
|
+
function leaseCondition(expectedLeaseId) {
|
|
20
|
+
return expectedLeaseId === undefined
|
|
21
|
+
? []
|
|
22
|
+
: [eq(workflowRunDispatch.deliveryLeaseId, expectedLeaseId)];
|
|
23
|
+
}
|
|
24
|
+
function byteArraysEqual(left, right) {
|
|
25
|
+
if (left.byteLength !== right.byteLength)
|
|
26
|
+
return false;
|
|
27
|
+
return left.every((value, index) => value === right[index]);
|
|
28
|
+
}
|
|
29
|
+
function grantsEqual(left, right) {
|
|
30
|
+
return isDeepStrictEqual(left, right);
|
|
31
|
+
}
|
|
32
|
+
function validateStepGrants(anchorRunId, stepGrants) {
|
|
33
|
+
return RunGrantsFrame.assert({
|
|
34
|
+
type: "run.grants",
|
|
35
|
+
agentAddress: "persisted@validation.invalid",
|
|
36
|
+
runId: anchorRunId,
|
|
37
|
+
stepGrants,
|
|
38
|
+
}).stepGrants;
|
|
39
|
+
}
|
|
40
|
+
async function insertOrReconcileDispatch(executor, args) {
|
|
41
|
+
const [anchor] = await executor
|
|
42
|
+
.select({ anchorRunId: workflowRun.anchorRunId })
|
|
43
|
+
.from(workflowRun)
|
|
44
|
+
.where(eq(workflowRun.id, args.anchorRunId))
|
|
45
|
+
.limit(1);
|
|
46
|
+
if (anchor?.anchorRunId !== args.anchorRunId) {
|
|
47
|
+
throw new Error(`workflowRunDispatchStore.${args.operation}: run ${args.anchorRunId} is not a deployment anchor`);
|
|
48
|
+
}
|
|
49
|
+
const now = databaseTimestamp(args.now);
|
|
50
|
+
const [inserted] = await executor
|
|
51
|
+
.insert(workflowRunDispatch)
|
|
52
|
+
.values({
|
|
53
|
+
id: args.id,
|
|
54
|
+
anchorRunId: args.anchorRunId,
|
|
55
|
+
messageId: args.messageId,
|
|
56
|
+
kind: args.kind,
|
|
57
|
+
rawMessage: args.rawMessage,
|
|
58
|
+
stepGrants: args.stepGrants,
|
|
59
|
+
status: "pending",
|
|
60
|
+
nextAttemptAt: now,
|
|
61
|
+
createdAt: now,
|
|
62
|
+
updatedAt: now,
|
|
63
|
+
})
|
|
64
|
+
.onConflictDoNothing({
|
|
65
|
+
target: [workflowRunDispatch.anchorRunId, workflowRunDispatch.messageId],
|
|
66
|
+
})
|
|
67
|
+
.returning();
|
|
68
|
+
if (inserted !== undefined) {
|
|
69
|
+
return {
|
|
70
|
+
dispatch: parseWorkflowRunDispatchRow(inserted),
|
|
71
|
+
created: true,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const existing = await executor.query.workflowRunDispatch.findFirst({
|
|
75
|
+
where: and(eq(workflowRunDispatch.anchorRunId, args.anchorRunId), eq(workflowRunDispatch.messageId, args.messageId)),
|
|
76
|
+
});
|
|
77
|
+
if (existing === undefined) {
|
|
78
|
+
throw new Error(`workflowRunDispatchStore.${args.operation}: conflicting row disappeared for ${args.anchorRunId}/${args.messageId}`);
|
|
79
|
+
}
|
|
80
|
+
const parsed = parseWorkflowRunDispatchRow(existing);
|
|
81
|
+
if (parsed.kind !== args.kind ||
|
|
82
|
+
!byteArraysEqual(parsed.rawMessage, args.rawMessage) ||
|
|
83
|
+
!grantsEqual(parsed.stepGrants, args.stepGrants)) {
|
|
84
|
+
const payloadKind = args.kind === "mail" ? "message" : "signal";
|
|
85
|
+
throw new WorkflowRunDispatchPayloadConflictError(args.messageId, payloadKind, args.operation);
|
|
86
|
+
}
|
|
87
|
+
return { dispatch: parsed, created: false };
|
|
88
|
+
}
|
|
89
|
+
async function acknowledgeMatchingDispatch(executor, generation, conditions, nowOverride) {
|
|
90
|
+
const now = databaseTimestamp(nowOverride);
|
|
91
|
+
const [updated] = await executor
|
|
92
|
+
.update(workflowRunDispatch)
|
|
93
|
+
.set({
|
|
94
|
+
status: "acknowledged",
|
|
95
|
+
acknowledgedGeneration: generation,
|
|
96
|
+
acknowledgedAt: now,
|
|
97
|
+
attemptCount: sql `${workflowRunDispatch.attemptCount} + 1`,
|
|
98
|
+
nextAttemptAt: null,
|
|
99
|
+
deliveryLeaseId: null,
|
|
100
|
+
deliveryLeaseExpiresAt: null,
|
|
101
|
+
failureCode: null,
|
|
102
|
+
failureMessage: null,
|
|
103
|
+
updatedAt: now,
|
|
104
|
+
})
|
|
105
|
+
.where(and(...conditions))
|
|
106
|
+
.returning();
|
|
107
|
+
return updated === undefined ? null : parseWorkflowRunDispatchRow(updated);
|
|
108
|
+
}
|
|
109
|
+
export function createWorkflowRunDispatchStore(db) {
|
|
110
|
+
return {
|
|
111
|
+
async enqueue(args, tx) {
|
|
112
|
+
const stepGrants = validateStepGrants(args.anchorRunId, args.stepGrants);
|
|
113
|
+
return insertOrReconcileDispatch(tx ?? db, {
|
|
114
|
+
operation: "enqueue",
|
|
115
|
+
id: args.id,
|
|
116
|
+
anchorRunId: args.anchorRunId,
|
|
117
|
+
messageId: args.messageId,
|
|
118
|
+
kind: "mail",
|
|
119
|
+
rawMessage: args.rawMessage,
|
|
120
|
+
stepGrants,
|
|
121
|
+
...(args.now !== undefined ? { now: args.now } : {}),
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
async enqueueSignal(args, tx) {
|
|
125
|
+
const signal = SignalDeliverFrame.assert({
|
|
126
|
+
type: "signal.deliver",
|
|
127
|
+
...args.signal,
|
|
128
|
+
});
|
|
129
|
+
const encoded = new TextEncoder().encode(JSON.stringify(signal));
|
|
130
|
+
return insertOrReconcileDispatch(tx ?? db, {
|
|
131
|
+
operation: "enqueueSignal",
|
|
132
|
+
id: args.id,
|
|
133
|
+
anchorRunId: args.anchorRunId,
|
|
134
|
+
messageId: signal.signalId,
|
|
135
|
+
kind: "signal",
|
|
136
|
+
rawMessage: encoded,
|
|
137
|
+
stepGrants: [],
|
|
138
|
+
...(args.now !== undefined ? { now: args.now } : {}),
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
async claimNextPending(args) {
|
|
142
|
+
return db.transaction(async (tx) => {
|
|
143
|
+
const [candidate] = await tx
|
|
144
|
+
.select({ id: workflowRunDispatch.id })
|
|
145
|
+
.from(workflowRunDispatch)
|
|
146
|
+
.where(and(eq(workflowRunDispatch.status, "pending"), lte(workflowRunDispatch.nextAttemptAt, sql `now()`), or(isNull(workflowRunDispatch.deliveryLeaseExpiresAt), lte(workflowRunDispatch.deliveryLeaseExpiresAt, sql `now()`))))
|
|
147
|
+
.orderBy(asc(workflowRunDispatch.nextAttemptAt), asc(workflowRunDispatch.createdAt))
|
|
148
|
+
.limit(1)
|
|
149
|
+
.for("update", { skipLocked: true });
|
|
150
|
+
if (candidate === undefined)
|
|
151
|
+
return null;
|
|
152
|
+
const [claimed] = await tx
|
|
153
|
+
.update(workflowRunDispatch)
|
|
154
|
+
.set({
|
|
155
|
+
deliveryLeaseId: args.leaseId,
|
|
156
|
+
deliveryLeaseExpiresAt: sql `now() + (${args.leaseDurationMs} * interval '1 millisecond')`,
|
|
157
|
+
})
|
|
158
|
+
.where(eq(workflowRunDispatch.id, candidate.id))
|
|
159
|
+
.returning();
|
|
160
|
+
return claimed === undefined
|
|
161
|
+
? null
|
|
162
|
+
: parseWorkflowRunDispatchRow(claimed);
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
async acknowledge(args) {
|
|
166
|
+
return db.transaction(async (tx) => {
|
|
167
|
+
const [allocation] = await tx
|
|
168
|
+
.select({
|
|
169
|
+
anchorRunId: sidecarAllocation.anchorRunId,
|
|
170
|
+
status: sidecarAllocation.status,
|
|
171
|
+
generation: sidecarAllocation.generation,
|
|
172
|
+
ensureAcceptedGeneration: sidecarAllocation.ensureAcceptedGeneration,
|
|
173
|
+
})
|
|
174
|
+
.from(sidecarAllocation)
|
|
175
|
+
.where(eq(sidecarAllocation.id, args.allocationId))
|
|
176
|
+
.limit(1)
|
|
177
|
+
.for("update");
|
|
178
|
+
if (allocation === undefined ||
|
|
179
|
+
allocation.anchorRunId !== args.anchorRunId ||
|
|
180
|
+
allocation.status !== "allocated" ||
|
|
181
|
+
allocation.generation !== args.generation ||
|
|
182
|
+
allocation.ensureAcceptedGeneration !== args.generation) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
return acknowledgeMatchingDispatch(tx, args.generation, [
|
|
186
|
+
eq(workflowRunDispatch.anchorRunId, args.anchorRunId),
|
|
187
|
+
eq(workflowRunDispatch.messageId, args.messageId),
|
|
188
|
+
eq(workflowRunDispatch.kind, "mail"),
|
|
189
|
+
eq(workflowRunDispatch.status, "pending"),
|
|
190
|
+
], args.now);
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
async scheduleRetry(args) {
|
|
194
|
+
const [updated] = await db
|
|
195
|
+
.update(workflowRunDispatch)
|
|
196
|
+
.set({
|
|
197
|
+
nextAttemptAt: args.nextAttemptAt,
|
|
198
|
+
attemptCount: sql `${workflowRunDispatch.attemptCount} + 1`,
|
|
199
|
+
deliveryLeaseId: null,
|
|
200
|
+
deliveryLeaseExpiresAt: null,
|
|
201
|
+
failureCode: args.code,
|
|
202
|
+
failureMessage: args.message,
|
|
203
|
+
updatedAt: databaseTimestamp(args.now),
|
|
204
|
+
})
|
|
205
|
+
.where(and(eq(workflowRunDispatch.id, args.dispatchId), eq(workflowRunDispatch.status, "pending"), ...leaseCondition(args.expectedLeaseId)))
|
|
206
|
+
.returning();
|
|
207
|
+
return updated === undefined
|
|
208
|
+
? null
|
|
209
|
+
: parseWorkflowRunDispatchRow(updated);
|
|
210
|
+
},
|
|
211
|
+
async settle(anchorRunId, messageId, now = new Date()) {
|
|
212
|
+
const [updated] = await db
|
|
213
|
+
.update(workflowRunDispatch)
|
|
214
|
+
.set({
|
|
215
|
+
status: "settled",
|
|
216
|
+
nextAttemptAt: null,
|
|
217
|
+
deliveryLeaseId: null,
|
|
218
|
+
deliveryLeaseExpiresAt: null,
|
|
219
|
+
failureCode: null,
|
|
220
|
+
failureMessage: null,
|
|
221
|
+
settledAt: now,
|
|
222
|
+
updatedAt: now,
|
|
223
|
+
})
|
|
224
|
+
.where(and(eq(workflowRunDispatch.anchorRunId, anchorRunId), eq(workflowRunDispatch.messageId, messageId), inArray(workflowRunDispatch.status, ["pending", "acknowledged"])))
|
|
225
|
+
.returning();
|
|
226
|
+
return updated === undefined
|
|
227
|
+
? null
|
|
228
|
+
: parseWorkflowRunDispatchRow(updated);
|
|
229
|
+
},
|
|
230
|
+
async fail(args) {
|
|
231
|
+
const now = args.now ?? new Date();
|
|
232
|
+
const [updated] = await db
|
|
233
|
+
.update(workflowRunDispatch)
|
|
234
|
+
.set({
|
|
235
|
+
status: "failed",
|
|
236
|
+
nextAttemptAt: null,
|
|
237
|
+
deliveryLeaseId: null,
|
|
238
|
+
deliveryLeaseExpiresAt: null,
|
|
239
|
+
failureCode: args.code,
|
|
240
|
+
failureMessage: args.message,
|
|
241
|
+
updatedAt: now,
|
|
242
|
+
})
|
|
243
|
+
.where(and(eq(workflowRunDispatch.anchorRunId, args.anchorRunId), eq(workflowRunDispatch.messageId, args.messageId), inArray(workflowRunDispatch.status, ["pending", "acknowledged"])))
|
|
244
|
+
.returning();
|
|
245
|
+
return updated === undefined
|
|
246
|
+
? null
|
|
247
|
+
: parseWorkflowRunDispatchRow(updated);
|
|
248
|
+
},
|
|
249
|
+
async failUnsettled(anchorRunId, code, message, now = new Date(), tx) {
|
|
250
|
+
const rows = await (tx ?? db)
|
|
251
|
+
.update(workflowRunDispatch)
|
|
252
|
+
.set({
|
|
253
|
+
status: "failed",
|
|
254
|
+
nextAttemptAt: null,
|
|
255
|
+
deliveryLeaseId: null,
|
|
256
|
+
deliveryLeaseExpiresAt: null,
|
|
257
|
+
failureCode: code,
|
|
258
|
+
failureMessage: message,
|
|
259
|
+
updatedAt: now,
|
|
260
|
+
})
|
|
261
|
+
.where(and(eq(workflowRunDispatch.anchorRunId, anchorRunId), inArray(workflowRunDispatch.status, ["pending", "acknowledged"])))
|
|
262
|
+
.returning({ id: workflowRunDispatch.id });
|
|
263
|
+
return rows.length;
|
|
264
|
+
},
|
|
265
|
+
async requeueUnsettled(anchorRunId) {
|
|
266
|
+
const rows = await db
|
|
267
|
+
.update(workflowRunDispatch)
|
|
268
|
+
.set({
|
|
269
|
+
status: "pending",
|
|
270
|
+
acknowledgedGeneration: null,
|
|
271
|
+
acknowledgedAt: null,
|
|
272
|
+
nextAttemptAt: sql `now()`,
|
|
273
|
+
deliveryLeaseId: null,
|
|
274
|
+
deliveryLeaseExpiresAt: null,
|
|
275
|
+
failureCode: null,
|
|
276
|
+
failureMessage: null,
|
|
277
|
+
updatedAt: sql `now()`,
|
|
278
|
+
})
|
|
279
|
+
.where(and(eq(workflowRunDispatch.anchorRunId, anchorRunId), inArray(workflowRunDispatch.status, ["pending", "acknowledged"])))
|
|
280
|
+
.returning({ id: workflowRunDispatch.id });
|
|
281
|
+
return rows.length;
|
|
282
|
+
},
|
|
283
|
+
async findById(id) {
|
|
284
|
+
const row = await db.query.workflowRunDispatch.findFirst({
|
|
285
|
+
where: eq(workflowRunDispatch.id, id),
|
|
286
|
+
});
|
|
287
|
+
return row === undefined ? null : parseWorkflowRunDispatchRow(row);
|
|
288
|
+
},
|
|
289
|
+
async listUnsettled(anchorRunId) {
|
|
290
|
+
const rows = await db
|
|
291
|
+
.select()
|
|
292
|
+
.from(workflowRunDispatch)
|
|
293
|
+
.where(and(eq(workflowRunDispatch.anchorRunId, anchorRunId), inArray(workflowRunDispatch.status, ["pending", "acknowledged"])))
|
|
294
|
+
.orderBy(asc(workflowRunDispatch.createdAt));
|
|
295
|
+
return rows.map(parseWorkflowRunDispatchRow);
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DB, DBExecutor } from "./client.js";
|
|
2
|
+
import { parseWorkflowRunLaunchSpecRow } from "./parse-row.js";
|
|
3
|
+
import { workflowRunLaunchSpec } from "./schema/workflow-run-launch-spec.js";
|
|
4
|
+
type DBHandle = DB["db"];
|
|
5
|
+
type LaunchSpecInsert = typeof workflowRunLaunchSpec.$inferInsert;
|
|
6
|
+
type ParsedLaunchSpec = ReturnType<typeof parseWorkflowRunLaunchSpecRow>;
|
|
7
|
+
/** Store for immutable workflow recovery inputs keyed by anchor run. */
|
|
8
|
+
export declare function createWorkflowRunLaunchSpecStore(db: DBHandle): {
|
|
9
|
+
create(row: LaunchSpecInsert, tx?: DBExecutor): Promise<ParsedLaunchSpec>;
|
|
10
|
+
get(anchorRunId: string, tx?: DBExecutor): Promise<ParsedLaunchSpec | null>;
|
|
11
|
+
};
|
|
12
|
+
export type WorkflowRunLaunchSpecStore = ReturnType<typeof createWorkflowRunLaunchSpecStore>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { parseWorkflowRunLaunchSpecRow } from "./parse-row.js";
|
|
3
|
+
import { workflowRunLaunchSpec } from "./schema/workflow-run-launch-spec.js";
|
|
4
|
+
/** Store for immutable workflow recovery inputs keyed by anchor run. */
|
|
5
|
+
export function createWorkflowRunLaunchSpecStore(db) {
|
|
6
|
+
return {
|
|
7
|
+
async create(row, tx) {
|
|
8
|
+
parseWorkflowRunLaunchSpecRow({
|
|
9
|
+
...row,
|
|
10
|
+
schemaVersion: row.schemaVersion ?? 1,
|
|
11
|
+
toolPackagePins: row.toolPackagePins ?? null,
|
|
12
|
+
createdAt: row.createdAt ?? new Date(),
|
|
13
|
+
});
|
|
14
|
+
const [inserted] = await (tx ?? db)
|
|
15
|
+
.insert(workflowRunLaunchSpec)
|
|
16
|
+
.values(row)
|
|
17
|
+
.returning();
|
|
18
|
+
if (inserted === undefined) {
|
|
19
|
+
throw new Error(`workflowRunLaunchSpecStore.create: insert returned no row for ${row.anchorRunId}`);
|
|
20
|
+
}
|
|
21
|
+
return parseWorkflowRunLaunchSpecRow(inserted);
|
|
22
|
+
},
|
|
23
|
+
async get(anchorRunId, tx) {
|
|
24
|
+
const row = await (tx ?? db).query.workflowRunLaunchSpec.findFirst({
|
|
25
|
+
where: eq(workflowRunLaunchSpec.anchorRunId, anchorRunId),
|
|
26
|
+
});
|
|
27
|
+
return row === undefined ? null : parseWorkflowRunLaunchSpecRow(row);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { DB, DBExecutor } from "./client.js";
|
|
2
|
+
import { workflowRun } from "./schema/workflow-run.js";
|
|
3
|
+
import { parseWorkflowRunRow } from "./parse-row.js";
|
|
4
|
+
type DBHandle = DB["db"];
|
|
5
|
+
type WorkflowRunInsert = typeof workflowRun.$inferInsert;
|
|
6
|
+
type ParsedWorkflowRun = ReturnType<typeof parseWorkflowRunRow>;
|
|
7
|
+
/**
|
|
8
|
+
* Store for the `workflow_run` table, the per-run authorization subject that
|
|
9
|
+
* approvals and signal correlations anchor to. Each method accepts an optional
|
|
10
|
+
* transaction handle so the run row can be written in the same transaction that
|
|
11
|
+
* co-writes the run's principal or its approval.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createWorkflowRunStore(db: DBHandle): {
|
|
14
|
+
create(row: WorkflowRunInsert, tx?: DBExecutor): Promise<ParsedWorkflowRun>;
|
|
15
|
+
/**
|
|
16
|
+
* Idempotent variant of `create`. On an `id` primary-key conflict the
|
|
17
|
+
* insert is a no-op and this returns `null` rather than throwing, so a
|
|
18
|
+
* redelivered createIfAbsent (workflow-log replay, supervisor restart
|
|
19
|
+
* re-emitting) does not fail the run's co-write. Returns the parsed row
|
|
20
|
+
* only when this call performed the insert.
|
|
21
|
+
*/
|
|
22
|
+
createIfAbsent(row: WorkflowRunInsert, tx?: DBExecutor): Promise<ParsedWorkflowRun | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Anchor an externally-triggered run onto its deploy-time anchor row. The
|
|
25
|
+
* anchor is created at deploy (born "deployed" with a null principal); the
|
|
26
|
+
* first trigger reconciles it here, attaching `principalId` and flipping
|
|
27
|
+
* "deployed" -> "running" in one UPDATE. The insert is a conflict-tolerant
|
|
28
|
+
* safety net: the anchor provably exists by the time a trigger runs, so it
|
|
29
|
+
* normally no-ops. The three-part guard makes the reconcile single-shot and
|
|
30
|
+
* safe: `principalId IS NULL` never overwrites a principal a concurrent
|
|
31
|
+
* winner already set, and `status = 'deployed'` never resurrects a run a
|
|
32
|
+
* concurrent teardown already settled terminal (the null-principal guard
|
|
33
|
+
* alone would). `principalId` must be non-null: only the externally-
|
|
34
|
+
* triggered path anchors through here, and it always mints one.
|
|
35
|
+
*/
|
|
36
|
+
anchorWithPrincipal(row: WorkflowRunInsert & {
|
|
37
|
+
principalId: string;
|
|
38
|
+
}, tx?: DBExecutor): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Atomically settle a live run into a terminal state. The live-status guard
|
|
41
|
+
* (`status IN ('deployed','running')`) makes the flip single-shot: the first
|
|
42
|
+
* caller stamps the terminal status and `endedAt` and gets the row back; any
|
|
43
|
+
* later caller matches no row and receives null, so the run is not
|
|
44
|
+
* re-terminated and its `endedAt` is not overwritten. Accepting "deployed"
|
|
45
|
+
* settles a deployment torn down before its first trigger. This is a safety
|
|
46
|
+
* property, not a recovery path -- it makes a second call (a manual replay
|
|
47
|
+
* against an already-settled run) a harmless no-op; it does not by itself
|
|
48
|
+
* re-drive a flip that failed. Returns the parsed row only on the winning
|
|
49
|
+
* flip.
|
|
50
|
+
*/
|
|
51
|
+
markTerminal(runId: string, status: "completed" | "failed" | "cancelled", endedAt: Date, tx?: DBExecutor): Promise<ParsedWorkflowRun | null>;
|
|
52
|
+
};
|
|
53
|
+
export type WorkflowRunStore = ReturnType<typeof createWorkflowRunStore>;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { and, eq, inArray, isNull } from "drizzle-orm";
|
|
2
|
+
import { liveWorkflowRunStatuses, workflowRun } from "./schema/workflow-run.js";
|
|
3
|
+
import { parseWorkflowRunRow } from "./parse-row.js";
|
|
4
|
+
/**
|
|
5
|
+
* Store for the `workflow_run` table, the per-run authorization subject that
|
|
6
|
+
* approvals and signal correlations anchor to. Each method accepts an optional
|
|
7
|
+
* transaction handle so the run row can be written in the same transaction that
|
|
8
|
+
* co-writes the run's principal or its approval.
|
|
9
|
+
*/
|
|
10
|
+
export function createWorkflowRunStore(db) {
|
|
11
|
+
return {
|
|
12
|
+
async create(row, tx) {
|
|
13
|
+
const [inserted] = await (tx ?? db)
|
|
14
|
+
.insert(workflowRun)
|
|
15
|
+
.values(row)
|
|
16
|
+
.returning();
|
|
17
|
+
if (inserted === undefined) {
|
|
18
|
+
throw new Error(`workflowRunStore.create: insert returned no row for ${row.id}`);
|
|
19
|
+
}
|
|
20
|
+
return parseWorkflowRunRow(inserted);
|
|
21
|
+
},
|
|
22
|
+
/**
|
|
23
|
+
* Idempotent variant of `create`. On an `id` primary-key conflict the
|
|
24
|
+
* insert is a no-op and this returns `null` rather than throwing, so a
|
|
25
|
+
* redelivered createIfAbsent (workflow-log replay, supervisor restart
|
|
26
|
+
* re-emitting) does not fail the run's co-write. Returns the parsed row
|
|
27
|
+
* only when this call performed the insert.
|
|
28
|
+
*/
|
|
29
|
+
async createIfAbsent(row, tx) {
|
|
30
|
+
const [inserted] = await (tx ?? db)
|
|
31
|
+
.insert(workflowRun)
|
|
32
|
+
.values(row)
|
|
33
|
+
.onConflictDoNothing({ target: workflowRun.id })
|
|
34
|
+
.returning();
|
|
35
|
+
return inserted === undefined ? null : parseWorkflowRunRow(inserted);
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* Anchor an externally-triggered run onto its deploy-time anchor row. The
|
|
39
|
+
* anchor is created at deploy (born "deployed" with a null principal); the
|
|
40
|
+
* first trigger reconciles it here, attaching `principalId` and flipping
|
|
41
|
+
* "deployed" -> "running" in one UPDATE. The insert is a conflict-tolerant
|
|
42
|
+
* safety net: the anchor provably exists by the time a trigger runs, so it
|
|
43
|
+
* normally no-ops. The three-part guard makes the reconcile single-shot and
|
|
44
|
+
* safe: `principalId IS NULL` never overwrites a principal a concurrent
|
|
45
|
+
* winner already set, and `status = 'deployed'` never resurrects a run a
|
|
46
|
+
* concurrent teardown already settled terminal (the null-principal guard
|
|
47
|
+
* alone would). `principalId` must be non-null: only the externally-
|
|
48
|
+
* triggered path anchors through here, and it always mints one.
|
|
49
|
+
*/
|
|
50
|
+
async anchorWithPrincipal(row, tx) {
|
|
51
|
+
const executor = tx ?? db;
|
|
52
|
+
const [inserted] = await executor
|
|
53
|
+
.insert(workflowRun)
|
|
54
|
+
.values(row)
|
|
55
|
+
.onConflictDoNothing({ target: workflowRun.id })
|
|
56
|
+
.returning();
|
|
57
|
+
if (inserted !== undefined)
|
|
58
|
+
return;
|
|
59
|
+
await executor
|
|
60
|
+
.update(workflowRun)
|
|
61
|
+
.set({ principalId: row.principalId, status: "running" })
|
|
62
|
+
.where(and(eq(workflowRun.id, row.id), isNull(workflowRun.principalId), eq(workflowRun.status, "deployed")));
|
|
63
|
+
},
|
|
64
|
+
/**
|
|
65
|
+
* Atomically settle a live run into a terminal state. The live-status guard
|
|
66
|
+
* (`status IN ('deployed','running')`) makes the flip single-shot: the first
|
|
67
|
+
* caller stamps the terminal status and `endedAt` and gets the row back; any
|
|
68
|
+
* later caller matches no row and receives null, so the run is not
|
|
69
|
+
* re-terminated and its `endedAt` is not overwritten. Accepting "deployed"
|
|
70
|
+
* settles a deployment torn down before its first trigger. This is a safety
|
|
71
|
+
* property, not a recovery path -- it makes a second call (a manual replay
|
|
72
|
+
* against an already-settled run) a harmless no-op; it does not by itself
|
|
73
|
+
* re-drive a flip that failed. Returns the parsed row only on the winning
|
|
74
|
+
* flip.
|
|
75
|
+
*/
|
|
76
|
+
async markTerminal(runId, status, endedAt, tx) {
|
|
77
|
+
const [updated] = await (tx ?? db)
|
|
78
|
+
.update(workflowRun)
|
|
79
|
+
.set({ status, endedAt })
|
|
80
|
+
.where(and(eq(workflowRun.id, runId), inArray(workflowRun.status, [...liveWorkflowRunStatuses])))
|
|
81
|
+
.returning();
|
|
82
|
+
return updated === undefined ? null : parseWorkflowRunRow(updated);
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "agent" ADD COLUMN "tool_packages" jsonb DEFAULT '[]'::jsonb NOT NULL;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
ALTER TABLE "session_asset" DROP CONSTRAINT "session_asset_instance_id_agent_asset_id_pk";--> statement-breakpoint
|
|
2
|
+
ALTER TABLE "session_asset" ALTER COLUMN "agent_asset_id" DROP NOT NULL;--> statement-breakpoint
|
|
3
|
+
ALTER TABLE "session_asset" ADD CONSTRAINT "session_asset_instance_id_mount_path_pk" PRIMARY KEY("instance_id","mount_path");--> statement-breakpoint
|
|
4
|
+
ALTER TABLE "session_asset" ADD COLUMN "source" text NOT NULL DEFAULT 'direct';--> statement-breakpoint
|
|
5
|
+
ALTER TABLE "session_asset" ALTER COLUMN "source" DROP DEFAULT;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
CREATE TABLE "model" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"tenant_id" text NOT NULL,
|
|
4
|
+
"canonical_name" text NOT NULL,
|
|
5
|
+
"display_name" text,
|
|
6
|
+
"description" text,
|
|
7
|
+
"disabled" boolean DEFAULT false NOT NULL,
|
|
8
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
9
|
+
"updated_at" timestamp DEFAULT now() NOT NULL,
|
|
10
|
+
CONSTRAINT "model_tenant_canonical_name" UNIQUE("tenant_id","canonical_name")
|
|
11
|
+
);
|
|
12
|
+
--> statement-breakpoint
|
|
13
|
+
CREATE TABLE "model_offering" (
|
|
14
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
15
|
+
"tenant_id" text NOT NULL,
|
|
16
|
+
"model_id" text NOT NULL,
|
|
17
|
+
"provider_id" text NOT NULL,
|
|
18
|
+
"priority" integer NOT NULL,
|
|
19
|
+
"deployment_tags" text[] DEFAULT '{}' NOT NULL,
|
|
20
|
+
"capabilities" text[] DEFAULT '{}' NOT NULL,
|
|
21
|
+
"disabled" boolean DEFAULT false NOT NULL,
|
|
22
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
23
|
+
"updated_at" timestamp DEFAULT now() NOT NULL,
|
|
24
|
+
CONSTRAINT "model_offering_tenant_model_provider" UNIQUE("tenant_id","model_id","provider_id")
|
|
25
|
+
);
|
|
26
|
+
--> statement-breakpoint
|
|
27
|
+
CREATE TABLE "model_pricing" (
|
|
28
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
29
|
+
"tenant_id" text NOT NULL,
|
|
30
|
+
"offering_id" text NOT NULL,
|
|
31
|
+
"currency" text NOT NULL,
|
|
32
|
+
"input_token_price" text,
|
|
33
|
+
"output_token_price" text,
|
|
34
|
+
"cache_read_token_price" text,
|
|
35
|
+
"cache_write_token_price" text,
|
|
36
|
+
"thinking_token_price" text,
|
|
37
|
+
"per_request_fee" text,
|
|
38
|
+
"per_image_fee" text,
|
|
39
|
+
"per_audio_fee" text,
|
|
40
|
+
"effective_from" timestamp with time zone NOT NULL,
|
|
41
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
42
|
+
CONSTRAINT "model_pricing_offering_currency_effective_from" UNIQUE("offering_id","currency","effective_from")
|
|
43
|
+
);
|
|
44
|
+
--> statement-breakpoint
|
|
45
|
+
CREATE TABLE "model_provider" (
|
|
46
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
47
|
+
"tenant_id" text NOT NULL,
|
|
48
|
+
"name" text NOT NULL,
|
|
49
|
+
"plugin" text NOT NULL,
|
|
50
|
+
"base_url" text NOT NULL,
|
|
51
|
+
"credential_id" text,
|
|
52
|
+
"wallet_id" text,
|
|
53
|
+
"disabled" boolean DEFAULT false NOT NULL,
|
|
54
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
55
|
+
"updated_at" timestamp DEFAULT now() NOT NULL,
|
|
56
|
+
CONSTRAINT "model_provider_tenant_name" UNIQUE("tenant_id","name"),
|
|
57
|
+
CONSTRAINT "model_provider_auth_xor" CHECK (("model_provider"."credential_id" is not null) <> ("model_provider"."wallet_id" is not null))
|
|
58
|
+
);
|
|
59
|
+
--> statement-breakpoint
|
|
60
|
+
ALTER TABLE "model" ADD CONSTRAINT "model_tenant_id_tenant_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenant"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
61
|
+
ALTER TABLE "model_offering" ADD CONSTRAINT "model_offering_tenant_id_tenant_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenant"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
62
|
+
ALTER TABLE "model_offering" ADD CONSTRAINT "model_offering_model_id_model_id_fk" FOREIGN KEY ("model_id") REFERENCES "public"."model"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
63
|
+
ALTER TABLE "model_offering" ADD CONSTRAINT "model_offering_provider_id_model_provider_id_fk" FOREIGN KEY ("provider_id") REFERENCES "public"."model_provider"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
64
|
+
ALTER TABLE "model_pricing" ADD CONSTRAINT "model_pricing_tenant_id_tenant_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenant"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
65
|
+
ALTER TABLE "model_pricing" ADD CONSTRAINT "model_pricing_offering_id_model_offering_id_fk" FOREIGN KEY ("offering_id") REFERENCES "public"."model_offering"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
66
|
+
ALTER TABLE "model_provider" ADD CONSTRAINT "model_provider_tenant_id_tenant_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenant"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
67
|
+
ALTER TABLE "model_provider" ADD CONSTRAINT "model_provider_credential_id_credential_id_fk" FOREIGN KEY ("credential_id") REFERENCES "public"."credential"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
|
68
|
+
ALTER TABLE "model_provider" ADD CONSTRAINT "model_provider_wallet_id_wallet_id_fk" FOREIGN KEY ("wallet_id") REFERENCES "public"."wallet"("id") ON DELETE restrict ON UPDATE no action;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "agent" ADD COLUMN "model_requirements" jsonb;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "agent_instance" ADD COLUMN "model_preferences" jsonb;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
CREATE TABLE "workflow_deployment" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"tenant_id" text NOT NULL,
|
|
4
|
+
"definition_asset_id" text NOT NULL,
|
|
5
|
+
"status" text DEFAULT 'deployed' NOT NULL,
|
|
6
|
+
"created_at" timestamp DEFAULT now() NOT NULL
|
|
7
|
+
);
|
|
8
|
+
--> statement-breakpoint
|
|
9
|
+
ALTER TABLE "workflow_deployment" ADD CONSTRAINT "workflow_deployment_tenant_id_tenant_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenant"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
10
|
+
ALTER TABLE "workflow_deployment" ADD CONSTRAINT "workflow_deployment_definition_asset_id_asset_id_fk" FOREIGN KEY ("definition_asset_id") REFERENCES "public"."asset"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
11
|
+
CREATE INDEX "workflow_deployment_tenant_idx" ON "workflow_deployment" USING btree ("tenant_id","created_at");
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
ALTER TABLE "workflow_deployment" ADD COLUMN "address" text NOT NULL;--> statement-breakpoint
|
|
2
|
+
ALTER TABLE "workflow_deployment" ADD COLUMN "public_key" text;--> statement-breakpoint
|
|
3
|
+
CREATE UNIQUE INDEX "workflow_deployment_address_idx" ON "workflow_deployment" USING btree ("address");
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
-- Backfill `credential:{id}` / `use` grants for the owner of every existing
|
|
2
|
+
-- personal credential (rows where `principal_id` is set) that does not already
|
|
3
|
+
-- have one. This keeps existing personal-credential owners working once
|
|
4
|
+
-- launch enforcement fails closed. Organizational credentials
|
|
5
|
+
-- (`principal_id` IS NULL) are intentionally excluded: they remain gated
|
|
6
|
+
-- behind tenant-owner role inheritance and explicit administrative grants.
|
|
7
|
+
--
|
|
8
|
+
-- Idempotent: the `WHERE NOT EXISTS` guard skips any credential whose owner
|
|
9
|
+
-- already holds a matching grant, so re-application inserts nothing and does
|
|
10
|
+
-- not fail. Grant ids reproduce the app convention (`grt_` prefix followed by
|
|
11
|
+
-- 32 hex characters) via `gen_random_uuid()`.
|
|
12
|
+
INSERT INTO "grant" (
|
|
13
|
+
"id",
|
|
14
|
+
"tenant_id",
|
|
15
|
+
"principal_id",
|
|
16
|
+
"resource",
|
|
17
|
+
"action",
|
|
18
|
+
"effect",
|
|
19
|
+
"origin",
|
|
20
|
+
"expires_at"
|
|
21
|
+
)
|
|
22
|
+
SELECT
|
|
23
|
+
'grt_' || replace(gen_random_uuid()::text, '-', ''),
|
|
24
|
+
c."tenant_id",
|
|
25
|
+
c."principal_id",
|
|
26
|
+
'credential:' || c."id",
|
|
27
|
+
'use',
|
|
28
|
+
'allow',
|
|
29
|
+
'creator',
|
|
30
|
+
NULL
|
|
31
|
+
FROM "credential" c
|
|
32
|
+
WHERE c."principal_id" IS NOT NULL
|
|
33
|
+
AND NOT EXISTS (
|
|
34
|
+
SELECT 1
|
|
35
|
+
FROM "grant" g
|
|
36
|
+
WHERE g."principal_id" = c."principal_id"
|
|
37
|
+
AND g."resource" = 'credential:' || c."id"
|
|
38
|
+
AND g."action" = 'use'
|
|
39
|
+
);
|