@opengeni/core 0.11.2 → 0.11.8

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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Settings } from '@opengeni/config';
2
- import { ScheduledTask, Document, GitHubAppApiPort, ConnectionCredentialsPort, SessionAuthorizationPort, Permission, AccessContext, AccessGrant, SessionAuthorizationActor, SessionAuthorizationTarget, SessionAuthorizationOperation, SessionAuthorizationSurface, SessionAuthorizationListScope, LimitAction, LimitDecision, TurnInitiator, TurnInitiatorContext, SessionTurnSource, CapabilityCatalogItem, CapabilityInstallation, CapabilityCatalogResponse, CreateCapabilityCatalogItemRequest, EnableCapabilityRequest, VariableSet, Rig, RigVersion, CreateRigRequest, RigDefinitionEditPayload, RigChange, ProposeRigChangeRequest, UpdateRigRequest, CapabilityPack, SocialConnection, ScheduledTaskAgentConfig, ToolRef, ResourceRef, SessionEffectiveToolPolicy, SessionToolPolicy, Session, CreateScheduledTaskRequest, UpdateScheduledTaskRequest, SessionSpawnDenial, ReasoningEffort, SessionMcpCredentialUpdateInput, SessionEvent, SessionTurn, TurnExecutionPolicyV1, GoalSpec, SessionMcpServerMetadata, CreateSessionResponse, SessionMcpApprovalPolicy, UpdateSessionMcpApprovalPolicyResponse, WorkspaceMember, NewSessionDraft, SessionControlRequest, SessionControlResponse, WorkspaceInferenceControlRequest, WorkspaceInferenceControlResponse, DeleteSessionQueueItemRequest, SessionQueueMutationResponse, EditSessionQueueItemRequest, ComposerDraft, MoveSessionQueueItemRequest, SaveComposerDraftRequest, SteerSessionQueueItemRequest } from '@opengeni/contracts';
2
+ import { ScheduledTask, Document, GitHubAppApiPort, ConnectionCredentialsPort, SessionAuthorizationPort, Permission, AccessContext, AccessGrant, SessionAuthorizationActor, SessionAuthorizationTarget, SessionAuthorizationOperation, SessionAuthorizationSurface, SessionAuthorizationListScope, LimitAction, LimitDecision, TurnInitiator, TurnInitiatorContext, SessionTurnSource, CapabilityCatalogItem, CapabilityInstallation, CapabilityCatalogResponse, CreateCapabilityCatalogItemRequest, EnableCapabilityRequest, VariableSet, Rig, RigVersion, CreateRigRequest, RigDefinitionEditPayload, RigChange, ProposeRigChangeRequest, UpdateRigRequest, CapabilityPack, SocialConnection, ScheduledTaskAgentConfig, ToolRef, ResourceRef, SessionEffectiveToolPolicy, SessionToolPolicy, Session, CreateScheduledTaskRequest, UpdateScheduledTaskRequest, SessionSpawnDenial, ReasoningEffort, SessionMcpCredentialUpdateInput, SessionEvent, SessionTurn, TurnExecutionPolicyV1, GoalSpec, SessionMcpServerMetadata, CreateSessionResponse, SessionMcpApprovalPolicy, UpdateSessionMcpApprovalPolicyResponse, UpdateSessionToolPolicyRequest, WorkspaceMember, NewSessionDraft, SessionControlRequest, SessionControlResponse, WorkspaceInferenceControlRequest, WorkspaceInferenceControlResponse, DeleteSessionQueueItemRequest, SessionQueueMutationResponse, EditSessionQueueItemRequest, ComposerDraft, MoveSessionQueueItemRequest, SaveComposerDraftRequest, SteerSessionQueueItemRequest } from '@opengeni/contracts';
3
3
  export { mergeToolRefs, stableJson } from '@opengeni/contracts';
4
4
  import * as _opengeni_db from '@opengeni/db';
5
5
  import { Database, SandboxRecord, EnabledMcpCapabilityServer, UpdateScheduledTaskInput, SessionCommandActor, CreateSessionMcpServerInput, UpdateSessionMcpServerCredentialsInput } from '@opengeni/db';
@@ -670,6 +670,12 @@ declare function validateGitHubRepositorySelectionShapes(resources: ResourceRef[
670
670
  /** @deprecated Use validateGitHubRepositorySelectionShapes for multi-installation sessions. */
671
671
  declare function validateGitHubRepositorySelectionShape(resources: ResourceRef[]): number | null;
672
672
  declare function validateGitHubRepositorySelection(db: Database, workspaceId: string, resources: ResourceRef[]): Promise<void>;
673
+ /**
674
+ * A 422 from repository selection validation is an authoritative stale or
675
+ * revoked identity. Other failures (for example a database/catalog outage)
676
+ * leave the result unknown and must not cause draft hydration to delete it.
677
+ */
678
+ declare function isAuthoritativeGitHubRepositorySelectionError(error: unknown): boolean;
673
679
  declare function validateFileResources(db: Database, workspaceId: string, resources: ResourceRef[]): Promise<void>;
674
680
 
675
681
  type ResolvedSessionToolPolicy = {
@@ -693,6 +699,14 @@ type SessionToolPolicyInput = {
693
699
  * `defaultMcpServerIds` is the capability-only omitted-tools set.
694
700
  */
695
701
  declare function resolveSessionToolPolicy(input: SessionToolPolicyInput): ResolvedSessionToolPolicy;
702
+ /**
703
+ * Native provider tools that belong to the workspace-default capability set
704
+ * follow the same omission/narrowing fence as deferred MCP tools. A durable
705
+ * workspace-default policy receives them; fixed historical policies and an
706
+ * explicit per-turn replacement do not. Provider support remains a separate
707
+ * runtime gate and must also be true before a native tool is attached.
708
+ */
709
+ declare function sessionToolPolicyAllowsDefaultNativeTools(policy: SessionEffectiveToolPolicy): boolean;
696
710
  /** Current full runtime registry IDs, including configured static servers. */
697
711
  declare function workspaceSessionToolPolicyServerIds(db: Database, workspaceId: string, settings: Settings): Promise<string[]>;
698
712
  /** Current omitted-tools defaults; this preserves capability-first behavior. */
@@ -985,6 +999,18 @@ declare function updateSessionMcpApprovalPolicy(deps: {
985
999
  bus: EventBus;
986
1000
  sessionAuthorization?: SessionAuthorizationPort | null;
987
1001
  }, grant: AccessGrant, sessionId: string, serverId: string, requireApproval: SessionMcpApprovalPolicy): Promise<UpdateSessionMcpApprovalPolicyResponse>;
1002
+ /**
1003
+ * Replace the durable session tool policy. The target and its parent (when
1004
+ * present) are locked by the DB event-writer helper, and the update/event are
1005
+ * committed under one version-fenced transaction. An already claimed turn
1006
+ * keeps its immutable snapshot; the next attempt observes this policy.
1007
+ */
1008
+ declare function updateSessionToolPolicy(deps: {
1009
+ db: Database;
1010
+ bus: EventBus;
1011
+ settings: Settings;
1012
+ sessionAuthorization?: SessionAuthorizationPort | null;
1013
+ }, grant: AccessGrant, sessionId: string, request: UpdateSessionToolPolicyRequest): Promise<Session>;
988
1014
  declare function readSessionLineage(deps: Pick<ApiRouteDeps, "db" | "sessionAuthorization">, grant: AccessGrant, sessionId: string): Promise<_opengeni_db.SessionLineage>;
989
1015
 
990
1016
  /** A member can manage other members (directly or via the admin wildcard). */
@@ -1117,4 +1143,4 @@ declare function controlHumanWorkspace(deps: {
1117
1143
  declare function getHumanComposerDraft(deps: SessionAuthorizationCommandDeps, context: HumanSessionCommandContext): Promise<ComposerDraft>;
1118
1144
  declare function saveHumanComposerDraft(deps: SessionAuthorizationCommandDeps, context: HumanSessionCommandContext, input: SaveComposerDraftRequest): Promise<ComposerDraft>;
1119
1145
 
1120
- export { type AcceptSessionUserMessageDependencies, type AccessDeps, type AgentSessionCommandContext, type ApiRouteDeps, type ApiSandboxClient, type ApiSandboxSession, type AppDependencies, type ChannelARoutingServices, type DocumentIndexClient, type FleetContext, type FleetListResult, type FleetLiveness, type FleetReadinessHold, type FleetSandboxEntry, type FleetServices, type FleetSwapResult, type HumanSessionCommandContext, type LimitCheckInput, type LimitDependencies, MARKETING_SOCIAL_PACK_ID, MAX_CHECKS_PER_RIG, MAX_CREDENTIAL_HOOKS_PER_RIG, MAX_DEFAULT_VARIABLE_SETS_PER_RIG, MAX_ENVIRONMENTS_PER_WORKSPACE, MAX_RIGS_PER_WORKSPACE, MAX_VARIABLES_PER_ENVIRONMENT, type ManagedAuth, type McpCapabilityProbe, type McpCapabilityProbeInput, type McpCapabilityProbeResult, type ObjectStorageDependency, type ProvisionResult, type ResolvedSessionAuthorization, type ResolvedSessionToolPolicy, type ResumeBoxByIdInput, type ResumedSandboxSession, type RigServices, type RigVerificationClassification, type RunOnOp, type RunOnResult, SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS, SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS, SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID, SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE, SessionAuthorizationDeniedError, type SessionAuthorizationDependencies, SessionAuthorizationUnavailableError, SessionSpawnDeniedError, type SessionToolPolicyInput, type SessionWorkflowClient, acceptSessionUserMessage, activateRigVersionForApi, appendRigSetupCommand, applyCapabilityEnablement, assertAllowedEnvironmentVariableName, assertAllowedVariableSetVariableName, assertConfiguredModel, assertPackSandboxImageCompatible, assertToolRefsSubset, assertWorkspaceDeletable, assertWorkspaceMemberRemovable, assertWorkspaceModelPolicyAllows, availableToolRefs, buildCapabilityCatalog, buildFleetContextForSession, buildMarketingDailyAnalysisAgentConfig, canonicalConfiguredModel, checkLimit, classifyRigVerificationOutcome, controlAgentSessionWorkstream, controlHumanSessionWorkstream, controlHumanWorkspace, createAndStartSession, createCatalogItem, createRigForApi, createRigVersionForApi, createSessionForRequest, createValidatedScheduledTask, deleteHumanQueuePrompt, deleteRigForApi, disableCapability, discoverMcpRegistryCapabilities, editHumanQueuePrompt, enableCapability, enabledCapabilityMcpToolRefs, getActorNewSessionDraft, getCapabilityPack, getHumanComposerDraft, hasPermission, isBuiltInCapabilityPack, isUserMember, listCapabilityPacks, listFleet, listRigChangesForApi, listRigVersionsForApi, listWorkspaceCapabilityPacks, manualScheduledTaskTriggerUsageKey, manualScheduledTaskTriggerWorkflowId, memberCanAdminister, mergeResourceRefs, moveHumanQueuePrompt, normalizeResources, officialMcpRegistryUrl, postUserMessageTurn, promoteSetupAppendChange, promoteVerifiedDefinitionEditChangeForApi, proposeRigChangeForApi, provisionSandbox, readSessionLineage, reasoningEffortForSession, recordRigAuditEvent, recordVariableSetAuditEvent, recordWorkspaceUsage, relayConfigFromSettings, relayDialBaseFromSettings, requireAccessContext, requireAccessGrant, requireEnvironmentEncryption, requireLimit, requirePermission, requireQueuedTurnForApi, requireRigChangeForApi, requireRigForApi, requireScheduledTaskForApi, requireSessionAuthorization, requireSessionAuthorizationListScope, requireVariableSetEncryption, requireVariableSetForApi, resolveCapabilityPack, resolveMemberSubjectId, resolveSessionToolPolicy, restoreScheduledTask, rigActorForGrant, routingEnabled, runOnSandbox, saveActorNewSessionDraft, saveHumanComposerDraft, scheduledTaskTemporalScheduleId, scheduledTaskToolsProvided, scheduledTaskTriggerToken, sendAgentSessionMessage, sessionSpawnDenialEnvelope, sessionWithEffectiveToolPolicy, settingsWithEnabledCapabilityMcpServers, settingsWithMcpCapabilityServers, settingsWithSessionMcpServerMetadata, steerAgentSession, steerHumanQueuePrompt, swapActiveSandbox, syncCreatedScheduledTask, syncUpdatedScheduledTask, updateRigForApi, updateSessionMcpApprovalPolicy, updateSessionTitle, validateFileResources, validateGitHubRepositorySelection, validateGitHubRepositorySelectionShape, validateGitHubRepositorySelectionShapes, validateMcpCapabilityConnection, validateToolRefs, validateToolRefsForSessionPolicy, validateVariableSetAttachment, validatedScheduledTaskUpdate, withDefaultEnabledCapabilityMcpTools, workflowIdForSession, workspaceSessionToolPolicyDefaultServerIds, workspaceSessionToolPolicyServerIds, wrapChannelABoxWithRouting };
1146
+ export { type AcceptSessionUserMessageDependencies, type AccessDeps, type AgentSessionCommandContext, type ApiRouteDeps, type ApiSandboxClient, type ApiSandboxSession, type AppDependencies, type ChannelARoutingServices, type DocumentIndexClient, type FleetContext, type FleetListResult, type FleetLiveness, type FleetReadinessHold, type FleetSandboxEntry, type FleetServices, type FleetSwapResult, type HumanSessionCommandContext, type LimitCheckInput, type LimitDependencies, MARKETING_SOCIAL_PACK_ID, MAX_CHECKS_PER_RIG, MAX_CREDENTIAL_HOOKS_PER_RIG, MAX_DEFAULT_VARIABLE_SETS_PER_RIG, MAX_ENVIRONMENTS_PER_WORKSPACE, MAX_RIGS_PER_WORKSPACE, MAX_VARIABLES_PER_ENVIRONMENT, type ManagedAuth, type McpCapabilityProbe, type McpCapabilityProbeInput, type McpCapabilityProbeResult, type ObjectStorageDependency, type ProvisionResult, type ResolvedSessionAuthorization, type ResolvedSessionToolPolicy, type ResumeBoxByIdInput, type ResumedSandboxSession, type RigServices, type RigVerificationClassification, type RunOnOp, type RunOnResult, SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS, SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS, SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID, SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE, SessionAuthorizationDeniedError, type SessionAuthorizationDependencies, SessionAuthorizationUnavailableError, SessionSpawnDeniedError, type SessionToolPolicyInput, type SessionWorkflowClient, acceptSessionUserMessage, activateRigVersionForApi, appendRigSetupCommand, applyCapabilityEnablement, assertAllowedEnvironmentVariableName, assertAllowedVariableSetVariableName, assertConfiguredModel, assertPackSandboxImageCompatible, assertToolRefsSubset, assertWorkspaceDeletable, assertWorkspaceMemberRemovable, assertWorkspaceModelPolicyAllows, availableToolRefs, buildCapabilityCatalog, buildFleetContextForSession, buildMarketingDailyAnalysisAgentConfig, canonicalConfiguredModel, checkLimit, classifyRigVerificationOutcome, controlAgentSessionWorkstream, controlHumanSessionWorkstream, controlHumanWorkspace, createAndStartSession, createCatalogItem, createRigForApi, createRigVersionForApi, createSessionForRequest, createValidatedScheduledTask, deleteHumanQueuePrompt, deleteRigForApi, disableCapability, discoverMcpRegistryCapabilities, editHumanQueuePrompt, enableCapability, enabledCapabilityMcpToolRefs, getActorNewSessionDraft, getCapabilityPack, getHumanComposerDraft, hasPermission, isAuthoritativeGitHubRepositorySelectionError, isBuiltInCapabilityPack, isUserMember, listCapabilityPacks, listFleet, listRigChangesForApi, listRigVersionsForApi, listWorkspaceCapabilityPacks, manualScheduledTaskTriggerUsageKey, manualScheduledTaskTriggerWorkflowId, memberCanAdminister, mergeResourceRefs, moveHumanQueuePrompt, normalizeResources, officialMcpRegistryUrl, postUserMessageTurn, promoteSetupAppendChange, promoteVerifiedDefinitionEditChangeForApi, proposeRigChangeForApi, provisionSandbox, readSessionLineage, reasoningEffortForSession, recordRigAuditEvent, recordVariableSetAuditEvent, recordWorkspaceUsage, relayConfigFromSettings, relayDialBaseFromSettings, requireAccessContext, requireAccessGrant, requireEnvironmentEncryption, requireLimit, requirePermission, requireQueuedTurnForApi, requireRigChangeForApi, requireRigForApi, requireScheduledTaskForApi, requireSessionAuthorization, requireSessionAuthorizationListScope, requireVariableSetEncryption, requireVariableSetForApi, resolveCapabilityPack, resolveMemberSubjectId, resolveSessionToolPolicy, restoreScheduledTask, rigActorForGrant, routingEnabled, runOnSandbox, saveActorNewSessionDraft, saveHumanComposerDraft, scheduledTaskTemporalScheduleId, scheduledTaskToolsProvided, scheduledTaskTriggerToken, sendAgentSessionMessage, sessionSpawnDenialEnvelope, sessionToolPolicyAllowsDefaultNativeTools, sessionWithEffectiveToolPolicy, settingsWithEnabledCapabilityMcpServers, settingsWithMcpCapabilityServers, settingsWithSessionMcpServerMetadata, steerAgentSession, steerHumanQueuePrompt, swapActiveSandbox, syncCreatedScheduledTask, syncUpdatedScheduledTask, updateRigForApi, updateSessionMcpApprovalPolicy, updateSessionTitle, updateSessionToolPolicy, validateFileResources, validateGitHubRepositorySelection, validateGitHubRepositorySelectionShape, validateGitHubRepositorySelectionShapes, validateMcpCapabilityConnection, validateToolRefs, validateToolRefsForSessionPolicy, validateVariableSetAttachment, validatedScheduledTaskUpdate, withDefaultEnabledCapabilityMcpTools, workflowIdForSession, workspaceSessionToolPolicyDefaultServerIds, workspaceSessionToolPolicyServerIds, wrapChannelABoxWithRouting };
package/dist/index.js CHANGED
@@ -25,10 +25,12 @@ import { HTTPException } from "hono/http-exception";
25
25
  import {
26
26
  advanceWorkspaceGenerationForDirectRequest,
27
27
  advanceWorkspaceGenerationForRetainedProcess,
28
+ getRetainedProcess,
28
29
  getSandbox,
29
30
  markWarmLeaseInstanceLost,
30
31
  readActiveSandbox,
31
32
  retainWorkspaceMutationProcess,
33
+ retainedProcessSettlementIdentity,
32
34
  settleRetainedProcess,
33
35
  verifyDirectWorkspaceMutationSettlement,
34
36
  verifyRetainedProcessMutationSettlement
@@ -43,7 +45,12 @@ import {
43
45
  function relayConfigFromSettings(settings) {
44
46
  const raw = settings.selfhostedRelayUrl?.trim();
45
47
  if (!raw) {
46
- return { host: "relay.opengeni.local", port: 443, tls: true, path: "/stream" };
48
+ return {
49
+ host: "relay.opengeni.local",
50
+ port: 443,
51
+ tls: true,
52
+ path: "/stream"
53
+ };
47
54
  }
48
55
  try {
49
56
  const url = new URL(raw.includes("://") ? raw : `wss://${raw}`);
@@ -185,14 +192,23 @@ function wrapChannelABoxWithRouting(services, ids, established) {
185
192
  process,
186
193
  proof
187
194
  }) => {
188
- if (backend.sandboxId !== null || backend.leaseEpoch === void 0 || backend.providerInstanceId === void 0) {
195
+ if (backend.sandboxId !== null || backend.leaseEpoch === void 0 || backend.providerInstanceId === void 0 || backend.activeEpoch === void 0) {
189
196
  return;
190
197
  }
198
+ const durable = await getRetainedProcess(db, {
199
+ workspaceId: ids.workspaceId,
200
+ sessionId: ids.sessionId,
201
+ processId: process.id
202
+ });
203
+ if (!durable || durable.providerSessionId !== process.providerSessionId || durable.providerBackend !== backend.kind || durable.providerInstanceId !== backend.providerInstanceId || durable.leaseEpoch !== backend.leaseEpoch || durable.routeKind !== (backend.sandboxId === null ? "home" : "active") || durable.routeTargetId !== backend.sandboxId || durable.routeEpoch !== backend.activeEpoch) {
204
+ throw new Error("API retained-process settlement lost its exact durable backend identity");
205
+ }
191
206
  await settleRetainedProcess(db, {
192
207
  accountId: ids.accountId,
193
208
  workspaceId: ids.workspaceId,
194
209
  sessionId: ids.sessionId,
195
210
  processId: process.id,
211
+ expected: retainedProcessSettlementIdentity(durable),
196
212
  outcome: proof.outcome,
197
213
  exitCode: proof.exitCode,
198
214
  reason: proof.reason,
@@ -3102,6 +3118,9 @@ async function validateGitHubRepositorySelection(db, workspaceId, resources) {
3102
3118
  }
3103
3119
  }
3104
3120
  }
3121
+ function isAuthoritativeGitHubRepositorySelectionError(error) {
3122
+ return error instanceof HTTPException8 && error.status === 422;
3123
+ }
3105
3124
  async function validateFileResources(db, workspaceId, resources) {
3106
3125
  const fileIds = /* @__PURE__ */ new Set();
3107
3126
  for (const resource of resources) {
@@ -3202,11 +3221,9 @@ function resolveSessionToolPolicy(input) {
3202
3221
  const configuredIds = effectiveIds.filter((id) => availableIds.has(id));
3203
3222
  const configuredIdSet = new Set(configuredIds);
3204
3223
  const droppedIds = effectiveIds.filter((id) => !configuredIdSet.has(id));
3205
- const deferredIds = tracksWorkspaceDefaults ? sortedIds(
3206
- toolRefs.filter(
3207
- (tool) => tool.optional === true && configuredIdSet.has(tool.id) && !mandatoryIdSet.has(tool.id)
3208
- ).map((tool) => tool.id)
3209
- ) : [];
3224
+ const deferredIds = sortedIds(
3225
+ toolRefs.filter((tool) => configuredIdSet.has(tool.id) && !mandatoryIdSet.has(tool.id)).map((tool) => tool.id)
3226
+ );
3210
3227
  const selectedIds = sortedIds(
3211
3228
  selectedRefs.filter(
3212
3229
  (tool) => !mandatoryIdSet.has(tool.id) && !(tracksWorkspaceDefaults && tool.optional === true)
@@ -3229,7 +3246,7 @@ function resolveSessionToolPolicy(input) {
3229
3246
  effectiveIds: projections.effective.ids,
3230
3247
  mandatoryIds: projections.mandatory.ids,
3231
3248
  lazyRouter: {
3232
- state: tracksWorkspaceDefaults ? "required" : "disabled",
3249
+ state: deferredIds.length > 0 ? "required" : "disabled",
3233
3250
  deferredIds: projections.deferred.ids
3234
3251
  },
3235
3252
  configuredIds: projections.configured.ids,
@@ -3246,6 +3263,9 @@ function resolveSessionToolPolicy(input) {
3246
3263
  }
3247
3264
  };
3248
3265
  }
3266
+ function sessionToolPolicyAllowsDefaultNativeTools(policy) {
3267
+ return policy.mode === "workspace_default" && policy.lazyRouter.state === "required";
3268
+ }
3249
3269
  async function workspaceSessionToolPolicyServerIds(db, workspaceId, settings) {
3250
3270
  const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings);
3251
3271
  return sortedIds(runtimeSettings.mcpServers.map((server) => server.id));
@@ -3298,6 +3318,7 @@ import {
3298
3318
  ServiceTurnInitiatorContext,
3299
3319
  evaluateWorkspaceModelPolicy,
3300
3320
  reasoningEffortForMetadata,
3321
+ stableJson as stableJson2,
3301
3322
  SessionMcpApprovalPolicy
3302
3323
  } from "@opengeni/contracts";
3303
3324
  import {
@@ -3330,7 +3351,8 @@ import {
3330
3351
  QueueCommandConflictError,
3331
3352
  AgentCommandAuthorityError,
3332
3353
  SessionSpawnDeniedDbError,
3333
- SessionControlConflictError
3354
+ SessionControlConflictError,
3355
+ SessionToolPolicyVersionConflictError
3334
3356
  } from "@opengeni/db";
3335
3357
  import {
3336
3358
  appendAndPublishEvents as appendAndPublishEvents2,
@@ -3341,6 +3363,7 @@ import { HTTPException as HTTPException9 } from "hono/http-exception";
3341
3363
  var reservedSessionMcpServerIds = /* @__PURE__ */ new Set(["opengeni", "files", "docs", "codex_apps"]);
3342
3364
  var maxSessionMcpCredentialHeaders = 16;
3343
3365
  var maxSessionMcpCredentialHeaderValueLength = 4096;
3366
+ var maxToolPolicyAuditRefs = 40;
3344
3367
  var sessionMcpCredentialHeaderName = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
3345
3368
  var SessionSpawnDeniedError = class extends Error {
3346
3369
  denial;
@@ -4472,6 +4495,150 @@ async function updateSessionMcpApprovalPolicy(deps, grant, sessionId, serverId,
4472
4495
  effectiveFrom: "next_attempt"
4473
4496
  };
4474
4497
  }
4498
+ function toolPolicyAuditSnapshot(session, tools, policy = session.toolPolicy ?? { mode: "legacy", inheritedFromSessionId: null }) {
4499
+ const allToolRefs = mergeToolRefs([], tools).sort((left, right) => {
4500
+ const leftMandatory = left.kind === "mcp" && left.id === "opengeni";
4501
+ const rightMandatory = right.kind === "mcp" && right.id === "opengeni";
4502
+ if (leftMandatory !== rightMandatory) return leftMandatory ? -1 : 1;
4503
+ return `${left.kind}:${left.id}`.localeCompare(`${right.kind}:${right.id}`);
4504
+ }).map((tool) => ({
4505
+ kind: tool.kind,
4506
+ id: tool.id,
4507
+ ...tool.optional === void 0 ? {} : { optional: tool.optional }
4508
+ }));
4509
+ const toolRefs = allToolRefs.slice(0, maxToolPolicyAuditRefs);
4510
+ return {
4511
+ mode: policy.mode,
4512
+ inheritedFromSessionId: policy.inheritedFromSessionId,
4513
+ // IDs only: no MCP URLs, names, headers, credentials, schemas, or args.
4514
+ toolIds: [...toolRefs].sort((left, right) => `${left.kind}:${left.id}`.localeCompare(`${right.kind}:${right.id}`)).map((tool) => tool.id),
4515
+ toolRefs,
4516
+ toolCount: allToolRefs.length,
4517
+ truncated: allToolRefs.length > toolRefs.length
4518
+ };
4519
+ }
4520
+ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
4521
+ await requireSessionAuthorization(deps, grant, {
4522
+ sessionId,
4523
+ operation: "session.tool_policy.write",
4524
+ surface: "core"
4525
+ });
4526
+ requirePermission(grant, "sessions:control");
4527
+ const existingSession = await requireSession2(deps.db, grant.workspaceId, sessionId);
4528
+ const capabilityRuntimeSettings = await settingsWithEnabledCapabilityMcpServers(
4529
+ deps.db,
4530
+ grant.workspaceId,
4531
+ deps.settings
4532
+ );
4533
+ const runtimeSettings = settingsWithSessionMcpServerMetadata(
4534
+ capabilityRuntimeSettings,
4535
+ existingSession.mcpServers
4536
+ );
4537
+ const explicitRequest = request.mode === "workspace_default" ? null : request;
4538
+ const requestedMode = explicitRequest ? "explicit" : "workspace_default";
4539
+ const explicitRequestedTools = explicitRequest ? (() => {
4540
+ const validatedTools = validateToolRefs(explicitRequest.tools, runtimeSettings);
4541
+ const validatedIds = new Set(validatedTools.map((tool) => `${tool.kind}:${tool.id}`));
4542
+ const unknown = explicitRequest.tools.find(
4543
+ (tool) => !validatedIds.has(`${tool.kind}:${tool.id}`)
4544
+ );
4545
+ if (unknown) {
4546
+ throw new HTTPException9(422, { message: `unknown MCP server id: ${unknown.id}` });
4547
+ }
4548
+ return withFirstPartyTools(validatedTools, runtimeSettings);
4549
+ })() : null;
4550
+ const workspaceDefaultTools = withFirstPartyTools(
4551
+ withDefaultEnabledCapabilityMcpTools([], deps.settings, capabilityRuntimeSettings),
4552
+ runtimeSettings
4553
+ );
4554
+ const events = await appendSessionEventsWithLockedSessionUpdate(
4555
+ deps.db,
4556
+ grant.workspaceId,
4557
+ sessionId,
4558
+ async (session, context) => {
4559
+ const currentVersion = session.toolPolicyVersion ?? 1;
4560
+ if (request.expectedVersion !== currentVersion) {
4561
+ throw new SessionToolPolicyVersionConflictError(currentVersion);
4562
+ }
4563
+ let nextTools;
4564
+ let nextPolicy;
4565
+ if (session.parentSessionId) {
4566
+ const parent = await context.getLockedSession(session.parentSessionId);
4567
+ if (!parent) {
4568
+ throw new HTTPException9(409, { message: "parent session is no longer available" });
4569
+ }
4570
+ const parentTracksWorkspaceDefaults = parent.toolPolicy?.mode === "workspace_default";
4571
+ const parentEffective = withFirstPartyTools(
4572
+ parentTracksWorkspaceDefaults ? withDefaultEnabledCapabilityMcpTools(
4573
+ availableToolRefs(parent.tools, runtimeSettings),
4574
+ deps.settings,
4575
+ runtimeSettings
4576
+ ) : parent.tools,
4577
+ runtimeSettings
4578
+ );
4579
+ if (requestedMode === "workspace_default") {
4580
+ if (!parentTracksWorkspaceDefaults) {
4581
+ throw new HTTPException9(403, {
4582
+ message: "a child may adopt workspace defaults only while its parent tracks workspace defaults"
4583
+ });
4584
+ }
4585
+ nextTools = parentEffective;
4586
+ nextPolicy = {
4587
+ mode: "workspace_default",
4588
+ inheritedFromSessionId: parent.id
4589
+ };
4590
+ } else {
4591
+ nextTools = explicitRequestedTools;
4592
+ assertToolRefsSubset(
4593
+ nextTools,
4594
+ parentEffective,
4595
+ "session tools may only narrow the parent session tool policy"
4596
+ );
4597
+ nextPolicy = {
4598
+ mode: "explicit",
4599
+ inheritedFromSessionId: parent.id
4600
+ };
4601
+ }
4602
+ } else {
4603
+ nextTools = requestedMode === "workspace_default" ? workspaceDefaultTools : explicitRequestedTools;
4604
+ nextPolicy = { mode: requestedMode, inheritedFromSessionId: null };
4605
+ }
4606
+ const currentPolicy = session.toolPolicy ?? {
4607
+ mode: "legacy",
4608
+ inheritedFromSessionId: null
4609
+ };
4610
+ const unchanged = stableJson2({ tools: session.tools, policy: currentPolicy }) === stableJson2({ tools: nextTools, policy: nextPolicy });
4611
+ if (unchanged) {
4612
+ return { events: [] };
4613
+ }
4614
+ const nextVersion = currentVersion + 1;
4615
+ return {
4616
+ events: [
4617
+ {
4618
+ type: "session.tool_policy.updated",
4619
+ payload: {
4620
+ before: toolPolicyAuditSnapshot(session, session.tools, currentPolicy),
4621
+ after: toolPolicyAuditSnapshot(session, nextTools, nextPolicy),
4622
+ version: nextVersion,
4623
+ effectiveFrom: "next_attempt"
4624
+ }
4625
+ }
4626
+ ],
4627
+ update: {
4628
+ tools: nextTools,
4629
+ toolPolicy: nextPolicy,
4630
+ toolPolicyVersion: nextVersion,
4631
+ expectedToolPolicyVersion: request.expectedVersion
4632
+ }
4633
+ };
4634
+ },
4635
+ { lockParentSession: true }
4636
+ );
4637
+ if (events.length > 0) {
4638
+ await publishDurableSessionEvents(deps.bus, grant.workspaceId, sessionId, events);
4639
+ }
4640
+ return await requireSession2(deps.db, grant.workspaceId, sessionId);
4641
+ }
4475
4642
  async function readSessionLineage(deps, grant, sessionId) {
4476
4643
  const authorization = await requireSessionAuthorization(deps, grant, {
4477
4644
  sessionId,
@@ -4784,24 +4951,105 @@ import {
4784
4951
  } from "@opengeni/contracts";
4785
4952
  import {
4786
4953
  getNewSessionDraftInTransaction,
4954
+ getEnrollment as getEnrollment3,
4955
+ getRig as getRig4,
4956
+ getSandbox as getSandbox4,
4957
+ getVariableSet as getVariableSet4,
4787
4958
  NewSessionDraftAccessError,
4959
+ newSessionDraftToolsProvided,
4960
+ publicNewSessionDraftOptions,
4961
+ requireFile as requireFile2,
4788
4962
  saveNewSessionDraftInTransaction,
4789
4963
  withWorkspaceSubjectRls as withWorkspaceSubjectRls2
4790
4964
  } from "@opengeni/db";
4791
4965
  import { HTTPException as HTTPException12 } from "hono/http-exception";
4966
+ function hasOwn(value, key) {
4967
+ return typeof value === "object" && value !== null && Object.hasOwn(value, key);
4968
+ }
4792
4969
  function mapNewSessionDraft(row) {
4793
4970
  if (!row) return null;
4794
4971
  return NewSessionDraft.parse({
4795
4972
  revision: row.revision,
4796
4973
  text: row.text,
4797
4974
  resources: row.resources,
4798
- tools: row.tools,
4975
+ tools: newSessionDraftToolsProvided(row) ? row.tools : [],
4976
+ toolsProvided: newSessionDraftToolsProvided(row),
4799
4977
  model: row.model,
4800
4978
  reasoningEffort: row.reasoningEffort,
4801
- options: row.sessionOptions,
4979
+ options: publicNewSessionDraftOptions(row),
4802
4980
  updatedAt: row.updatedAt.toISOString()
4803
4981
  });
4804
4982
  }
4983
+ async function hydrateNewSessionDraft(deps, grant, workspaceId, row) {
4984
+ if (!row) return null;
4985
+ const mapped = mapNewSessionDraft(row);
4986
+ if (!mapped) return null;
4987
+ const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
4988
+ deps.db,
4989
+ workspaceId,
4990
+ deps.settings
4991
+ );
4992
+ const resources = [];
4993
+ for (const resource of mapped.resources) {
4994
+ if (resource.kind === "repository") {
4995
+ try {
4996
+ await validateGitHubRepositorySelection(deps.db, workspaceId, [resource]);
4997
+ resources.push(resource);
4998
+ } catch (error) {
4999
+ if (isAuthoritativeGitHubRepositorySelectionError(error)) {
5000
+ continue;
5001
+ }
5002
+ resources.push(resource);
5003
+ }
5004
+ continue;
5005
+ }
5006
+ try {
5007
+ const file = await requireFile2(deps.db, workspaceId, resource.fileId);
5008
+ if (file.status === "ready") resources.push(resource);
5009
+ } catch {
5010
+ }
5011
+ }
5012
+ const options = { ...mapped.options };
5013
+ if (options.variableSetId) {
5014
+ if (!hasPermission(grant.permissions, "variable-sets:use") || !await getVariableSet4(deps.db, workspaceId, options.variableSetId)) {
5015
+ delete options.variableSetId;
5016
+ }
5017
+ }
5018
+ if (options.rigId) {
5019
+ const rig = await getRig4(deps.db, workspaceId, options.rigId);
5020
+ if (!rig?.activeVersion) delete options.rigId;
5021
+ }
5022
+ if (options.targetSandboxId) {
5023
+ const sandbox = await getSandbox4(deps.db, workspaceId, options.targetSandboxId);
5024
+ const enrollment = sandbox?.enrollmentId ? await getEnrollment3(deps.db, workspaceId, sandbox.enrollmentId) : null;
5025
+ if (!sandbox || sandbox.kind !== "selfhosted" || !enrollment || enrollment.status !== "active") {
5026
+ delete options.targetSandboxId;
5027
+ delete options.workingDir;
5028
+ delete options.sandboxBackend;
5029
+ }
5030
+ }
5031
+ let tools = [];
5032
+ if (mapped.toolsProvided) {
5033
+ try {
5034
+ tools = validateToolRefs(mapped.tools, runtimeSettings);
5035
+ } catch {
5036
+ tools = mapped.tools.filter((tool) => {
5037
+ try {
5038
+ validateToolRefs([tool], runtimeSettings);
5039
+ return true;
5040
+ } catch {
5041
+ return false;
5042
+ }
5043
+ });
5044
+ }
5045
+ }
5046
+ return {
5047
+ ...mapped,
5048
+ resources,
5049
+ tools,
5050
+ options
5051
+ };
5052
+ }
4805
5053
  async function getActorNewSessionDraft(deps, grant, workspaceId) {
4806
5054
  const row = await withWorkspaceSubjectRls2(
4807
5055
  deps.db,
@@ -4812,11 +5060,12 @@ async function getActorNewSessionDraft(deps, grant, workspaceId) {
4812
5060
  subjectId: grant.subjectId
4813
5061
  })
4814
5062
  );
4815
- return mapNewSessionDraft(row) ?? {
5063
+ return await hydrateNewSessionDraft(deps, grant, workspaceId, row) ?? {
4816
5064
  revision: 0,
4817
5065
  text: "",
4818
5066
  resources: [],
4819
5067
  tools: [],
5068
+ toolsProvided: false,
4820
5069
  model: deps.settings.openaiModel,
4821
5070
  reasoningEffort: deps.settings.openaiReasoningEffort,
4822
5071
  options: {},
@@ -4825,13 +5074,14 @@ async function getActorNewSessionDraft(deps, grant, workspaceId) {
4825
5074
  }
4826
5075
  async function saveActorNewSessionDraft(deps, grant, workspaceId, rawInput) {
4827
5076
  const input = SaveNewSessionDraftRequest.parse(rawInput);
5077
+ const toolsProvided = hasOwn(rawInput, "toolsProvided") ? input.toolsProvided : true;
4828
5078
  const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
4829
5079
  deps.db,
4830
5080
  workspaceId,
4831
5081
  deps.settings
4832
5082
  );
4833
5083
  const resources = normalizeResources(input.resources);
4834
- const tools = validateToolRefs(input.tools, runtimeSettings);
5084
+ const tools = toolsProvided ? validateToolRefs(input.tools, runtimeSettings) : [];
4835
5085
  await validateGitHubRepositorySelection(deps.db, workspaceId, resources);
4836
5086
  if (resources.some((resource) => resource.kind === "file") && !deps.objectStorage) {
4837
5087
  throw new HTTPException12(503, { message: "object storage is not configured" });
@@ -4853,6 +5103,7 @@ async function saveActorNewSessionDraft(deps, grant, workspaceId, rawInput) {
4853
5103
  text: input.text,
4854
5104
  resources,
4855
5105
  tools,
5106
+ toolsProvided,
4856
5107
  model: input.model,
4857
5108
  reasoningEffort: input.reasoningEffort,
4858
5109
  options: input.options,
@@ -5405,6 +5656,7 @@ export {
5405
5656
  getCapabilityPack,
5406
5657
  getHumanComposerDraft,
5407
5658
  hasPermission,
5659
+ isAuthoritativeGitHubRepositorySelectionError,
5408
5660
  isBuiltInCapabilityPack,
5409
5661
  isUserMember,
5410
5662
  listCapabilityPacks,
@@ -5459,6 +5711,7 @@ export {
5459
5711
  scheduledTaskTriggerToken,
5460
5712
  sendAgentSessionMessage,
5461
5713
  sessionSpawnDenialEnvelope,
5714
+ sessionToolPolicyAllowsDefaultNativeTools,
5462
5715
  sessionWithEffectiveToolPolicy,
5463
5716
  settingsWithEnabledCapabilityMcpServers,
5464
5717
  settingsWithMcpCapabilityServers,
@@ -5472,6 +5725,7 @@ export {
5472
5725
  updateRigForApi,
5473
5726
  updateSessionMcpApprovalPolicy,
5474
5727
  updateSessionTitle,
5728
+ updateSessionToolPolicy,
5475
5729
  validateFileResources,
5476
5730
  validateGitHubRepositorySelection,
5477
5731
  validateGitHubRepositorySelectionShape,