@opengeni/core 0.16.3 → 0.17.1

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.
@@ -0,0 +1,48 @@
1
+ import type { McpServerConfig, Settings } from "@opengeni/config";
2
+ import type { AccessGrant, ConnectionMetadata, McpPersonalConnectionDelegation, SessionTurn, ToolRef } from "@opengeni/contracts";
3
+ import { type Database, type ResolveConnectionCredentialInput, type ResolveConnectionCredentialResult } from "@opengeni/db";
4
+ export type PersonalConnectionDelegationSource = {
5
+ kind: "subject";
6
+ subjectId: string;
7
+ } | {
8
+ kind: "turn";
9
+ sessionId: string;
10
+ turnId: string;
11
+ } | {
12
+ kind: "none";
13
+ };
14
+ export declare function directPersonalConnectionSubjectId(turn: Pick<SessionTurn, "source" | "initiator" | "initiatorContext">): string | undefined;
15
+ export declare function personalConnectionDelegationSourceForGrant(grant: AccessGrant): PersonalConnectionDelegationSource;
16
+ export declare function selectedPersonalConnectionServers(settings: Pick<Settings, "mcpServers">, tools: ToolRef[]): McpServerConfig[];
17
+ export declare function personalConnectionDelegationsFromVisibleConnections(input: {
18
+ servers: McpServerConfig[];
19
+ subjectId: string;
20
+ connections: ConnectionMetadata[];
21
+ }): McpPersonalConnectionDelegation[];
22
+ export declare function personalConnectionDelegationsFromParent(input: {
23
+ servers: McpServerConfig[];
24
+ parentDelegations: McpPersonalConnectionDelegation[];
25
+ }): McpPersonalConnectionDelegation[];
26
+ export declare function personalConnectionDelegationsEqual(left: McpPersonalConnectionDelegation[], right: McpPersonalConnectionDelegation[]): boolean;
27
+ export declare function personalConnectionDelegationForServer(delegations: McpPersonalConnectionDelegation[], server: Pick<McpServerConfig, "id" | "connectionRef">): McpPersonalConnectionDelegation | null;
28
+ type ConnectionCredentialResolver = (request: ResolveConnectionCredentialInput) => Promise<ResolveConnectionCredentialResult>;
29
+ /**
30
+ * Resolves subject-owned MCP credentials only through the exact authority
31
+ * frozen on the causal turn. A direct human subject, worker Toolspace caller,
32
+ * retry, or recovery can identify the caller, but none may widen or replace
33
+ * the persisted connection UUID.
34
+ */
35
+ export declare function withFrozenPersonalConnectionDelegations(input: {
36
+ resolveCredential: ConnectionCredentialResolver;
37
+ settings: Pick<Settings, "mcpServers">;
38
+ personalConnectionDelegations: McpPersonalConnectionDelegation[];
39
+ ownerHasWorkspaceMembership: (subjectId: string) => Promise<boolean>;
40
+ }): ConnectionCredentialResolver;
41
+ export declare function freezePersonalConnectionDelegations(input: {
42
+ db: Database;
43
+ workspaceId: string;
44
+ settings: Pick<Settings, "mcpServers">;
45
+ tools: ToolRef[];
46
+ source: PersonalConnectionDelegationSource;
47
+ }): Promise<McpPersonalConnectionDelegation[]>;
48
+ export {};
@@ -1,5 +1,5 @@
1
1
  import type { Settings } from "@opengeni/config";
2
- import type { AccessGrant, ScheduledTask, CreateScheduledTaskRequest as CreateScheduledTaskPayload, UpdateScheduledTaskRequest as UpdateScheduledTaskPayload } from "@opengeni/contracts";
2
+ import type { AccessGrant, McpPersonalConnectionDelegation, ScheduledTask, CreateScheduledTaskRequest as CreateScheduledTaskPayload, UpdateScheduledTaskRequest as UpdateScheduledTaskPayload } from "@opengeni/contracts";
3
3
  import { type Database, type UpdateScheduledTaskInput } from "@opengeni/db";
4
4
  import type { SessionWorkflowClient } from "../dependencies";
5
5
  import type { ObjectStorageDependency } from "../dependencies";
@@ -32,7 +32,12 @@ export declare function validatedScheduledTaskUpdate(input: {
32
32
  toolsProvided?: boolean;
33
33
  }): Promise<UpdateScheduledTaskInput>;
34
34
  export declare function requireScheduledTaskForApi(db: Database, workspaceId: string, taskId: string): Promise<ScheduledTask>;
35
- export declare function restoreScheduledTask(db: Database, task: ScheduledTask): Promise<ScheduledTask>;
35
+ export type ScheduledTaskRestoreState = {
36
+ task: ScheduledTask;
37
+ personalConnectionDelegations: McpPersonalConnectionDelegation[];
38
+ };
39
+ export declare function captureScheduledTaskRestoreState(db: Database, task: ScheduledTask): Promise<ScheduledTaskRestoreState>;
40
+ export declare function restoreScheduledTask(db: Database, previous: ScheduledTaskRestoreState): Promise<ScheduledTask>;
36
41
  export declare function syncCreatedScheduledTask(input: {
37
42
  db: Database;
38
43
  workflowClient: SessionWorkflowClient;
@@ -41,7 +46,7 @@ export declare function syncCreatedScheduledTask(input: {
41
46
  export declare function syncUpdatedScheduledTask(input: {
42
47
  db: Database;
43
48
  workflowClient: SessionWorkflowClient;
44
- previous: ScheduledTask;
49
+ previous: ScheduledTaskRestoreState;
45
50
  task: ScheduledTask;
46
51
  }): Promise<void>;
47
52
  export declare function scheduledTaskTemporalScheduleId(taskId: string): string;
@@ -1,5 +1,5 @@
1
1
  import { type Settings } from "@opengeni/config";
2
- import { SessionSpawnDenial, type AccessGrant, type CreateSessionResponse, type GoalSpec, type FirstPartyMcpToolName, type Permission, type ReasoningEffort, type ResourceRef, type Session, type SessionSkill, type SessionEvent, SessionMcpApprovalPolicy, type SessionMcpCredentialUpdateInput, type SessionMcpServerMetadata, type UpdateSessionMcpApprovalPolicyResponse, type UpdateSessionToolPolicyRequest, type SessionAuthorizationPort, type SessionToolPolicy, type SessionTurn, type ToolRef, type TurnInitiator, type TurnInitiatorContext, type TurnExecutionPolicyV1 } from "@opengeni/contracts";
2
+ import { SessionSpawnDenial, type AccessGrant, type CreateSessionResponse, type GoalSpec, type FirstPartyMcpToolName, type McpPersonalConnectionDelegation, type Permission, type ReasoningEffort, type ResourceRef, type Session, type SessionSkill, type SessionEvent, SessionMcpApprovalPolicy, type SessionMcpCredentialUpdateInput, type SessionMcpServerMetadata, type UpdateSessionMcpApprovalPolicyResponse, type UpdateSessionToolPolicyRequest, type SessionAuthorizationPort, type SessionToolPolicy, type SessionTurn, type ToolRef, type TurnInitiator, type TurnInitiatorContext, type TurnExecutionPolicyV1 } from "@opengeni/contracts";
3
3
  import { type CreateSessionMcpServerInput, type Database, type UpdateSessionMcpServerCredentialsInput, type SessionCommandActor } from "@opengeni/db";
4
4
  import { type EventBus } from "@opengeni/events";
5
5
  import type { AcceptSessionUserMessageDependencies, ApiRouteDeps, SessionWorkflowClient } from "../dependencies";
@@ -40,6 +40,14 @@ export declare function sessionSpawnDenialEnvelope(error: SessionSpawnDeniedErro
40
40
  };
41
41
  };
42
42
  };
43
+ export type FrozenCreationInitiator = {
44
+ initiator?: TurnInitiator;
45
+ context?: TurnInitiatorContext;
46
+ actor?: Extract<SessionCommandActor, {
47
+ type: "agent_attempt";
48
+ }>;
49
+ };
50
+ export declare function creationInitiatorForGrant(grant: AccessGrant): FrozenCreationInitiator;
43
51
  export declare function settingsWithSessionMcpServerMetadata(settings: Settings, servers: SessionMcpServerMetadata[]): Settings;
44
52
  export declare function createAndStartSession(input: {
45
53
  requestedSessionId?: string;
@@ -79,6 +87,7 @@ export declare function createAndStartSession(input: {
79
87
  firstPartyMcpTools: FirstPartyMcpToolName[];
80
88
  mcpServers?: CreateSessionMcpServerInput[];
81
89
  sessionMcpServers?: SessionMcpServerMetadata[];
90
+ personalConnectionDelegations?: McpPersonalConnectionDelegation[];
82
91
  parentSessionId?: string | null;
83
92
  createIdempotencyKey?: string | null;
84
93
  sandboxGroupId?: string | null;
@@ -165,6 +174,7 @@ export declare function postUserMessageTurn(input: {
165
174
  latencyMode?: "standard" | "priority" | "fast" | null;
166
175
  clientEventId?: string;
167
176
  mcpCredentialUpdates?: UpdateSessionMcpServerCredentialsInput[];
177
+ personalConnectionDelegations?: McpPersonalConnectionDelegation[];
168
178
  delivery?: "send" | "steer";
169
179
  origin?: "human" | "operator";
170
180
  actor?: string;
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export * from "./domain/capabilities";
12
12
  export * from "./domain/environments";
13
13
  export * from "./rigs";
14
14
  export * from "./domain/packs";
15
+ export * from "./domain/personal-connection-delegations";
15
16
  export * from "./domain/resources";
16
17
  export * from "./domain/session-tool-policy";
17
18
  export * from "./domain/scheduled-tasks";
package/dist/index.js CHANGED
@@ -3151,6 +3151,158 @@ async function listRigChangesForApi(deps, workspaceId, rigId, limit) {
3151
3151
  return await listRigChanges(deps.db, workspaceId, rigId, limit);
3152
3152
  }
3153
3153
 
3154
+ // src/domain/personal-connection-delegations.ts
3155
+ import {
3156
+ getSessionTurnPersonalConnectionDelegations,
3157
+ getWorkspaceGrant as getWorkspaceGrant2,
3158
+ listConnectionsMetadata as listConnectionsMetadata2
3159
+ } from "@opengeni/db";
3160
+ function directPersonalConnectionSubjectId(turn) {
3161
+ if (turn.source !== "user" && turn.source !== "api" || turn.initiator.kind !== "subject") {
3162
+ return void 0;
3163
+ }
3164
+ return ["via", "viaTruncated", "provenanceError", "backfill"].some(
3165
+ (key) => Object.prototype.hasOwnProperty.call(turn.initiatorContext, key)
3166
+ ) ? void 0 : turn.initiator.subjectId;
3167
+ }
3168
+ function personalConnectionDelegationSourceForGrant(grant) {
3169
+ const callerSessionId = grant.metadata?.["sessionId"];
3170
+ const callerTurnId = grant.metadata?.["turnId"];
3171
+ if (typeof callerSessionId === "string" && typeof callerTurnId === "string") {
3172
+ return { kind: "turn", sessionId: callerSessionId, turnId: callerTurnId };
3173
+ }
3174
+ if (grant.principalKind === "agent_attempt" || grant.principalKind === "service" || grant.serviceInitiator) {
3175
+ return { kind: "none" };
3176
+ }
3177
+ return { kind: "subject", subjectId: grant.subjectId };
3178
+ }
3179
+ function selectedPersonalConnectionServers(settings, tools) {
3180
+ const selected = new Set(tools.map((tool) => tool.id));
3181
+ return settings.mcpServers.filter(
3182
+ (server) => selected.has(server.id) && server.connectionRef?.subjectScope === "subject"
3183
+ );
3184
+ }
3185
+ function canonicalPersonalConnections(connections) {
3186
+ return [...connections].sort((left, right) => {
3187
+ const active = Number(right.status === "active") - Number(left.status === "active");
3188
+ if (active !== 0) return active;
3189
+ const updated = Date.parse(right.updatedAt) - Date.parse(left.updatedAt);
3190
+ if (updated !== 0) return updated;
3191
+ const created = Date.parse(right.createdAt) - Date.parse(left.createdAt);
3192
+ if (created !== 0) return created;
3193
+ return right.id.localeCompare(left.id);
3194
+ });
3195
+ }
3196
+ function sameProviderDomain(left, right) {
3197
+ return left.toLowerCase() === right.toLowerCase();
3198
+ }
3199
+ function personalConnectionDelegationsFromVisibleConnections(input) {
3200
+ const delegations = [];
3201
+ const connections = canonicalPersonalConnections(input.connections);
3202
+ for (const server of input.servers) {
3203
+ const ref = server.connectionRef;
3204
+ if (!ref || ref.subjectScope !== "subject") continue;
3205
+ const connection = connections.find(
3206
+ (candidate) => candidate.subjectId === input.subjectId && candidate.status === "active" && sameProviderDomain(candidate.providerDomain, ref.providerDomain) && (!ref.kind || candidate.kind === ref.kind) && (!ref.connectionId || candidate.id === ref.connectionId)
3207
+ );
3208
+ if (!connection) continue;
3209
+ delegations.push({
3210
+ serverId: server.id,
3211
+ connectionId: connection.id,
3212
+ ownerSubjectId: input.subjectId,
3213
+ providerDomain: connection.providerDomain,
3214
+ kind: connection.kind
3215
+ });
3216
+ }
3217
+ return delegations;
3218
+ }
3219
+ function personalConnectionDelegationsFromParent(input) {
3220
+ return input.servers.flatMap((server) => {
3221
+ const ref = server.connectionRef;
3222
+ if (!ref || ref.subjectScope !== "subject") return [];
3223
+ const delegation = input.parentDelegations.find(
3224
+ (candidate) => candidate.serverId === server.id && sameProviderDomain(candidate.providerDomain, ref.providerDomain) && (!ref.kind || !candidate.kind || candidate.kind === ref.kind)
3225
+ );
3226
+ return delegation ? [{ ...delegation }] : [];
3227
+ });
3228
+ }
3229
+ function personalConnectionDelegationsEqual(left, right) {
3230
+ if (left.length !== right.length) return false;
3231
+ const byServer = new Map(right.map((delegation) => [delegation.serverId, delegation]));
3232
+ return left.every((delegation) => {
3233
+ const other = byServer.get(delegation.serverId);
3234
+ return other?.connectionId === delegation.connectionId && other.ownerSubjectId === delegation.ownerSubjectId && sameProviderDomain(other.providerDomain, delegation.providerDomain) && other.kind === delegation.kind;
3235
+ });
3236
+ }
3237
+ function personalConnectionDelegationForServer(delegations, server) {
3238
+ const ref = server.connectionRef;
3239
+ if (!ref || ref.subjectScope !== "subject") return null;
3240
+ return delegations.find(
3241
+ (delegation) => delegation.serverId === server.id && sameProviderDomain(delegation.providerDomain, ref.providerDomain) && (!ref.kind || !delegation.kind || delegation.kind === ref.kind)
3242
+ ) ?? null;
3243
+ }
3244
+ function personalAuthorityUnavailable(request) {
3245
+ const ref = request.connectionRef;
3246
+ return {
3247
+ status: "auth_needed",
3248
+ reason: "personal_authority_unavailable",
3249
+ providerDomain: ref.providerDomain,
3250
+ ...ref.provider ? { provider: ref.provider } : {},
3251
+ ...ref.scopes ? { scopes: ref.scopes } : {},
3252
+ ...ref.resource ? { resource: ref.resource } : {},
3253
+ ...ref.selectedResources ? { selectedResources: ref.selectedResources } : {}
3254
+ };
3255
+ }
3256
+ function withFrozenPersonalConnectionDelegations(input) {
3257
+ return async (request) => {
3258
+ let effectiveRequest = request;
3259
+ if (request.connectionRef.subjectScope === "subject") {
3260
+ const config = input.settings.mcpServers.find((server) => server.id === request.serverId);
3261
+ const delegation = config ? personalConnectionDelegationForServer(input.personalConnectionDelegations, config) : null;
3262
+ if (!delegation || !await input.ownerHasWorkspaceMembership(delegation.ownerSubjectId)) {
3263
+ return personalAuthorityUnavailable(request);
3264
+ }
3265
+ effectiveRequest = {
3266
+ ...request,
3267
+ subjectId: delegation.ownerSubjectId,
3268
+ connectionRef: {
3269
+ ...request.connectionRef,
3270
+ providerDomain: delegation.providerDomain,
3271
+ connectionId: delegation.connectionId,
3272
+ ...delegation.kind ? { kind: delegation.kind } : {}
3273
+ }
3274
+ };
3275
+ }
3276
+ const result = await input.resolveCredential(effectiveRequest);
3277
+ if (result.status === "ok" || request.connectionRef.subjectScope !== "subject") {
3278
+ return result;
3279
+ }
3280
+ return personalAuthorityUnavailable(request);
3281
+ };
3282
+ }
3283
+ async function freezePersonalConnectionDelegations(input) {
3284
+ const servers = selectedPersonalConnectionServers(input.settings, input.tools);
3285
+ if (servers.length === 0 || input.source.kind === "none") return [];
3286
+ if (input.source.kind === "turn") {
3287
+ return personalConnectionDelegationsFromParent({
3288
+ servers,
3289
+ parentDelegations: await getSessionTurnPersonalConnectionDelegations(
3290
+ input.db,
3291
+ input.workspaceId,
3292
+ input.source.sessionId,
3293
+ input.source.turnId
3294
+ )
3295
+ });
3296
+ }
3297
+ const membership = await getWorkspaceGrant2(input.db, input.source.subjectId, input.workspaceId);
3298
+ if (!membership) return [];
3299
+ return personalConnectionDelegationsFromVisibleConnections({
3300
+ servers,
3301
+ subjectId: input.source.subjectId,
3302
+ connections: await listConnectionsMetadata2(input.db, input.workspaceId, input.source.subjectId)
3303
+ });
3304
+ }
3305
+
3154
3306
  // src/domain/resources.ts
3155
3307
  import {
3156
3308
  gitCredentialBindingIdForRepository,
@@ -3552,6 +3704,7 @@ import {
3552
3704
  getNestedAgentDepthDeploymentPolicy,
3553
3705
  getRig as getRig3,
3554
3706
  getScheduledTask,
3707
+ getScheduledTaskPersonalConnectionDelegations,
3555
3708
  requireWorkspace as requireWorkspace2,
3556
3709
  updateScheduledTask
3557
3710
  } from "@opengeni/db";
@@ -4009,6 +4162,7 @@ async function createAndStartSession(input) {
4009
4162
  sandboxGroupId: input.sandboxGroupId ?? null,
4010
4163
  ...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {},
4011
4164
  mcpServers: input.mcpServers ?? [],
4165
+ personalConnectionDelegations: input.personalConnectionDelegations ?? [],
4012
4166
  maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
4013
4167
  allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
4014
4168
  subjectId: input.subjectId ?? null
@@ -4053,6 +4207,7 @@ async function createAndStartSession(input) {
4053
4207
  sandboxGroupId: input.sandboxGroupId ?? null,
4054
4208
  ...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {},
4055
4209
  mcpServers: input.mcpServers ?? [],
4210
+ personalConnectionDelegations: input.personalConnectionDelegations ?? [],
4056
4211
  maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
4057
4212
  allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
4058
4213
  subjectId: input.subjectId ?? null
@@ -4251,6 +4406,7 @@ async function postUserMessageTurn(input) {
4251
4406
  reasoningEffortFallback: input.reasoningEffortFallback ?? settings.openaiReasoningEffort,
4252
4407
  turnExecutionPolicy: input.turnExecutionPolicy,
4253
4408
  source: input.origin === "operator" ? "api" : "user",
4409
+ personalConnectionDelegations: input.personalConnectionDelegations ?? [],
4254
4410
  mcpCredentialUpdates: input.mcpCredentialUpdates ?? []
4255
4411
  })
4256
4412
  )
@@ -4405,6 +4561,13 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
4405
4561
  toolPolicy = { mode: "workspace_default", inheritedFromSessionId: null };
4406
4562
  }
4407
4563
  const tools = withFirstPartyTools(selectedTools, runtimeSettings);
4564
+ const personalConnectionDelegations = await freezePersonalConnectionDelegations({
4565
+ db,
4566
+ workspaceId,
4567
+ settings: runtimeSettings,
4568
+ tools,
4569
+ source: personalConnectionDelegationSourceForGrant(grant)
4570
+ });
4408
4571
  await validateGitHubRepositorySelection(db, workspaceId, resources);
4409
4572
  if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
4410
4573
  throw new HTTPException10(503, { message: "object storage is not configured" });
@@ -4640,6 +4803,7 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
4640
4803
  firstPartyMcpTools,
4641
4804
  mcpServers: sessionMcpServers.dbServers,
4642
4805
  sessionMcpServers: sessionMcpServers.metadata,
4806
+ personalConnectionDelegations,
4643
4807
  parentSessionId,
4644
4808
  createIdempotencyKey: payload.idempotencyKey ?? null,
4645
4809
  maxNestedAgentDepthOverride: payload.maxNestedAgentDepth ?? null,
@@ -4742,6 +4906,14 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
4742
4906
  session: existingSession,
4743
4907
  updates: input.mcpCredentialUpdates ?? []
4744
4908
  });
4909
+ const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings);
4910
+ const personalConnectionDelegations = await freezePersonalConnectionDelegations({
4911
+ db,
4912
+ workspaceId,
4913
+ settings: runtimeSettings,
4914
+ tools: existingSession.tools,
4915
+ source: personalConnectionDelegationSourceForGrant(grant)
4916
+ });
4745
4917
  const delegatedServiceInitiator = serviceInitiatorForGrant(grant);
4746
4918
  const { accepted, turn } = await postUserMessageTurn({
4747
4919
  db,
@@ -4760,6 +4932,7 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
4760
4932
  reasoningEffortFallback: sessionReasoningEffort,
4761
4933
  turnExecutionPolicy,
4762
4934
  mcpCredentialUpdates,
4935
+ personalConnectionDelegations,
4763
4936
  delivery: input.delivery ?? "send",
4764
4937
  origin: delegatedServiceInitiator ? "operator" : input.origin ?? "human",
4765
4938
  actor: grant.subjectId,
@@ -5101,6 +5274,19 @@ async function createValidatedScheduledTask(input) {
5101
5274
  if (input.payload.rigId) {
5102
5275
  await requireScheduledTaskRig(input.db, input.grant.workspaceId, input.payload.rigId);
5103
5276
  }
5277
+ const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
5278
+ input.db,
5279
+ input.grant.workspaceId,
5280
+ input.settings
5281
+ );
5282
+ const personalConnectionDelegations = await freezePersonalConnectionDelegations({
5283
+ db: input.db,
5284
+ workspaceId: input.grant.workspaceId,
5285
+ settings: runtimeSettings,
5286
+ tools: agentConfig.tools,
5287
+ source: personalConnectionDelegationSourceForGrant(input.grant)
5288
+ });
5289
+ const creationInitiator = creationInitiatorForGrant(input.grant);
5104
5290
  return await createScheduledTask(input.db, {
5105
5291
  id,
5106
5292
  accountId: input.grant.accountId,
@@ -5112,6 +5298,10 @@ async function createValidatedScheduledTask(input) {
5112
5298
  runMode: input.payload.runMode,
5113
5299
  overlapPolicy: input.payload.overlapPolicy,
5114
5300
  agentConfig,
5301
+ ...creationInitiator.initiator ? { createdBy: creationInitiator.initiator } : {},
5302
+ ...creationInitiator.context ? { createdByContext: creationInitiator.context } : {},
5303
+ createdByActor: creationInitiator.actor ?? null,
5304
+ personalConnectionDelegations,
5115
5305
  variableSetId: input.payload.variableSetId ?? null,
5116
5306
  rigId: input.payload.rigId ?? null,
5117
5307
  metadata: input.payload.metadata
@@ -5192,6 +5382,31 @@ async function validatedScheduledTaskUpdate(input) {
5192
5382
  });
5193
5383
  }
5194
5384
  update.agentConfig = nextAgentConfig;
5385
+ const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
5386
+ input.db,
5387
+ input.existing.workspaceId,
5388
+ input.settings
5389
+ );
5390
+ const personalConnectionDelegations = await freezePersonalConnectionDelegations({
5391
+ db: input.db,
5392
+ workspaceId: input.existing.workspaceId,
5393
+ settings: runtimeSettings,
5394
+ tools: nextAgentConfig.tools,
5395
+ source: personalConnectionDelegationSourceForGrant(input.grant)
5396
+ });
5397
+ if (input.existing.reusableSessionId && input.existing.runMode === "reusable_session") {
5398
+ const existingDelegations = await getScheduledTaskPersonalConnectionDelegations(
5399
+ input.db,
5400
+ input.existing.workspaceId,
5401
+ input.existing.id
5402
+ );
5403
+ if (!personalConnectionDelegationsEqual(existingDelegations, personalConnectionDelegations)) {
5404
+ throw new HTTPException11(409, {
5405
+ message: "cannot change personal MCP connections of a task with a live reusable session; recreate the task"
5406
+ });
5407
+ }
5408
+ }
5409
+ update.personalConnectionDelegations = personalConnectionDelegations;
5195
5410
  }
5196
5411
  return update;
5197
5412
  }
@@ -5202,7 +5417,18 @@ async function requireScheduledTaskForApi(db, workspaceId, taskId) {
5202
5417
  }
5203
5418
  return task;
5204
5419
  }
5205
- async function restoreScheduledTask(db, task) {
5420
+ async function captureScheduledTaskRestoreState(db, task) {
5421
+ return {
5422
+ task,
5423
+ personalConnectionDelegations: await getScheduledTaskPersonalConnectionDelegations(
5424
+ db,
5425
+ task.workspaceId,
5426
+ task.id
5427
+ )
5428
+ };
5429
+ }
5430
+ async function restoreScheduledTask(db, previous) {
5431
+ const { task } = previous;
5206
5432
  return await updateScheduledTask(db, task.workspaceId, task.id, {
5207
5433
  name: task.name,
5208
5434
  status: task.status,
@@ -5210,8 +5436,10 @@ async function restoreScheduledTask(db, task) {
5210
5436
  runMode: task.runMode,
5211
5437
  overlapPolicy: task.overlapPolicy,
5212
5438
  agentConfig: task.agentConfig,
5439
+ personalConnectionDelegations: previous.personalConnectionDelegations,
5213
5440
  reusableSessionId: task.reusableSessionId,
5214
5441
  variableSetId: task.variableSetId,
5442
+ rigId: task.rigId,
5215
5443
  metadata: task.metadata
5216
5444
  });
5217
5445
  }
@@ -6484,6 +6712,7 @@ export {
6484
6712
  buildFleetContextForSession,
6485
6713
  buildMarketingDailyAnalysisAgentConfig,
6486
6714
  canonicalConfiguredModel,
6715
+ captureScheduledTaskRestoreState,
6487
6716
  checkLimit,
6488
6717
  classifyRigVerificationOutcome,
6489
6718
  controlAgentSessionWorkstream,
@@ -6495,9 +6724,11 @@ export {
6495
6724
  createRigVersionForApi,
6496
6725
  createSessionForRequest,
6497
6726
  createValidatedScheduledTask,
6727
+ creationInitiatorForGrant,
6498
6728
  defaultSessionMcpServerIds,
6499
6729
  deleteHumanQueuePrompt,
6500
6730
  deleteRigForApi,
6731
+ directPersonalConnectionSubjectId,
6501
6732
  disableCapability,
6502
6733
  discoverMcpRegistryCapabilities,
6503
6734
  editHumanQueuePrompt,
@@ -6505,6 +6736,7 @@ export {
6505
6736
  enabledCapabilityMcpToolRefs,
6506
6737
  executeRunOnSelfhostedMachine,
6507
6738
  filenameForMimeType,
6739
+ freezePersonalConnectionDelegations,
6508
6740
  getActorNewSessionDraft,
6509
6741
  getCapabilityPack,
6510
6742
  getHumanComposerDraft,
@@ -6534,6 +6766,11 @@ export {
6534
6766
  normalizeResources,
6535
6767
  officialMcpRegistryUrl,
6536
6768
  openGeniSlackBotMetadata,
6769
+ personalConnectionDelegationForServer,
6770
+ personalConnectionDelegationSourceForGrant,
6771
+ personalConnectionDelegationsEqual,
6772
+ personalConnectionDelegationsFromParent,
6773
+ personalConnectionDelegationsFromVisibleConnections,
6537
6774
  postUserMessageTurn,
6538
6775
  promoteSetupAppendChange,
6539
6776
  promoteVerifiedDefinitionEditChangeForApi,
@@ -6575,6 +6812,7 @@ export {
6575
6812
  scheduledTaskTemporalScheduleId,
6576
6813
  scheduledTaskToolsProvided,
6577
6814
  scheduledTaskTriggerToken,
6815
+ selectedPersonalConnectionServers,
6578
6816
  sendAgentSessionMessage,
6579
6817
  sessionSpawnDenialEnvelope,
6580
6818
  sessionWithEffectiveToolPolicy,
@@ -6604,6 +6842,7 @@ export {
6604
6842
  validateVariableSetAttachment,
6605
6843
  validatedScheduledTaskUpdate,
6606
6844
  withDefaultEnabledCapabilityMcpTools,
6845
+ withFrozenPersonalConnectionDelegations,
6607
6846
  workflowIdForSession,
6608
6847
  workspaceSessionToolPolicyDefaultServerIds,
6609
6848
  workspaceSessionToolPolicyServerIds,