@opengeni/core 2.6.3-canary.0 → 2.6.4-canary.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/dist/billing/limits.d.ts +5 -0
- package/dist/dependencies.d.ts +10 -1
- package/dist/domain/sessions.d.ts +24 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1049 -316
- package/dist/index.js.map +1 -1
- package/dist/model-catalog.d.ts +89 -0
- package/package.json +10 -10
- package/src/billing/limits.ts +52 -22
- package/src/dependencies.ts +15 -1
- package/src/domain/scheduled-tasks.ts +52 -1
- package/src/domain/sessions.ts +790 -344
- package/src/index.ts +1 -0
- package/src/model-catalog.ts +565 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type ConfiguredModel, type Settings } from "@opengeni/config";
|
|
2
|
+
import { type ModelAvailabilityV1, type ModelCredentialReadinessV1, type WorkspaceModelPolicyContract } from "@opengeni/contracts";
|
|
3
|
+
import { type Database } from "@opengeni/db";
|
|
4
|
+
export type ResolvedCatalogSettings = {
|
|
5
|
+
settings: Settings;
|
|
6
|
+
source: "code" | "database";
|
|
7
|
+
version: number | null;
|
|
8
|
+
modelNotes: Record<string, string>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Curated workspace Gateway products and workspace-owned custom slugs share
|
|
12
|
+
* one public prefix. Only the latter have a mutable catalog row whose active
|
|
13
|
+
* generation must be rechecked at a fresh acceptance commit boundary.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isWorkspaceGatewayCustomModelId(settings: Settings, modelId: string): boolean;
|
|
16
|
+
export declare function isWorkspaceOpenRouterCustomModelId(settings: Settings, modelId: string): boolean;
|
|
17
|
+
export type WorkspaceCustomModelReference = {
|
|
18
|
+
providerKind: "vercel_gateway" | "openrouter";
|
|
19
|
+
upstreamModelId: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function workspaceCustomModelReference(settings: Settings, modelId: string): WorkspaceCustomModelReference | null;
|
|
22
|
+
export declare function isWorkspaceCustomModelId(settings: Settings, modelId: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the deployment catalog without making synchronous env settings read
|
|
25
|
+
* Postgres. Database mode fails closed when the singleton is absent or invalid;
|
|
26
|
+
* code mode preserves the already-validated env catalog.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveCatalogSettings(db: Database, envSettings: Settings): Promise<ResolvedCatalogSettings>;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the deployment catalog and add only the custom Gateway slugs owned by
|
|
31
|
+
* one workspace. Use this at model-bearing workspace boundaries; public config
|
|
32
|
+
* and deployment-operator surfaces must continue to use `resolveCatalogSettings`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveWorkspaceCatalogSettings(db: Database, envSettings: Settings, input: {
|
|
35
|
+
accountId: string;
|
|
36
|
+
workspaceId: string;
|
|
37
|
+
retainedProductModelId?: string | null;
|
|
38
|
+
retainedProductModelIds?: readonly (string | null | undefined)[];
|
|
39
|
+
}): Promise<ResolvedCatalogSettings>;
|
|
40
|
+
export type ModelAvailabilityObservation = {
|
|
41
|
+
status: "available" | "degraded" | "unavailable";
|
|
42
|
+
reason: "not_entitled" | "provider_unhealthy" | null;
|
|
43
|
+
checkedAt: string;
|
|
44
|
+
};
|
|
45
|
+
export type ModelCredentialReadinessObservation = {
|
|
46
|
+
status: "ready";
|
|
47
|
+
checkedAt: string;
|
|
48
|
+
} | {
|
|
49
|
+
status: "not_ready";
|
|
50
|
+
reason: "prerequisites_missing" | "needs_reauth";
|
|
51
|
+
checkedAt: string;
|
|
52
|
+
} | {
|
|
53
|
+
status: "error";
|
|
54
|
+
reason: "resolver_error";
|
|
55
|
+
checkedAt: string;
|
|
56
|
+
};
|
|
57
|
+
export declare const MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS: number;
|
|
58
|
+
export type WorkspaceModelSelectionInput = {
|
|
59
|
+
settings: Settings;
|
|
60
|
+
policy: WorkspaceModelPolicyContract | null;
|
|
61
|
+
codexSubscriptionActive: boolean;
|
|
62
|
+
xaiSubscriptionActive?: boolean;
|
|
63
|
+
workspaceGatewayConnectionActive?: boolean;
|
|
64
|
+
workspaceOpenRouterConnectionActive?: boolean;
|
|
65
|
+
workspaceGatewayCustomModels?: readonly {
|
|
66
|
+
upstreamModelId: string;
|
|
67
|
+
label?: string | null;
|
|
68
|
+
}[];
|
|
69
|
+
workspaceOpenRouterCustomModels?: readonly {
|
|
70
|
+
upstreamModelId: string;
|
|
71
|
+
label?: string | null;
|
|
72
|
+
}[];
|
|
73
|
+
credentialReadinessObservations?: Readonly<Record<string, ModelCredentialReadinessObservation>> | undefined;
|
|
74
|
+
observations?: Readonly<Record<string, ModelAvailabilityObservation>> | undefined;
|
|
75
|
+
now?: Date | undefined;
|
|
76
|
+
credentialReadinessMaxAgeMs?: number | undefined;
|
|
77
|
+
};
|
|
78
|
+
export type WorkspaceModelSelection = {
|
|
79
|
+
model: ConfiguredModel;
|
|
80
|
+
credentialReadiness: ModelCredentialReadinessV1;
|
|
81
|
+
policyAllowed: boolean;
|
|
82
|
+
availability: ModelAvailabilityV1;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* One shared picker/tool decision. Catalog membership, credential readiness,
|
|
86
|
+
* workspace policy, and optional provider-health observations are evaluated in
|
|
87
|
+
* configured catalog order so every consumer exposes the same selectable set.
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveWorkspaceModelSelection(input: WorkspaceModelSelectionInput): WorkspaceModelSelection[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/core",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.4-canary.0",
|
|
4
4
|
"description": "OpenGeni framework-agnostic core: the domain, access, and billing layers (neutral access, off-HTTP V2 surface). Behavior-preserving extraction from apps/api — keeps Hono's HTTPException for error throwing (typed-errors cleanup deferred).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
57
|
-
"@opengeni/codex": "^0.2.20-canary.
|
|
58
|
-
"@opengeni/config": "^0.22.5-canary.
|
|
59
|
-
"@opengeni/contracts": "^2.9.2-canary.
|
|
60
|
-
"@opengeni/db": "^3.7.
|
|
61
|
-
"@opengeni/documents": "^0.8.
|
|
62
|
-
"@opengeni/events": "^0.4.
|
|
63
|
-
"@opengeni/observability": "^0.8.14-canary.
|
|
64
|
-
"@opengeni/runtime": "^2.0.1-canary.
|
|
65
|
-
"@opengeni/storage": "^0.2.115-canary.
|
|
57
|
+
"@opengeni/codex": "^0.2.20-canary.1",
|
|
58
|
+
"@opengeni/config": "^0.22.5-canary.1",
|
|
59
|
+
"@opengeni/contracts": "^2.9.2-canary.1",
|
|
60
|
+
"@opengeni/db": "^3.7.4-canary.0",
|
|
61
|
+
"@opengeni/documents": "^0.8.14-canary.0",
|
|
62
|
+
"@opengeni/events": "^0.4.12-canary.0",
|
|
63
|
+
"@opengeni/observability": "^0.8.14-canary.1",
|
|
64
|
+
"@opengeni/runtime": "^2.0.1-canary.1",
|
|
65
|
+
"@opengeni/storage": "^0.2.115-canary.1",
|
|
66
66
|
"hono": "^4.12.18",
|
|
67
67
|
"zod": "^4.2.1"
|
|
68
68
|
},
|
package/src/billing/limits.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
configuredStaticUsageLimits,
|
|
3
|
+
resolveModelProviderForTurn,
|
|
4
|
+
type Settings,
|
|
5
|
+
} from "@opengeni/config";
|
|
2
6
|
import type {
|
|
3
7
|
LimitAction,
|
|
4
8
|
LimitDecision,
|
|
@@ -26,14 +30,35 @@ export type LimitCheckInput = {
|
|
|
26
30
|
workspaceId?: string;
|
|
27
31
|
action: LimitAction;
|
|
28
32
|
quantity?: number;
|
|
29
|
-
// The turn's model id, when the action represents an agent turn.
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
// statically ready by definition. Non-model infra actions leave this undefined.
|
|
33
|
+
// The turn's model id, when the action represents an agent turn. The model's
|
|
34
|
+
// deployment cost controls credit/cost gates; upstream metering independently
|
|
35
|
+
// controls the token cap. Connected subscriptions still require live workspace
|
|
36
|
+
// readiness. Non-model infra actions leave this undefined.
|
|
34
37
|
model?: string | null;
|
|
35
38
|
};
|
|
36
39
|
|
|
40
|
+
export function modelFundingForAdmission(
|
|
41
|
+
settings: Settings,
|
|
42
|
+
model: string | null | undefined,
|
|
43
|
+
codexBilled: boolean,
|
|
44
|
+
): { fundedWithoutCredits: boolean; countsTowardTokenCap: boolean } {
|
|
45
|
+
const resolvedModel = model ? resolveModelProviderForTurn(settings, model)?.model : null;
|
|
46
|
+
const codexSubscriptionModel =
|
|
47
|
+
resolvedModel?.credentialSource.kind === "connected_subscription" &&
|
|
48
|
+
resolvedModel.credentialSource.provider === "codex";
|
|
49
|
+
return {
|
|
50
|
+
// A Codex namespace/definition never bypasses credits by itself: the live
|
|
51
|
+
// workspace credential predicate above remains authoritative. SuperGrok's
|
|
52
|
+
// static overlay is likewise secret-free; its worker/provider admission
|
|
53
|
+
// owns live account selection before any upstream request can occur.
|
|
54
|
+
fundedWithoutCredits:
|
|
55
|
+
codexBilled ||
|
|
56
|
+
(resolvedModel != null && !codexSubscriptionModel && resolvedModel.cost !== "credits"),
|
|
57
|
+
countsTowardTokenCap:
|
|
58
|
+
!codexBilled && resolvedModel != null && resolvedModel.billing.upstreamPayer === "deployment",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
37
62
|
export async function requireLimit(deps: LimitDependencies, input: LimitCheckInput): Promise<void> {
|
|
38
63
|
const decision = await checkLimit(deps, input);
|
|
39
64
|
if (decision.allowed) {
|
|
@@ -59,25 +84,22 @@ export async function checkLimit(
|
|
|
59
84
|
model: input.model,
|
|
60
85
|
})
|
|
61
86
|
: false;
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
resolved.model.credentialSource.mechanism === "none"
|
|
69
|
-
);
|
|
70
|
-
})()
|
|
71
|
-
: false;
|
|
72
|
-
const externallyBilled = codexBilled || credentialFreeExternal;
|
|
73
|
-
const creditDecision = await checkCreditBalance(deps, input, externallyBilled);
|
|
87
|
+
const { fundedWithoutCredits, countsTowardTokenCap } = modelFundingForAdmission(
|
|
88
|
+
deps.settings,
|
|
89
|
+
input.model,
|
|
90
|
+
codexBilled,
|
|
91
|
+
);
|
|
92
|
+
const creditDecision = await checkCreditBalance(deps, input, fundedWithoutCredits);
|
|
74
93
|
if (!creditDecision.allowed) {
|
|
75
94
|
return creditDecision;
|
|
76
95
|
}
|
|
77
96
|
if (deps.settings.usageLimitsMode !== "static" && deps.settings.usageLimitsMode !== "managed") {
|
|
78
97
|
return { allowed: true };
|
|
79
98
|
}
|
|
80
|
-
return await checkStaticCaps(deps, input,
|
|
99
|
+
return await checkStaticCaps(deps, input, {
|
|
100
|
+
fundedWithoutCredits,
|
|
101
|
+
countsTowardTokenCap,
|
|
102
|
+
});
|
|
81
103
|
}
|
|
82
104
|
|
|
83
105
|
async function checkCreditBalance(
|
|
@@ -101,10 +123,14 @@ async function checkCreditBalance(
|
|
|
101
123
|
async function checkStaticCaps(
|
|
102
124
|
deps: LimitDependencies,
|
|
103
125
|
input: LimitCheckInput,
|
|
104
|
-
|
|
126
|
+
funding: { fundedWithoutCredits: boolean; countsTowardTokenCap: boolean },
|
|
105
127
|
): Promise<LimitDecision> {
|
|
106
128
|
const limits = configuredStaticUsageLimits(deps.settings);
|
|
107
|
-
if (
|
|
129
|
+
if (
|
|
130
|
+
limits.maxMonthlyCostMicrosPerAccount &&
|
|
131
|
+
isCostlyAction(input.action) &&
|
|
132
|
+
!funding.fundedWithoutCredits
|
|
133
|
+
) {
|
|
108
134
|
const used = await sumUsageQuantity(deps.db, {
|
|
109
135
|
accountId: input.accountId,
|
|
110
136
|
eventType: "model.cost",
|
|
@@ -185,7 +211,11 @@ async function checkStaticCaps(
|
|
|
185
211
|
);
|
|
186
212
|
}
|
|
187
213
|
case "tokens:consume": {
|
|
188
|
-
if (
|
|
214
|
+
if (
|
|
215
|
+
!funding.countsTowardTokenCap ||
|
|
216
|
+
!limits.maxMonthlyTokensPerWorkspace ||
|
|
217
|
+
!input.workspaceId
|
|
218
|
+
) {
|
|
189
219
|
return { allowed: true };
|
|
190
220
|
}
|
|
191
221
|
const used = await sumUsageQuantity(deps.db, {
|
package/src/dependencies.ts
CHANGED
|
@@ -19,6 +19,7 @@ import type { ManagedAuthSessionAdapter } from "./managed-auth-session-sets";
|
|
|
19
19
|
import type { ApiSandboxClient, ResumeBoxByIdInput, ResumedSandboxSession } from "./sandbox-types";
|
|
20
20
|
import type { TranscriptionSegmenter, TranscriptionService } from "./transcription";
|
|
21
21
|
import type { EditableArtifactApplicationPort } from "./editable-artifact-live";
|
|
22
|
+
import type { ResolvedCatalogSettings } from "./model-catalog";
|
|
22
23
|
import type {
|
|
23
24
|
EditableArtifactAgentApplication,
|
|
24
25
|
EditableArtifactDurableExportService,
|
|
@@ -129,6 +130,13 @@ export type ManagedEmailTransport = {
|
|
|
129
130
|
|
|
130
131
|
export type AppDependencies = {
|
|
131
132
|
settings: Settings;
|
|
133
|
+
/**
|
|
134
|
+
* Original deployment settings when `settings` is already overlaid with a
|
|
135
|
+
* deployment/workspace catalog snapshot. Model-bearing request adapters set
|
|
136
|
+
* this marker so core admission never feeds a synthetic reviewed provider
|
|
137
|
+
* back through deployment validation.
|
|
138
|
+
*/
|
|
139
|
+
catalogSourceSettings?: Settings;
|
|
132
140
|
db: Database;
|
|
133
141
|
/**
|
|
134
142
|
* Host-composed editable artifact engine. Standalone startup binds the same
|
|
@@ -225,6 +233,7 @@ export type AppDependencies = {
|
|
|
225
233
|
export type ObjectStorageDependency = ReturnType<typeof createObjectStorage>;
|
|
226
234
|
|
|
227
235
|
export type ApiRouteDeps = AppDependencies & {
|
|
236
|
+
resolveCatalogSettings: () => Promise<ResolvedCatalogSettings>;
|
|
228
237
|
managedEmailTransport: ManagedEmailTransport;
|
|
229
238
|
objectStorage: ObjectStorageDependency;
|
|
230
239
|
githubStateSecret: string;
|
|
@@ -244,7 +253,12 @@ export type ApiRouteDeps = AppDependencies & {
|
|
|
244
253
|
*/
|
|
245
254
|
export type AcceptSessionUserMessageDependencies = Pick<
|
|
246
255
|
AppDependencies,
|
|
247
|
-
|
|
256
|
+
| "settings"
|
|
257
|
+
| "catalogSourceSettings"
|
|
258
|
+
| "db"
|
|
259
|
+
| "bus"
|
|
260
|
+
| "sessionAuthorization"
|
|
261
|
+
| "schedulePromptPostCommit"
|
|
248
262
|
> & {
|
|
249
263
|
workflowClient: Pick<SessionWorkflowClient, "wakeSessionWorkflow">;
|
|
250
264
|
objectStorage: ObjectStorageDependency;
|
|
@@ -34,6 +34,8 @@ import {
|
|
|
34
34
|
getSandbox,
|
|
35
35
|
getSessionTurnXaiProviderAccountAuthoritySnapshot,
|
|
36
36
|
getSession,
|
|
37
|
+
lockActiveWorkspaceGatewayCustomModelForAdmission,
|
|
38
|
+
lockActiveWorkspaceOpenRouterCustomModelForAdmission,
|
|
37
39
|
nestedPostgresSqlState,
|
|
38
40
|
requireWorkspace,
|
|
39
41
|
scopedKnowledgeScopeKey,
|
|
@@ -54,6 +56,7 @@ import {
|
|
|
54
56
|
} from "../session-authorization";
|
|
55
57
|
import type { SessionWorkflowClient } from "../dependencies";
|
|
56
58
|
import type { ObjectStorageDependency } from "../dependencies";
|
|
59
|
+
import { workspaceCustomModelReference } from "../model-catalog";
|
|
57
60
|
import { settingsWithEnabledCapabilityMcpServers } from "./capabilities";
|
|
58
61
|
import { validateVariableSetAttachment } from "./environments";
|
|
59
62
|
import {
|
|
@@ -99,6 +102,35 @@ export function scheduledTaskToolsProvided(rawPayload: unknown): boolean {
|
|
|
99
102
|
);
|
|
100
103
|
}
|
|
101
104
|
|
|
105
|
+
function workspaceCustomModelCommitGuard(input: {
|
|
106
|
+
settings: Settings;
|
|
107
|
+
accountId: string;
|
|
108
|
+
workspaceId: string;
|
|
109
|
+
modelId: string;
|
|
110
|
+
}): ((tx: Database) => Promise<void>) | undefined {
|
|
111
|
+
const reference = workspaceCustomModelReference(input.settings, input.modelId);
|
|
112
|
+
if (!reference) return undefined;
|
|
113
|
+
return async (tx: Database): Promise<void> => {
|
|
114
|
+
const active =
|
|
115
|
+
reference.providerKind === "openrouter"
|
|
116
|
+
? await lockActiveWorkspaceOpenRouterCustomModelForAdmission(tx, {
|
|
117
|
+
accountId: input.accountId,
|
|
118
|
+
workspaceId: input.workspaceId,
|
|
119
|
+
upstreamModelId: reference.upstreamModelId,
|
|
120
|
+
})
|
|
121
|
+
: await lockActiveWorkspaceGatewayCustomModelForAdmission(tx, {
|
|
122
|
+
accountId: input.accountId,
|
|
123
|
+
workspaceId: input.workspaceId,
|
|
124
|
+
upstreamModelId: reference.upstreamModelId,
|
|
125
|
+
});
|
|
126
|
+
if (!active) {
|
|
127
|
+
throw new HTTPException(422, {
|
|
128
|
+
message: `model is not available: ${input.modelId}`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
102
134
|
export function scheduledConnectionSurfaceEligibility(
|
|
103
135
|
settings: Settings,
|
|
104
136
|
target: Pick<Session, "firstPartyMcpTools" | "firstPartyMcpPermissions"> | null,
|
|
@@ -146,7 +178,7 @@ export async function createValidatedScheduledTask(input: {
|
|
|
146
178
|
action: knowledgeAction,
|
|
147
179
|
});
|
|
148
180
|
}
|
|
149
|
-
const agentConfig = knowledgeAction
|
|
181
|
+
const agentConfig: ScheduledTaskAgentConfig = knowledgeAction
|
|
150
182
|
? input.payload.agentConfig
|
|
151
183
|
: await validateScheduledTaskAgentConfig({
|
|
152
184
|
...input,
|
|
@@ -235,6 +267,15 @@ export async function createValidatedScheduledTask(input: {
|
|
|
235
267
|
workspaceId: input.grant.workspaceId,
|
|
236
268
|
subjectId: input.grant.subjectId,
|
|
237
269
|
});
|
|
270
|
+
const beforeCreateCommit =
|
|
271
|
+
!knowledgeAction && input.payload.runMode !== "existing_session"
|
|
272
|
+
? workspaceCustomModelCommitGuard({
|
|
273
|
+
settings: input.settings,
|
|
274
|
+
accountId: input.grant.accountId,
|
|
275
|
+
workspaceId: input.grant.workspaceId,
|
|
276
|
+
modelId: agentConfig.model ?? input.settings.openaiModel,
|
|
277
|
+
})
|
|
278
|
+
: undefined;
|
|
238
279
|
return await withScheduledTaskAuthorityWriteErrors(() =>
|
|
239
280
|
createScheduledTask(input.db, {
|
|
240
281
|
id,
|
|
@@ -257,6 +298,7 @@ export async function createValidatedScheduledTask(input: {
|
|
|
257
298
|
variableSetId: input.payload.variableSetId ?? null,
|
|
258
299
|
rigId: input.payload.rigId ?? null,
|
|
259
300
|
metadata: input.payload.metadata,
|
|
301
|
+
...(beforeCreateCommit ? { beforeCreateCommit } : {}),
|
|
260
302
|
}),
|
|
261
303
|
);
|
|
262
304
|
}
|
|
@@ -734,6 +776,15 @@ export async function validatedScheduledTaskUpdate(input: {
|
|
|
734
776
|
(input.payload.metadata !== undefined &&
|
|
735
777
|
!isDeepStrictEqual(input.payload.metadata, input.existing.metadata)) ||
|
|
736
778
|
(input.existing.status === "paused" && input.payload.status === "active");
|
|
779
|
+
if (materialExecutionChange && nextRunMode !== "existing_session") {
|
|
780
|
+
const beforeUpdateCommit = workspaceCustomModelCommitGuard({
|
|
781
|
+
settings: input.settings,
|
|
782
|
+
accountId: input.existing.accountId,
|
|
783
|
+
workspaceId: input.existing.workspaceId,
|
|
784
|
+
modelId: nextAgentConfig.model ?? input.settings.openaiModel,
|
|
785
|
+
});
|
|
786
|
+
if (beforeUpdateCommit) update.beforeUpdateCommit = beforeUpdateCommit;
|
|
787
|
+
}
|
|
737
788
|
const existingXaiAuthority = await getScheduledTaskXaiProviderAccountAuthoritySnapshot(
|
|
738
789
|
input.db,
|
|
739
790
|
input.existing.workspaceId,
|