@opengeni/core 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +64 -5
- package/dist/index.js +343 -35
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/application/session-commands.ts +2 -0
- package/src/dependencies.ts +2 -0
- package/src/domain/resources.ts +32 -1
- package/src/domain/scheduled-tasks.ts +4 -8
- package/src/domain/session-tool-policy.ts +211 -0
- package/src/domain/sessions.ts +255 -35
- package/src/index.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -2698,6 +2698,22 @@ function enabledCapabilityMcpToolRefs(settings, runtimeSettings) {
|
|
|
2698
2698
|
function withDefaultEnabledCapabilityMcpTools(tools, settings, runtimeSettings) {
|
|
2699
2699
|
return mergeToolRefs(tools, enabledCapabilityMcpToolRefs(settings, runtimeSettings));
|
|
2700
2700
|
}
|
|
2701
|
+
function availableToolRefs(tools, settings) {
|
|
2702
|
+
const available = new Set(settings.mcpServers.map((server) => server.id));
|
|
2703
|
+
return tools.filter((tool) => available.has(tool.id));
|
|
2704
|
+
}
|
|
2705
|
+
function assertToolRefsSubset(requested, allowed, message = "requested tools exceed the session tool policy") {
|
|
2706
|
+
const allowedIds = new Set(allowed.map((tool) => `${tool.kind}:${tool.id}`));
|
|
2707
|
+
const widened = requested.find((tool) => !allowedIds.has(`${tool.kind}:${tool.id}`));
|
|
2708
|
+
if (widened) {
|
|
2709
|
+
throw new HTTPException8(403, { message: `${message}: ${widened.id}` });
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
function validateToolRefsForSessionPolicy(input) {
|
|
2713
|
+
const validated = validateToolRefs(input.requested, input.settings);
|
|
2714
|
+
assertToolRefsSubset(validated, input.allowedTools, input.message);
|
|
2715
|
+
return validated;
|
|
2716
|
+
}
|
|
2701
2717
|
function normalizeResources(resources) {
|
|
2702
2718
|
const mountPaths = /* @__PURE__ */ new Map();
|
|
2703
2719
|
const identities = /* @__PURE__ */ new Map();
|
|
@@ -2902,6 +2918,128 @@ function positiveInteger(value) {
|
|
|
2902
2918
|
return null;
|
|
2903
2919
|
}
|
|
2904
2920
|
|
|
2921
|
+
// src/domain/session-tool-policy.ts
|
|
2922
|
+
import {
|
|
2923
|
+
SESSION_EFFECTIVE_TOOL_POLICY_ID_LIMIT,
|
|
2924
|
+
SESSION_EFFECTIVE_TOOL_POLICY_ID_MAX_LENGTH,
|
|
2925
|
+
mergeToolRefs as mergeToolRefs2
|
|
2926
|
+
} from "@opengeni/contracts";
|
|
2927
|
+
var MANDATORY_SESSION_MCP_SERVER_IDS = ["opengeni"];
|
|
2928
|
+
var PROJECTABLE_REGISTRY_ID = /^[A-Za-z0-9_-]+$/;
|
|
2929
|
+
function sortedIds(ids) {
|
|
2930
|
+
return [...new Set(ids)].sort();
|
|
2931
|
+
}
|
|
2932
|
+
function projectIds(ids) {
|
|
2933
|
+
const projectable = ids.filter(
|
|
2934
|
+
(id) => id.length <= SESSION_EFFECTIVE_TOOL_POLICY_ID_MAX_LENGTH && PROJECTABLE_REGISTRY_ID.test(id)
|
|
2935
|
+
);
|
|
2936
|
+
return {
|
|
2937
|
+
ids: projectable.slice(0, SESSION_EFFECTIVE_TOOL_POLICY_ID_LIMIT),
|
|
2938
|
+
truncated: projectable.length !== ids.length || projectable.length > SESSION_EFFECTIVE_TOOL_POLICY_ID_LIMIT
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
function resolveSessionToolPolicy(input) {
|
|
2942
|
+
const policy = input.toolPolicy ?? { mode: "legacy", inheritedFromSessionId: null };
|
|
2943
|
+
const availableIds = new Set(input.availableMcpServerIds);
|
|
2944
|
+
const defaultIds = new Set(input.defaultMcpServerIds ?? []);
|
|
2945
|
+
const mandatoryIds = MANDATORY_SESSION_MCP_SERVER_IDS.filter(
|
|
2946
|
+
(id) => availableIds.has(id)
|
|
2947
|
+
);
|
|
2948
|
+
const mandatoryIdSet = new Set(mandatoryIds);
|
|
2949
|
+
const selectedRefs = input.turnToolsProvided === true ? mergeToolRefs2([], input.turnTools ?? []) : input.turnToolsProvided === false ? mergeToolRefs2([], input.sessionTools) : mergeToolRefs2(input.sessionTools, input.turnTools ?? []);
|
|
2950
|
+
const tracksWorkspaceDefaults = policy.mode === "workspace_default" && input.turnToolsProvided !== true;
|
|
2951
|
+
let toolRefs = selectedRefs.filter((tool) => tool.optional !== true || availableIds.has(tool.id));
|
|
2952
|
+
if (tracksWorkspaceDefaults) {
|
|
2953
|
+
toolRefs = mergeToolRefs2(
|
|
2954
|
+
toolRefs,
|
|
2955
|
+
sortedIds(defaultIds).filter((id) => availableIds.has(id)).map((id) => ({ kind: "mcp", id, optional: true }))
|
|
2956
|
+
);
|
|
2957
|
+
}
|
|
2958
|
+
toolRefs = mergeToolRefs2(
|
|
2959
|
+
toolRefs,
|
|
2960
|
+
mandatoryIds.map((id) => ({ kind: "mcp", id }))
|
|
2961
|
+
);
|
|
2962
|
+
const requestedEffectiveRefs = mergeToolRefs2(
|
|
2963
|
+
selectedRefs,
|
|
2964
|
+
tracksWorkspaceDefaults ? sortedIds(defaultIds).filter((id) => availableIds.has(id)).map((id) => ({ kind: "mcp", id, optional: true })) : []
|
|
2965
|
+
);
|
|
2966
|
+
const effectiveIds = sortedIds(
|
|
2967
|
+
mergeToolRefs2(
|
|
2968
|
+
requestedEffectiveRefs,
|
|
2969
|
+
mandatoryIds.map((id) => ({ kind: "mcp", id }))
|
|
2970
|
+
).map((tool) => tool.id)
|
|
2971
|
+
);
|
|
2972
|
+
const configuredIds = effectiveIds.filter((id) => availableIds.has(id));
|
|
2973
|
+
const configuredIdSet = new Set(configuredIds);
|
|
2974
|
+
const droppedIds = effectiveIds.filter((id) => !configuredIdSet.has(id));
|
|
2975
|
+
const deferredIds = tracksWorkspaceDefaults ? sortedIds(
|
|
2976
|
+
toolRefs.filter(
|
|
2977
|
+
(tool) => tool.optional === true && configuredIdSet.has(tool.id) && !mandatoryIdSet.has(tool.id)
|
|
2978
|
+
).map((tool) => tool.id)
|
|
2979
|
+
) : [];
|
|
2980
|
+
const selectedIds = sortedIds(
|
|
2981
|
+
selectedRefs.filter(
|
|
2982
|
+
(tool) => !mandatoryIdSet.has(tool.id) && !(tracksWorkspaceDefaults && tool.optional === true)
|
|
2983
|
+
).map((tool) => tool.id)
|
|
2984
|
+
);
|
|
2985
|
+
const projections = {
|
|
2986
|
+
selected: projectIds(selectedIds),
|
|
2987
|
+
effective: projectIds(effectiveIds),
|
|
2988
|
+
mandatory: projectIds(sortedIds(mandatoryIds)),
|
|
2989
|
+
deferred: projectIds(deferredIds),
|
|
2990
|
+
configured: projectIds(configuredIds),
|
|
2991
|
+
dropped: projectIds(droppedIds)
|
|
2992
|
+
};
|
|
2993
|
+
return {
|
|
2994
|
+
toolRefs,
|
|
2995
|
+
effectivePolicy: {
|
|
2996
|
+
mode: policy.mode,
|
|
2997
|
+
inheritedFromSessionId: policy.inheritedFromSessionId,
|
|
2998
|
+
selectedIds: projections.selected.ids,
|
|
2999
|
+
effectiveIds: projections.effective.ids,
|
|
3000
|
+
mandatoryIds: projections.mandatory.ids,
|
|
3001
|
+
lazyRouter: {
|
|
3002
|
+
state: tracksWorkspaceDefaults ? "required" : "disabled",
|
|
3003
|
+
deferredIds: projections.deferred.ids
|
|
3004
|
+
},
|
|
3005
|
+
configuredIds: projections.configured.ids,
|
|
3006
|
+
droppedIds: projections.dropped.ids,
|
|
3007
|
+
counts: {
|
|
3008
|
+
selected: selectedIds.length,
|
|
3009
|
+
effective: effectiveIds.length,
|
|
3010
|
+
mandatory: mandatoryIds.length,
|
|
3011
|
+
deferred: deferredIds.length,
|
|
3012
|
+
configured: configuredIds.length,
|
|
3013
|
+
dropped: droppedIds.length
|
|
3014
|
+
},
|
|
3015
|
+
idsTruncated: Object.values(projections).some((projection) => projection.truncated)
|
|
3016
|
+
}
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
async function workspaceSessionToolPolicyServerIds(db, workspaceId, settings) {
|
|
3020
|
+
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings);
|
|
3021
|
+
return sortedIds(runtimeSettings.mcpServers.map((server) => server.id));
|
|
3022
|
+
}
|
|
3023
|
+
async function workspaceSessionToolPolicyDefaultServerIds(db, workspaceId, settings) {
|
|
3024
|
+
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings);
|
|
3025
|
+
return sortedIds(enabledCapabilityMcpToolRefs(settings, runtimeSettings).map((tool) => tool.id));
|
|
3026
|
+
}
|
|
3027
|
+
function sessionWithEffectiveToolPolicy(session, workspaceServerIds, workspaceDefaultServerIds = []) {
|
|
3028
|
+
const availableIds = new Set(workspaceServerIds);
|
|
3029
|
+
for (const server of session.mcpServers) {
|
|
3030
|
+
availableIds.add(server.id);
|
|
3031
|
+
}
|
|
3032
|
+
return {
|
|
3033
|
+
...session,
|
|
3034
|
+
effectiveToolPolicy: resolveSessionToolPolicy({
|
|
3035
|
+
...session.toolPolicy ? { toolPolicy: session.toolPolicy } : {},
|
|
3036
|
+
sessionTools: session.tools,
|
|
3037
|
+
availableMcpServerIds: availableIds,
|
|
3038
|
+
defaultMcpServerIds: workspaceDefaultServerIds
|
|
3039
|
+
}).effectivePolicy
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
|
|
2905
3043
|
// src/domain/scheduled-tasks.ts
|
|
2906
3044
|
import {
|
|
2907
3045
|
createScheduledTask,
|
|
@@ -2914,13 +3052,20 @@ import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
|
2914
3052
|
|
|
2915
3053
|
// src/domain/sessions.ts
|
|
2916
3054
|
import { CODEX_MODEL_ID_PREFIX } from "@opengeni/codex";
|
|
2917
|
-
import {
|
|
3055
|
+
import {
|
|
3056
|
+
canonicalizeConfiguredModelId,
|
|
3057
|
+
configuredAllowedModels,
|
|
3058
|
+
policyProviderIdForModel,
|
|
3059
|
+
resolveTurnExecutionPolicyV1
|
|
3060
|
+
} from "@opengeni/config";
|
|
2918
3061
|
import {
|
|
2919
3062
|
CreateSessionRequest,
|
|
3063
|
+
DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
|
|
2920
3064
|
ServiceTurnInitiator,
|
|
2921
3065
|
ServiceTurnInitiatorContext,
|
|
2922
3066
|
evaluateWorkspaceModelPolicy,
|
|
2923
|
-
reasoningEffortForMetadata
|
|
3067
|
+
reasoningEffortForMetadata,
|
|
3068
|
+
SessionMcpApprovalPolicy
|
|
2924
3069
|
} from "@opengeni/contracts";
|
|
2925
3070
|
import {
|
|
2926
3071
|
createSession,
|
|
@@ -2946,6 +3091,7 @@ import {
|
|
|
2946
3091
|
listSessionMcpServersForChildInheritance,
|
|
2947
3092
|
requireSession as requireSession2,
|
|
2948
3093
|
submitHumanPromptInTransaction,
|
|
3094
|
+
appendSessionEventsWithLockedSessionUpdate,
|
|
2949
3095
|
updateSessionTitle as updateSessionTitleRow,
|
|
2950
3096
|
withWorkspaceSubjectRls,
|
|
2951
3097
|
QueueCommandConflictError,
|
|
@@ -3091,6 +3237,7 @@ function mcpServerConfigFromMetadata(server) {
|
|
|
3091
3237
|
...server.name ? { name: server.name } : {},
|
|
3092
3238
|
url: server.url,
|
|
3093
3239
|
cacheToolsList: false,
|
|
3240
|
+
requireApproval: server.requireApproval,
|
|
3094
3241
|
...server.connectionRef ? { connectionRef: server.connectionRef } : {}
|
|
3095
3242
|
};
|
|
3096
3243
|
}
|
|
@@ -3151,6 +3298,7 @@ function validateSessionMcpServersForCreate(settings, grant, servers) {
|
|
|
3151
3298
|
url: server.url,
|
|
3152
3299
|
headerNames: Object.keys(headersEncrypted).sort(),
|
|
3153
3300
|
credentialVersion: 1,
|
|
3301
|
+
requireApproval: server.requireApproval ?? false,
|
|
3154
3302
|
connectionRef: server.connectionRef ?? null
|
|
3155
3303
|
});
|
|
3156
3304
|
}
|
|
@@ -3186,6 +3334,7 @@ function validateInheritedSessionMcpServersForCreate(servers) {
|
|
|
3186
3334
|
url: server.url,
|
|
3187
3335
|
headerNames: Object.keys(server.headersEncrypted ?? {}).sort(),
|
|
3188
3336
|
credentialVersion: 1,
|
|
3337
|
+
requireApproval: server.requireApproval ?? false,
|
|
3189
3338
|
connectionRef: server.connectionRef ?? null
|
|
3190
3339
|
}))
|
|
3191
3340
|
};
|
|
@@ -3250,6 +3399,7 @@ async function createAndStartSession(input) {
|
|
|
3250
3399
|
initialTurnInstructions: input.turnInstructions ?? null,
|
|
3251
3400
|
resources: input.resources,
|
|
3252
3401
|
tools: input.tools,
|
|
3402
|
+
...input.toolPolicy ? { toolPolicy: input.toolPolicy } : {},
|
|
3253
3403
|
metadata: sessionMetadata,
|
|
3254
3404
|
...input.createdBy ? { createdBy: input.createdBy } : {},
|
|
3255
3405
|
...input.createdByContext ? { createdByContext: input.createdByContext } : {},
|
|
@@ -3283,6 +3433,7 @@ async function createAndStartSession(input) {
|
|
|
3283
3433
|
initialTurnInstructions: input.turnInstructions ?? null,
|
|
3284
3434
|
resources: input.resources,
|
|
3285
3435
|
tools: input.tools,
|
|
3436
|
+
...input.toolPolicy ? { toolPolicy: input.toolPolicy } : {},
|
|
3286
3437
|
metadata: sessionMetadata,
|
|
3287
3438
|
...input.createdBy ? { createdBy: input.createdBy } : {},
|
|
3288
3439
|
...input.createdByContext ? { createdByContext: input.createdByContext } : {},
|
|
@@ -3335,7 +3486,9 @@ async function finishStartSession(input, session) {
|
|
|
3335
3486
|
sessionId: session.id,
|
|
3336
3487
|
...input.clientEventId ? { clientEventId: input.clientEventId } : {},
|
|
3337
3488
|
reasoningEffortFallback: input.reasoningEffort,
|
|
3489
|
+
turnExecutionPolicy: input.turnExecutionPolicy,
|
|
3338
3490
|
createdEventPayload: {
|
|
3491
|
+
...input.toolPolicy ? { toolPolicy: input.toolPolicy } : {},
|
|
3339
3492
|
...input.variableSet ? { variableSetId: input.variableSet.id, variableSetName: input.variableSet.name } : {},
|
|
3340
3493
|
...input.sessionMcpServers?.length ? { mcpServers: input.sessionMcpServers } : {}
|
|
3341
3494
|
},
|
|
@@ -3362,31 +3515,42 @@ async function finishStartSession(input, session) {
|
|
|
3362
3515
|
function workflowIdForSession(sessionId) {
|
|
3363
3516
|
return `session-${sessionId}`;
|
|
3364
3517
|
}
|
|
3365
|
-
function
|
|
3518
|
+
function canonicalConfiguredModel(settings, model) {
|
|
3366
3519
|
if (model === null || model === void 0) {
|
|
3367
|
-
return;
|
|
3520
|
+
return model;
|
|
3368
3521
|
}
|
|
3369
|
-
|
|
3370
|
-
|
|
3522
|
+
const canonicalModel = canonicalizeConfiguredModelId(settings, model);
|
|
3523
|
+
if (configuredAllowedModels(settings).includes(canonicalModel)) {
|
|
3524
|
+
return canonicalModel;
|
|
3371
3525
|
}
|
|
3372
|
-
if (settings.codexSubscriptionEnabled &&
|
|
3373
|
-
return;
|
|
3526
|
+
if (settings.codexSubscriptionEnabled && canonicalModel.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
3527
|
+
return canonicalModel;
|
|
3374
3528
|
}
|
|
3375
3529
|
throw new HTTPException9(422, { message: `model is not available: ${model}` });
|
|
3376
3530
|
}
|
|
3531
|
+
function assertConfiguredModel(settings, model) {
|
|
3532
|
+
canonicalConfiguredModel(settings, model);
|
|
3533
|
+
}
|
|
3377
3534
|
async function assertWorkspaceModelPolicyAllows(db, settings, workspaceId, model) {
|
|
3378
3535
|
if (model === null || model === void 0) {
|
|
3379
3536
|
return;
|
|
3380
3537
|
}
|
|
3538
|
+
const canonicalModel = canonicalConfiguredModel(settings, model);
|
|
3539
|
+
if (canonicalModel === null || canonicalModel === void 0) {
|
|
3540
|
+
return;
|
|
3541
|
+
}
|
|
3381
3542
|
const policy = await getWorkspaceModelPolicy(db, workspaceId);
|
|
3382
3543
|
if (!policy) {
|
|
3383
3544
|
return;
|
|
3384
3545
|
}
|
|
3385
|
-
const providerId = policyProviderIdForModel(settings,
|
|
3386
|
-
const verdict = evaluateWorkspaceModelPolicy(policy, {
|
|
3546
|
+
const providerId = policyProviderIdForModel(settings, canonicalModel);
|
|
3547
|
+
const verdict = evaluateWorkspaceModelPolicy(policy, {
|
|
3548
|
+
providerId,
|
|
3549
|
+
modelId: canonicalModel
|
|
3550
|
+
});
|
|
3387
3551
|
if (!verdict.allowed) {
|
|
3388
3552
|
throw new HTTPException9(422, {
|
|
3389
|
-
message: verdict.reason === "provider" ? `model "${
|
|
3553
|
+
message: verdict.reason === "provider" ? `model "${canonicalModel}" is not allowed by this workspace's model policy: provider "${providerId}" is not in the allowed providers` : `model "${canonicalModel}" is not allowed by this workspace's model policy`
|
|
3390
3554
|
});
|
|
3391
3555
|
}
|
|
3392
3556
|
}
|
|
@@ -3407,7 +3571,7 @@ function reasoningEffortForSession(metadata, fallback) {
|
|
|
3407
3571
|
}
|
|
3408
3572
|
async function postUserMessageTurn(input) {
|
|
3409
3573
|
const { db, bus, workflowClient, settings, accountId, workspaceId, sessionId } = input;
|
|
3410
|
-
const requestedModel = input.model ?? null;
|
|
3574
|
+
const requestedModel = canonicalConfiguredModel(settings, input.model ?? null) ?? null;
|
|
3411
3575
|
const requestedReasoningEffort = input.reasoningEffort ?? null;
|
|
3412
3576
|
assertConfiguredModel(settings, requestedModel);
|
|
3413
3577
|
await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, requestedModel);
|
|
@@ -3437,9 +3601,11 @@ async function postUserMessageTurn(input) {
|
|
|
3437
3601
|
turnInstructions: input.turnInstructions ?? null,
|
|
3438
3602
|
resources: input.resources,
|
|
3439
3603
|
tools: input.tools,
|
|
3604
|
+
toolsProvided: input.toolsProvided,
|
|
3440
3605
|
model: requestedModel,
|
|
3441
3606
|
reasoningEffort: requestedReasoningEffort,
|
|
3442
|
-
reasoningEffortFallback: settings.openaiReasoningEffort,
|
|
3607
|
+
reasoningEffortFallback: input.reasoningEffortFallback ?? settings.openaiReasoningEffort,
|
|
3608
|
+
turnExecutionPolicy: input.turnExecutionPolicy,
|
|
3443
3609
|
source: input.origin === "operator" ? "api" : "user",
|
|
3444
3610
|
mcpCredentialUpdates: input.mcpCredentialUpdates ?? []
|
|
3445
3611
|
})
|
|
@@ -3535,12 +3701,50 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
|
3535
3701
|
const resources = normalizeResources(
|
|
3536
3702
|
hasOwnProperty(rawPayload, "resources") ? payload.resources : parentSession?.resources ?? payload.resources
|
|
3537
3703
|
);
|
|
3704
|
+
const toolsProvided = hasOwnProperty(rawPayload, "tools");
|
|
3538
3705
|
const requestedTools = validateToolRefs(
|
|
3539
|
-
|
|
3706
|
+
toolsProvided ? payload.tools : parentSession?.tools ?? payload.tools,
|
|
3540
3707
|
runtimeSettings
|
|
3541
3708
|
);
|
|
3542
|
-
|
|
3543
|
-
|
|
3709
|
+
let selectedTools;
|
|
3710
|
+
let toolPolicy;
|
|
3711
|
+
if (parentSession) {
|
|
3712
|
+
const parentTracksWorkspaceDefaults = parentSession.toolPolicy?.mode === "workspace_default";
|
|
3713
|
+
const parentEffective = withFirstPartyTools(
|
|
3714
|
+
parentTracksWorkspaceDefaults ? withDefaultEnabledCapabilityMcpTools(
|
|
3715
|
+
availableToolRefs(parentSession.tools, runtimeSettings),
|
|
3716
|
+
settings,
|
|
3717
|
+
runtimeSettings
|
|
3718
|
+
) : parentSession.tools,
|
|
3719
|
+
runtimeSettings
|
|
3720
|
+
);
|
|
3721
|
+
if (toolsProvided) {
|
|
3722
|
+
assertToolRefsSubset(
|
|
3723
|
+
requestedTools,
|
|
3724
|
+
parentEffective,
|
|
3725
|
+
"child tools may only narrow the parent session tool policy"
|
|
3726
|
+
);
|
|
3727
|
+
selectedTools = requestedTools;
|
|
3728
|
+
toolPolicy = { mode: "explicit", inheritedFromSessionId: parentSession.id };
|
|
3729
|
+
} else {
|
|
3730
|
+
selectedTools = parentEffective;
|
|
3731
|
+
toolPolicy = {
|
|
3732
|
+
mode: parentTracksWorkspaceDefaults ? "workspace_default" : "inherited",
|
|
3733
|
+
inheritedFromSessionId: parentSession.id
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3736
|
+
} else if (toolsProvided) {
|
|
3737
|
+
selectedTools = requestedTools;
|
|
3738
|
+
toolPolicy = { mode: "explicit", inheritedFromSessionId: null };
|
|
3739
|
+
} else {
|
|
3740
|
+
selectedTools = withDefaultEnabledCapabilityMcpTools(
|
|
3741
|
+
requestedTools,
|
|
3742
|
+
settings,
|
|
3743
|
+
capabilityRuntimeSettings
|
|
3744
|
+
);
|
|
3745
|
+
toolPolicy = { mode: "workspace_default", inheritedFromSessionId: null };
|
|
3746
|
+
}
|
|
3747
|
+
const tools = withFirstPartyTools(selectedTools, runtimeSettings);
|
|
3544
3748
|
await validateGitHubRepositorySelection(db, workspaceId, resources);
|
|
3545
3749
|
if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
3546
3750
|
throw new HTTPException9(503, { message: "object storage is not configured" });
|
|
@@ -3568,16 +3772,30 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
|
3568
3772
|
frozenRigVersionId = rig.activeVersion.id;
|
|
3569
3773
|
}
|
|
3570
3774
|
}
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
payload.model ?? settings.openaiModel
|
|
3577
|
-
);
|
|
3578
|
-
const model = payload.model ?? settings.openaiModel;
|
|
3775
|
+
const model = canonicalConfiguredModel(settings, payload.model ?? settings.openaiModel);
|
|
3776
|
+
if (model === null || model === void 0) {
|
|
3777
|
+
throw new Error("effective session model unexpectedly resolved to null");
|
|
3778
|
+
}
|
|
3779
|
+
await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, model);
|
|
3579
3780
|
const reasoningEffort = payload.reasoningEffort ?? settings.openaiReasoningEffort;
|
|
3580
|
-
|
|
3781
|
+
const turnExecutionPolicy = resolveTurnExecutionPolicyV1(settings, {
|
|
3782
|
+
modelId: model,
|
|
3783
|
+
requestedModelId: payload.model ?? null,
|
|
3784
|
+
modelSource: payload.model === void 0 ? "deployment" : "explicit",
|
|
3785
|
+
reasoningEffort,
|
|
3786
|
+
reasoningSource: payload.reasoningEffort === void 0 ? "deployment" : "explicit"
|
|
3787
|
+
});
|
|
3788
|
+
const parentFirstPartyMcpPermissions = parentSession ? [...parentSession.firstPartyMcpPermissions ?? DEFAULT_FIRST_PARTY_MCP_PERMISSIONS] : null;
|
|
3789
|
+
if (parentFirstPartyMcpPermissions && payload.firstPartyMcpPermissions?.some(
|
|
3790
|
+
(permission) => !hasPermission(parentFirstPartyMcpPermissions, permission)
|
|
3791
|
+
)) {
|
|
3792
|
+
throw new HTTPException9(403, {
|
|
3793
|
+
message: "child first-party MCP permissions may only narrow the parent session grant"
|
|
3794
|
+
});
|
|
3795
|
+
}
|
|
3796
|
+
let firstPartyMcpPermissions = payload.firstPartyMcpPermissions ?? (parentFirstPartyMcpPermissions ? parentFirstPartyMcpPermissions.filter(
|
|
3797
|
+
(permission) => hasPermission(grant.permissions, permission)
|
|
3798
|
+
) : null);
|
|
3581
3799
|
if (firstPartyMcpPermissions && firstPartyMcpPermissions.length === 0) {
|
|
3582
3800
|
throw new HTTPException9(422, {
|
|
3583
3801
|
message: "firstPartyMcpPermissions must not be empty; omit it for the default worker permission set"
|
|
@@ -3710,9 +3928,11 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
|
3710
3928
|
turnInstructions: payload.turnInstructions ?? null,
|
|
3711
3929
|
resources,
|
|
3712
3930
|
tools,
|
|
3931
|
+
toolPolicy,
|
|
3713
3932
|
...payload.clientEventId ? { clientEventId: payload.clientEventId } : {},
|
|
3714
3933
|
model,
|
|
3715
3934
|
reasoningEffort,
|
|
3935
|
+
turnExecutionPolicy,
|
|
3716
3936
|
// A shared spawn inherits the box's backend; a caller-supplied
|
|
3717
3937
|
// sandboxBackend on a shared spawn is ignored (it is the same box). A
|
|
3718
3938
|
// machine-targeted top-level create labels the home "selfhosted"
|
|
@@ -3777,6 +3997,11 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
|
3777
3997
|
return session;
|
|
3778
3998
|
}
|
|
3779
3999
|
async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, input) {
|
|
4000
|
+
if (input.toolsProvided && !deps.settings.sessionTurnToolReplacementEnabled) {
|
|
4001
|
+
throw new HTTPException9(503, {
|
|
4002
|
+
message: "explicit follow-up tool replacement is temporarily unavailable until provenance-aware turn workers finish rolling out; omit tools to inherit the session policy and retry"
|
|
4003
|
+
});
|
|
4004
|
+
}
|
|
3780
4005
|
const { settings, db, bus, workflowClient, objectStorage } = deps;
|
|
3781
4006
|
await requireSessionAuthorization(deps, grant, {
|
|
3782
4007
|
sessionId,
|
|
@@ -3789,19 +4014,50 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
|
|
|
3789
4014
|
settings
|
|
3790
4015
|
);
|
|
3791
4016
|
const existingSession = await requireSession2(db, workspaceId, sessionId);
|
|
4017
|
+
const requestedModel = canonicalConfiguredModel(settings, input.model ?? null) ?? null;
|
|
4018
|
+
const effectiveModel = canonicalConfiguredModel(settings, requestedModel ?? existingSession.model) ?? null;
|
|
4019
|
+
if (effectiveModel === null) {
|
|
4020
|
+
throw new Error("effective follow-up model unexpectedly resolved to null");
|
|
4021
|
+
}
|
|
4022
|
+
const sessionReasoningEffort = reasoningEffortForSession(
|
|
4023
|
+
existingSession.metadata,
|
|
4024
|
+
settings.openaiReasoningEffort
|
|
4025
|
+
);
|
|
4026
|
+
const effectiveReasoningEffort = input.reasoningEffort ?? sessionReasoningEffort;
|
|
4027
|
+
const turnExecutionPolicy = resolveTurnExecutionPolicyV1(settings, {
|
|
4028
|
+
modelId: effectiveModel,
|
|
4029
|
+
requestedModelId: input.model ?? null,
|
|
4030
|
+
modelSource: input.model == null ? "session" : "explicit",
|
|
4031
|
+
reasoningEffort: effectiveReasoningEffort,
|
|
4032
|
+
reasoningSource: input.reasoningEffort == null ? "session" : "explicit"
|
|
4033
|
+
});
|
|
3792
4034
|
const runtimeSettings = settingsWithSessionMcpServerMetadata(
|
|
3793
4035
|
capabilityRuntimeSettings,
|
|
3794
4036
|
existingSession.mcpServers
|
|
3795
4037
|
);
|
|
3796
4038
|
const requestedResources = normalizeResources(input.resources ?? []);
|
|
3797
|
-
const
|
|
3798
|
-
const
|
|
4039
|
+
const tracksWorkspaceDefaults = existingSession.toolPolicy?.mode === "workspace_default";
|
|
4040
|
+
const sessionPolicyTools = withFirstPartyTools(
|
|
4041
|
+
tracksWorkspaceDefaults ? withDefaultEnabledCapabilityMcpTools(
|
|
4042
|
+
availableToolRefs(existingSession.tools, runtimeSettings),
|
|
4043
|
+
settings,
|
|
4044
|
+
capabilityRuntimeSettings
|
|
4045
|
+
) : existingSession.tools,
|
|
4046
|
+
runtimeSettings
|
|
4047
|
+
);
|
|
4048
|
+
const validatedTools = input.toolsProvided ? validateToolRefsForSessionPolicy({
|
|
4049
|
+
requested: input.tools ?? [],
|
|
4050
|
+
settings: runtimeSettings,
|
|
4051
|
+
allowedTools: sessionPolicyTools,
|
|
4052
|
+
message: "message tools may only narrow the session tool policy"
|
|
4053
|
+
}) : [];
|
|
4054
|
+
const requestedTools = input.toolsProvided ? validatedTools : [];
|
|
3799
4055
|
await requireLimit(deps, {
|
|
3800
4056
|
accountId: grant.accountId,
|
|
3801
4057
|
workspaceId,
|
|
3802
4058
|
action: "agent_run:create",
|
|
3803
4059
|
quantity: 1,
|
|
3804
|
-
model:
|
|
4060
|
+
model: effectiveModel
|
|
3805
4061
|
});
|
|
3806
4062
|
if (requestedResources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
3807
4063
|
throw new HTTPException9(503, { message: "object storage is not configured" });
|
|
@@ -3830,8 +4086,11 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
|
|
|
3830
4086
|
turnInstructions: input.turnInstructions ?? null,
|
|
3831
4087
|
resources: requestedResources,
|
|
3832
4088
|
tools: requestedTools,
|
|
4089
|
+
toolsProvided: input.toolsProvided,
|
|
3833
4090
|
model: input.model ?? null,
|
|
3834
4091
|
reasoningEffort: input.reasoningEffort ?? null,
|
|
4092
|
+
reasoningEffortFallback: sessionReasoningEffort,
|
|
4093
|
+
turnExecutionPolicy,
|
|
3835
4094
|
mcpCredentialUpdates,
|
|
3836
4095
|
delivery: input.delivery ?? "send",
|
|
3837
4096
|
origin: delegatedServiceInitiator ? "operator" : input.origin ?? "human",
|
|
@@ -3892,6 +4151,48 @@ async function updateSessionTitle(deps, grant, sessionId, title, source) {
|
|
|
3892
4151
|
relatedSessionAccess: authorization?.relatedSessionAccess ?? "root"
|
|
3893
4152
|
};
|
|
3894
4153
|
}
|
|
4154
|
+
async function updateSessionMcpApprovalPolicy(deps, grant, sessionId, serverId, requireApproval) {
|
|
4155
|
+
const normalizedPolicy = SessionMcpApprovalPolicy.parse(requireApproval);
|
|
4156
|
+
await requireSessionAuthorization(deps, grant, {
|
|
4157
|
+
sessionId,
|
|
4158
|
+
operation: "session.mcp.approval_policy.write",
|
|
4159
|
+
surface: "core"
|
|
4160
|
+
});
|
|
4161
|
+
requirePermission(grant, "sessions:control");
|
|
4162
|
+
const outcome = {};
|
|
4163
|
+
const events = await appendSessionEventsWithLockedSessionUpdate(
|
|
4164
|
+
deps.db,
|
|
4165
|
+
grant.workspaceId,
|
|
4166
|
+
sessionId,
|
|
4167
|
+
async (_session, context) => {
|
|
4168
|
+
const result = await context.updateSessionMcpApprovalPolicy(serverId, normalizedPolicy);
|
|
4169
|
+
if (!result.server) {
|
|
4170
|
+
throw new HTTPException9(404, { message: "session MCP server not found" });
|
|
4171
|
+
}
|
|
4172
|
+
outcome.server = result.server;
|
|
4173
|
+
return {
|
|
4174
|
+
events: result.changed ? [
|
|
4175
|
+
{
|
|
4176
|
+
type: "session.mcp.approval_policy.updated",
|
|
4177
|
+
payload: {
|
|
4178
|
+
serverId,
|
|
4179
|
+
effectiveFrom: "next_attempt"
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
] : []
|
|
4183
|
+
};
|
|
4184
|
+
}
|
|
4185
|
+
);
|
|
4186
|
+
const updatedServer = outcome.server;
|
|
4187
|
+
if (!updatedServer) {
|
|
4188
|
+
throw new Error("session MCP approval policy update returned no server");
|
|
4189
|
+
}
|
|
4190
|
+
await publishDurableSessionEvents(deps.bus, grant.workspaceId, sessionId, events);
|
|
4191
|
+
return {
|
|
4192
|
+
server: updatedServer,
|
|
4193
|
+
effectiveFrom: "next_attempt"
|
|
4194
|
+
};
|
|
4195
|
+
}
|
|
3895
4196
|
async function readSessionLineage(deps, grant, sessionId) {
|
|
3896
4197
|
const authorization = await requireSessionAuthorization(deps, grant, {
|
|
3897
4198
|
sessionId,
|
|
@@ -4095,13 +4396,8 @@ function manualScheduledTaskTriggerUsageKey(workspaceId, taskId, triggerToken) {
|
|
|
4095
4396
|
return `agent_run.created:scheduled-trigger:${workspaceId}:${taskId}:${triggerToken}`;
|
|
4096
4397
|
}
|
|
4097
4398
|
async function validateScheduledTaskAgentConfig(input) {
|
|
4098
|
-
|
|
4099
|
-
await assertWorkspaceModelPolicyAllows(
|
|
4100
|
-
input.db,
|
|
4101
|
-
input.settings,
|
|
4102
|
-
input.workspaceId,
|
|
4103
|
-
input.payload.agentConfig.model
|
|
4104
|
-
);
|
|
4399
|
+
const model = canonicalConfiguredModel(input.settings, input.payload.agentConfig.model);
|
|
4400
|
+
await assertWorkspaceModelPolicyAllows(input.db, input.settings, input.workspaceId, model);
|
|
4105
4401
|
const resources = normalizeResources(input.payload.agentConfig.resources ?? []);
|
|
4106
4402
|
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
|
|
4107
4403
|
input.db,
|
|
@@ -4121,6 +4417,7 @@ async function validateScheduledTaskAgentConfig(input) {
|
|
|
4121
4417
|
await validateFileResources(input.db, input.workspaceId, resources);
|
|
4122
4418
|
return {
|
|
4123
4419
|
...input.payload.agentConfig,
|
|
4420
|
+
...model === void 0 || model === null ? {} : { model },
|
|
4124
4421
|
prompt,
|
|
4125
4422
|
resources,
|
|
4126
4423
|
tools
|
|
@@ -4423,6 +4720,7 @@ function composerDraft(row) {
|
|
|
4423
4720
|
text: row.text,
|
|
4424
4721
|
resources: row.resources,
|
|
4425
4722
|
tools: row.tools,
|
|
4723
|
+
toolsProvided: row.toolsProvided,
|
|
4426
4724
|
model: row.model,
|
|
4427
4725
|
reasoningEffort: row.reasoningEffort,
|
|
4428
4726
|
sourceTurnId: row.sourceTurnId,
|
|
@@ -4641,6 +4939,7 @@ async function getHumanComposerDraft(deps, context) {
|
|
|
4641
4939
|
text: "",
|
|
4642
4940
|
resources: [],
|
|
4643
4941
|
tools: [],
|
|
4942
|
+
toolsProvided: false,
|
|
4644
4943
|
model: session.model,
|
|
4645
4944
|
reasoningEffort: reasoningEffortForMetadata2(session.metadata, "medium"),
|
|
4646
4945
|
sourceTurnId: null,
|
|
@@ -4686,12 +4985,15 @@ export {
|
|
|
4686
4985
|
assertAllowedVariableSetVariableName,
|
|
4687
4986
|
assertConfiguredModel,
|
|
4688
4987
|
assertPackSandboxImageCompatible,
|
|
4988
|
+
assertToolRefsSubset,
|
|
4689
4989
|
assertWorkspaceDeletable,
|
|
4690
4990
|
assertWorkspaceMemberRemovable,
|
|
4691
4991
|
assertWorkspaceModelPolicyAllows,
|
|
4992
|
+
availableToolRefs,
|
|
4692
4993
|
buildCapabilityCatalog,
|
|
4693
4994
|
buildFleetContextForSession,
|
|
4694
4995
|
buildMarketingDailyAnalysisAgentConfig,
|
|
4996
|
+
canonicalConfiguredModel,
|
|
4695
4997
|
checkLimit,
|
|
4696
4998
|
classifyRigVerificationOutcome,
|
|
4697
4999
|
controlAgentSessionWorkstream,
|
|
@@ -4755,6 +5057,7 @@ export {
|
|
|
4755
5057
|
requireVariableSetForApi,
|
|
4756
5058
|
resolveCapabilityPack,
|
|
4757
5059
|
resolveMemberSubjectId,
|
|
5060
|
+
resolveSessionToolPolicy,
|
|
4758
5061
|
restoreScheduledTask,
|
|
4759
5062
|
rigActorForGrant,
|
|
4760
5063
|
routingEnabled,
|
|
@@ -4764,6 +5067,7 @@ export {
|
|
|
4764
5067
|
scheduledTaskToolsProvided,
|
|
4765
5068
|
scheduledTaskTriggerToken,
|
|
4766
5069
|
sendAgentSessionMessage,
|
|
5070
|
+
sessionWithEffectiveToolPolicy,
|
|
4767
5071
|
settingsWithEnabledCapabilityMcpServers,
|
|
4768
5072
|
settingsWithMcpCapabilityServers,
|
|
4769
5073
|
settingsWithSessionMcpServerMetadata,
|
|
@@ -4774,6 +5078,7 @@ export {
|
|
|
4774
5078
|
syncCreatedScheduledTask,
|
|
4775
5079
|
syncUpdatedScheduledTask,
|
|
4776
5080
|
updateRigForApi,
|
|
5081
|
+
updateSessionMcpApprovalPolicy,
|
|
4777
5082
|
updateSessionTitle,
|
|
4778
5083
|
validateFileResources,
|
|
4779
5084
|
validateGitHubRepositorySelection,
|
|
@@ -4781,10 +5086,13 @@ export {
|
|
|
4781
5086
|
validateGitHubRepositorySelectionShapes,
|
|
4782
5087
|
validateMcpCapabilityConnection,
|
|
4783
5088
|
validateToolRefs,
|
|
5089
|
+
validateToolRefsForSessionPolicy,
|
|
4784
5090
|
validateVariableSetAttachment,
|
|
4785
5091
|
validatedScheduledTaskUpdate,
|
|
4786
5092
|
withDefaultEnabledCapabilityMcpTools,
|
|
4787
5093
|
workflowIdForSession,
|
|
5094
|
+
workspaceSessionToolPolicyDefaultServerIds,
|
|
5095
|
+
workspaceSessionToolPolicyServerIds,
|
|
4788
5096
|
wrapChannelABoxWithRouting
|
|
4789
5097
|
};
|
|
4790
5098
|
//# sourceMappingURL=index.js.map
|