@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 +28 -2
- package/dist/index.js +267 -13
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/application/new-session-drafts.ts +120 -4
- package/src/domain/resources.ts +9 -0
- package/src/domain/session-tool-policy.ts +25 -13
- package/src/domain/sessions.ts +200 -0
- package/src/sandbox/routing.ts +28 -2
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 {
|
|
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 =
|
|
3206
|
-
toolRefs.filter(
|
|
3207
|
-
|
|
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:
|
|
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
|
|
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
|
|
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,
|