@opengeni/core 0.2.1 → 0.3.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 +16 -4
- package/dist/index.js +172 -17
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
- package/src/domain/resources.ts +14 -11
- package/src/domain/sessions.ts +206 -11
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Settings } from '@opengeni/config';
|
|
2
|
-
import { ScheduledTask, Document, Permission, AccessContext, AccessGrant, LimitAction, LimitDecision, CapabilityCatalogResponse, CreateCapabilityCatalogItemRequest, CapabilityCatalogItem, CapabilityInstallation, EnableCapabilityRequest, WorkspaceEnvironment, CapabilityPack, SocialConnection, ScheduledTaskAgentConfig, ToolRef, ResourceRef, CreateScheduledTaskRequest, UpdateScheduledTaskRequest, ReasoningEffort, SessionEvent, SessionTurn, GoalSpec, Session, WorkspaceMember } from '@opengeni/contracts';
|
|
2
|
+
import { ScheduledTask, Document, Permission, AccessContext, AccessGrant, LimitAction, LimitDecision, CapabilityCatalogResponse, CreateCapabilityCatalogItemRequest, CapabilityCatalogItem, CapabilityInstallation, EnableCapabilityRequest, WorkspaceEnvironment, CapabilityPack, SocialConnection, ScheduledTaskAgentConfig, ToolRef, ResourceRef, CreateScheduledTaskRequest, UpdateScheduledTaskRequest, ReasoningEffort, SessionMcpCredentialUpdateInput, SessionEvent, SessionTurn, GoalSpec, SessionMcpServerMetadata, Session, WorkspaceMember } from '@opengeni/contracts';
|
|
3
3
|
export { mergeToolRefs, stableJson } from '@opengeni/contracts';
|
|
4
|
-
import { Database, SandboxRecord, EnabledMcpCapabilityServer, UpdateScheduledTaskInput } from '@opengeni/db';
|
|
4
|
+
import { Database, SandboxRecord, EnabledMcpCapabilityServer, UpdateScheduledTaskInput, CreateSessionMcpServerInput, UpdateSessionMcpServerCredentialsInput } from '@opengeni/db';
|
|
5
5
|
import { DocumentServices } from '@opengeni/documents';
|
|
6
6
|
import { EventBus } from '@opengeni/events';
|
|
7
7
|
import { Observability } from '@opengeni/observability';
|
|
@@ -534,6 +534,7 @@ declare function manualScheduledTaskTriggerWorkflowId(taskId: string, triggerTok
|
|
|
534
534
|
*/
|
|
535
535
|
declare function manualScheduledTaskTriggerUsageKey(workspaceId: string, taskId: string, triggerToken: string): string;
|
|
536
536
|
|
|
537
|
+
declare function settingsWithSessionMcpServerMetadata(settings: Settings, servers: SessionMcpServerMetadata[]): Settings;
|
|
537
538
|
declare function createAndStartSession(input: {
|
|
538
539
|
db: Database;
|
|
539
540
|
bus: EventBus;
|
|
@@ -554,6 +555,8 @@ declare function createAndStartSession(input: {
|
|
|
554
555
|
} | null;
|
|
555
556
|
goal?: GoalSpec | null;
|
|
556
557
|
firstPartyMcpPermissions?: Permission[] | null;
|
|
558
|
+
mcpServers?: CreateSessionMcpServerInput[];
|
|
559
|
+
sessionMcpServers?: SessionMcpServerMetadata[];
|
|
557
560
|
parentSessionId?: string | null;
|
|
558
561
|
createIdempotencyKey?: string | null;
|
|
559
562
|
sandboxGroupId?: string | null;
|
|
@@ -597,7 +600,14 @@ declare function createAndStartSession(input: {
|
|
|
597
600
|
activeSandboxId: string | null;
|
|
598
601
|
activeEpoch: number;
|
|
599
602
|
environmentId: string | null;
|
|
600
|
-
firstPartyMcpPermissions: ("account:read" | "account:admin" | "members:manage" | "workspace:create" | "billing:read" | "billing:manage" | "workspace:read" | "workspace:admin" | "sessions:create" | "sessions:read" | "sessions:control" | "stream:view" | "stream:control" | "stream:acknowledge" | "files:upload" | "files:read" | "files:write" | "terminal:attach" | "documents:manage" | "documents:search" | "scheduled_tasks:manage" | "scheduled_tasks:run" | "github:manage" | "github:use" | "api_keys:manage" | "environments:manage" | "environments:use" | "goals:manage" | "enrollments:read" | "enrollments:manage")[] | null;
|
|
603
|
+
firstPartyMcpPermissions: ("account:read" | "account:admin" | "members:manage" | "workspace:create" | "billing:read" | "billing:manage" | "workspace:read" | "workspace:admin" | "sessions:create" | "sessions:read" | "sessions:control" | "stream:view" | "stream:control" | "stream:acknowledge" | "files:upload" | "files:read" | "files:write" | "terminal:attach" | "documents:manage" | "documents:search" | "scheduled_tasks:manage" | "scheduled_tasks:run" | "github:manage" | "github:use" | "api_keys:manage" | "environments:manage" | "environments:use" | "mcp_servers:attach" | "goals:manage" | "enrollments:read" | "enrollments:manage")[] | null;
|
|
604
|
+
mcpServers: {
|
|
605
|
+
id: string;
|
|
606
|
+
name: string | null;
|
|
607
|
+
url: string;
|
|
608
|
+
headerNames: string[];
|
|
609
|
+
credentialVersion: number;
|
|
610
|
+
}[];
|
|
601
611
|
parentSessionId: string | null;
|
|
602
612
|
createIdempotencyKey: string | null;
|
|
603
613
|
temporalWorkflowId: string | null;
|
|
@@ -650,6 +660,7 @@ declare function postUserMessageTurn(input: {
|
|
|
650
660
|
model?: string | null;
|
|
651
661
|
reasoningEffort?: Settings["openaiReasoningEffort"] | null;
|
|
652
662
|
clientEventId?: string;
|
|
663
|
+
mcpCredentialUpdates?: UpdateSessionMcpServerCredentialsInput[];
|
|
653
664
|
}): Promise<{
|
|
654
665
|
accepted: SessionEvent;
|
|
655
666
|
turn: SessionTurn;
|
|
@@ -677,6 +688,7 @@ declare function acceptSessionUserMessage(deps: ApiRouteDeps, grant: AccessGrant
|
|
|
677
688
|
model?: string | null;
|
|
678
689
|
reasoningEffort?: ReasoningEffort | null;
|
|
679
690
|
clientEventId?: string;
|
|
691
|
+
mcpCredentialUpdates?: SessionMcpCredentialUpdateInput[];
|
|
680
692
|
}): Promise<{
|
|
681
693
|
accepted: SessionEvent;
|
|
682
694
|
turn: SessionTurn;
|
|
@@ -732,4 +744,4 @@ declare function assertWorkspaceDeletable(input: {
|
|
|
732
744
|
activeSessionCount: number;
|
|
733
745
|
}): void;
|
|
734
746
|
|
|
735
|
-
export { type AccessDeps, type ApiRouteDeps, type ApiSandboxClient, type ApiSandboxSession, type AppDependencies, type ChannelARoutingServices, type DocumentIndexClient, type FleetContext, type FleetListResult, type FleetLiveness, type FleetSandboxEntry, type FleetServices, type FleetSwapResult, type LimitCheckInput, MARKETING_SOCIAL_PACK_ID, MAX_ENVIRONMENTS_PER_WORKSPACE, MAX_VARIABLES_PER_ENVIRONMENT, type ManagedAuth, type McpCapabilityProbe, type McpCapabilityProbeInput, type McpCapabilityProbeResult, type ObjectStorageDependency, type ProvisionResult, type ResumeBoxByIdInput, type ResumedSandboxSession, type RunOnOp, type RunOnResult, type SessionWorkflowClient, acceptSessionUserMessage, assertAllowedEnvironmentVariableName, assertConfiguredModel, assertPackSandboxImageCompatible, assertWorkspaceDeletable, assertWorkspaceMemberRemovable, buildCapabilityCatalog, buildFleetContextForSession, buildMarketingDailyAnalysisAgentConfig, checkLimit, createAndStartSession, createCatalogItem, createSessionForRequest, createValidatedScheduledTask, disableCapability, discoverMcpRegistryCapabilities, enableCapability, enabledCapabilityMcpToolRefs, getCapabilityPack, hasPermission, isBuiltInCapabilityPack, isUserMember, listCapabilityPacks, listFleet, listWorkspaceCapabilityPacks, manualScheduledTaskTriggerUsageKey, manualScheduledTaskTriggerWorkflowId, memberCanAdminister, mergeResourceRefs, normalizeResources, officialMcpRegistryUrl, postUserMessageTurn, provisionSandbox, reasoningEffortForSession, recordEnvironmentAuditEvent, recordWorkspaceUsage, relayConfigFromSettings, relayDialBaseFromSettings, requireAccessContext, requireAccessGrant, requireEnvironmentEncryption, requireEnvironmentForApi, requireLimit, requirePermission, requireQueuedTurnForApi, requireScheduledTaskForApi, resolveCapabilityPack, resolveMemberSubjectId, restoreScheduledTask, routingEnabled, runOnSandbox, scheduledTaskTemporalScheduleId, scheduledTaskToolsProvided, scheduledTaskTriggerToken, settingsWithEnabledCapabilityMcpServers, settingsWithMcpCapabilityServers, swapActiveSandbox, syncCreatedScheduledTask, syncUpdatedScheduledTask, updateSessionTitle, validateEnvironmentAttachment, validateFileResources, validateGitHubRepositorySelection, validateGitHubRepositorySelectionShape, validateMcpCapabilityConnection, validateToolRefs, validatedScheduledTaskUpdate, withDefaultEnabledCapabilityMcpTools, workflowIdForSession, wrapChannelABoxWithRouting };
|
|
747
|
+
export { type AccessDeps, type ApiRouteDeps, type ApiSandboxClient, type ApiSandboxSession, type AppDependencies, type ChannelARoutingServices, type DocumentIndexClient, type FleetContext, type FleetListResult, type FleetLiveness, type FleetSandboxEntry, type FleetServices, type FleetSwapResult, type LimitCheckInput, MARKETING_SOCIAL_PACK_ID, MAX_ENVIRONMENTS_PER_WORKSPACE, MAX_VARIABLES_PER_ENVIRONMENT, type ManagedAuth, type McpCapabilityProbe, type McpCapabilityProbeInput, type McpCapabilityProbeResult, type ObjectStorageDependency, type ProvisionResult, type ResumeBoxByIdInput, type ResumedSandboxSession, type RunOnOp, type RunOnResult, type SessionWorkflowClient, acceptSessionUserMessage, assertAllowedEnvironmentVariableName, assertConfiguredModel, assertPackSandboxImageCompatible, assertWorkspaceDeletable, assertWorkspaceMemberRemovable, buildCapabilityCatalog, buildFleetContextForSession, buildMarketingDailyAnalysisAgentConfig, checkLimit, createAndStartSession, createCatalogItem, createSessionForRequest, createValidatedScheduledTask, disableCapability, discoverMcpRegistryCapabilities, enableCapability, enabledCapabilityMcpToolRefs, getCapabilityPack, hasPermission, isBuiltInCapabilityPack, isUserMember, listCapabilityPacks, listFleet, listWorkspaceCapabilityPacks, manualScheduledTaskTriggerUsageKey, manualScheduledTaskTriggerWorkflowId, memberCanAdminister, mergeResourceRefs, normalizeResources, officialMcpRegistryUrl, postUserMessageTurn, provisionSandbox, reasoningEffortForSession, recordEnvironmentAuditEvent, recordWorkspaceUsage, relayConfigFromSettings, relayDialBaseFromSettings, requireAccessContext, requireAccessGrant, requireEnvironmentEncryption, requireEnvironmentForApi, requireLimit, requirePermission, requireQueuedTurnForApi, requireScheduledTaskForApi, resolveCapabilityPack, resolveMemberSubjectId, restoreScheduledTask, routingEnabled, runOnSandbox, scheduledTaskTemporalScheduleId, scheduledTaskToolsProvided, scheduledTaskTriggerToken, settingsWithEnabledCapabilityMcpServers, settingsWithMcpCapabilityServers, settingsWithSessionMcpServerMetadata, swapActiveSandbox, syncCreatedScheduledTask, syncUpdatedScheduledTask, updateSessionTitle, validateEnvironmentAttachment, validateFileResources, validateGitHubRepositorySelection, validateGitHubRepositorySelectionShape, validateMcpCapabilityConnection, validateToolRefs, validatedScheduledTaskUpdate, withDefaultEnabledCapabilityMcpTools, workflowIdForSession, wrapChannelABoxWithRouting };
|
package/dist/index.js
CHANGED
|
@@ -1664,22 +1664,21 @@ import {
|
|
|
1664
1664
|
import { HTTPException as HTTPException7 } from "hono/http-exception";
|
|
1665
1665
|
function validateToolRefs(tools, settings) {
|
|
1666
1666
|
const mcpServerIds = new Set(settings.mcpServers.map((server) => server.id));
|
|
1667
|
-
const selected = /* @__PURE__ */ new Set();
|
|
1668
1667
|
const out = [];
|
|
1669
1668
|
for (const tool of tools) {
|
|
1670
1669
|
if (tool.kind !== "mcp") {
|
|
1671
1670
|
throw new HTTPException7(422, { message: `unsupported tool kind: ${tool.kind}` });
|
|
1672
1671
|
}
|
|
1672
|
+
const optional = tool.optional === true;
|
|
1673
1673
|
if (!mcpServerIds.has(tool.id)) {
|
|
1674
|
+
if (optional) {
|
|
1675
|
+
continue;
|
|
1676
|
+
}
|
|
1674
1677
|
throw new HTTPException7(422, { message: `unknown MCP server id: ${tool.id}` });
|
|
1675
1678
|
}
|
|
1676
|
-
|
|
1677
|
-
continue;
|
|
1678
|
-
}
|
|
1679
|
-
selected.add(tool.id);
|
|
1680
|
-
out.push({ kind: "mcp", id: tool.id });
|
|
1679
|
+
out.push(optional ? { kind: "mcp", id: tool.id, optional: true } : { kind: "mcp", id: tool.id });
|
|
1681
1680
|
}
|
|
1682
|
-
return out;
|
|
1681
|
+
return mergeToolRefs([], out);
|
|
1683
1682
|
}
|
|
1684
1683
|
function enabledCapabilityMcpToolRefs(settings, runtimeSettings) {
|
|
1685
1684
|
const configuredIds = new Set(settings.mcpServers.map((server) => server.id));
|
|
@@ -1865,6 +1864,7 @@ import {
|
|
|
1865
1864
|
createSessionGoal,
|
|
1866
1865
|
createSessionWithIdempotencyKey,
|
|
1867
1866
|
enqueueSessionTurn,
|
|
1867
|
+
encryptEnvironmentValue as encryptEnvironmentValue2,
|
|
1868
1868
|
getAnySessionInGroup,
|
|
1869
1869
|
getEnrollment as getEnrollment2,
|
|
1870
1870
|
listDistinctEnvironmentIdsInGroup,
|
|
@@ -1878,6 +1878,140 @@ import {
|
|
|
1878
1878
|
} from "@opengeni/db";
|
|
1879
1879
|
import { appendAndPublishEvents } from "@opengeni/events";
|
|
1880
1880
|
import { HTTPException as HTTPException8 } from "hono/http-exception";
|
|
1881
|
+
var reservedSessionMcpServerIds = /* @__PURE__ */ new Set(["opengeni", "files", "docs", "codex_apps"]);
|
|
1882
|
+
var maxSessionMcpCredentialHeaders = 16;
|
|
1883
|
+
var maxSessionMcpCredentialHeaderValueLength = 4096;
|
|
1884
|
+
var sessionMcpCredentialHeaderName = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
|
|
1885
|
+
function normalizedSessionMcpCredentialHeaders(headers) {
|
|
1886
|
+
if (!headers) {
|
|
1887
|
+
return {};
|
|
1888
|
+
}
|
|
1889
|
+
const entries = Object.entries(headers).map(([name, value]) => [name.trim(), value]).filter(([name]) => name.length > 0);
|
|
1890
|
+
if (entries.length > maxSessionMcpCredentialHeaders) {
|
|
1891
|
+
throw new HTTPException8(422, { message: `a session MCP server supports at most ${maxSessionMcpCredentialHeaders} credential headers` });
|
|
1892
|
+
}
|
|
1893
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1894
|
+
for (const [name, value] of entries) {
|
|
1895
|
+
if (!sessionMcpCredentialHeaderName.test(name)) {
|
|
1896
|
+
throw new HTTPException8(422, { message: `invalid credential header name: ${name}` });
|
|
1897
|
+
}
|
|
1898
|
+
const lower = name.toLowerCase();
|
|
1899
|
+
if (seen.has(lower)) {
|
|
1900
|
+
throw new HTTPException8(422, { message: `duplicate credential header name: ${name}` });
|
|
1901
|
+
}
|
|
1902
|
+
seen.add(lower);
|
|
1903
|
+
if (value.length === 0 || value.length > maxSessionMcpCredentialHeaderValueLength) {
|
|
1904
|
+
throw new HTTPException8(422, { message: `credential header ${name} must be 1-${maxSessionMcpCredentialHeaderValueLength} characters` });
|
|
1905
|
+
}
|
|
1906
|
+
if (/[\u0000-\u0008\u000A-\u001F\u007F]/.test(value)) {
|
|
1907
|
+
throw new HTTPException8(422, { message: `credential header ${name} contains forbidden control characters` });
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
return Object.fromEntries(entries);
|
|
1911
|
+
}
|
|
1912
|
+
function mcpServerConfigFromInput(server) {
|
|
1913
|
+
return {
|
|
1914
|
+
id: server.id,
|
|
1915
|
+
...server.name ? { name: server.name } : {},
|
|
1916
|
+
url: server.url,
|
|
1917
|
+
...server.allowedTools ? { allowedTools: server.allowedTools } : {},
|
|
1918
|
+
...server.timeoutMs ? { timeoutMs: server.timeoutMs } : {},
|
|
1919
|
+
cacheToolsList: server.cacheToolsList ?? false
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
function mcpServerConfigFromMetadata(server) {
|
|
1923
|
+
return {
|
|
1924
|
+
id: server.id,
|
|
1925
|
+
...server.name ? { name: server.name } : {},
|
|
1926
|
+
url: server.url,
|
|
1927
|
+
cacheToolsList: false
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
function settingsWithSessionMcpServerConfigs(settings, servers) {
|
|
1931
|
+
if (servers.length === 0) {
|
|
1932
|
+
return settings;
|
|
1933
|
+
}
|
|
1934
|
+
const sessionIds = new Set(servers.map((server) => server.id));
|
|
1935
|
+
return {
|
|
1936
|
+
...settings,
|
|
1937
|
+
mcpServers: [
|
|
1938
|
+
...settings.mcpServers.filter((server) => !sessionIds.has(server.id)),
|
|
1939
|
+
...servers
|
|
1940
|
+
]
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
function settingsWithSessionMcpServerMetadata(settings, servers) {
|
|
1944
|
+
return settingsWithSessionMcpServerConfigs(settings, servers.map(mcpServerConfigFromMetadata));
|
|
1945
|
+
}
|
|
1946
|
+
function validateSessionMcpServersForCreate(settings, grant, servers) {
|
|
1947
|
+
if (servers.length === 0) {
|
|
1948
|
+
return { runtimeServers: [], dbServers: [], metadata: [] };
|
|
1949
|
+
}
|
|
1950
|
+
requirePermission(grant, "mcp_servers:attach");
|
|
1951
|
+
const encryptionKey = requireEnvironmentEncryption(settings);
|
|
1952
|
+
const existingIds = new Set(settings.mcpServers.map((server) => server.id));
|
|
1953
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
1954
|
+
const runtimeServers = [];
|
|
1955
|
+
const dbServers = [];
|
|
1956
|
+
const metadata = [];
|
|
1957
|
+
for (const server of servers) {
|
|
1958
|
+
if (seenIds.has(server.id)) {
|
|
1959
|
+
throw new HTTPException8(422, { message: `duplicate session MCP server id: ${server.id}` });
|
|
1960
|
+
}
|
|
1961
|
+
seenIds.add(server.id);
|
|
1962
|
+
if (reservedSessionMcpServerIds.has(server.id) || existingIds.has(server.id)) {
|
|
1963
|
+
throw new HTTPException8(422, { message: `MCP server id already exists: ${server.id}` });
|
|
1964
|
+
}
|
|
1965
|
+
const headers = normalizedSessionMcpCredentialHeaders(server.headers);
|
|
1966
|
+
const headersEncrypted = Object.fromEntries(
|
|
1967
|
+
Object.entries(headers).map(([name, value]) => [name, encryptEnvironmentValue2(encryptionKey, value)])
|
|
1968
|
+
);
|
|
1969
|
+
runtimeServers.push(mcpServerConfigFromInput(server));
|
|
1970
|
+
dbServers.push({
|
|
1971
|
+
id: server.id,
|
|
1972
|
+
name: server.name ?? null,
|
|
1973
|
+
url: server.url,
|
|
1974
|
+
allowedTools: server.allowedTools ?? null,
|
|
1975
|
+
timeoutMs: server.timeoutMs ?? null,
|
|
1976
|
+
cacheToolsList: server.cacheToolsList ?? false,
|
|
1977
|
+
headersEncrypted
|
|
1978
|
+
});
|
|
1979
|
+
metadata.push({
|
|
1980
|
+
id: server.id,
|
|
1981
|
+
name: server.name ?? null,
|
|
1982
|
+
url: server.url,
|
|
1983
|
+
headerNames: Object.keys(headersEncrypted).sort(),
|
|
1984
|
+
credentialVersion: 1
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
return { runtimeServers, dbServers, metadata };
|
|
1988
|
+
}
|
|
1989
|
+
function validateSessionMcpCredentialUpdates(input) {
|
|
1990
|
+
if (input.updates.length === 0) {
|
|
1991
|
+
return [];
|
|
1992
|
+
}
|
|
1993
|
+
requirePermission(input.grant, "mcp_servers:attach");
|
|
1994
|
+
const encryptionKey = requireEnvironmentEncryption(input.settings);
|
|
1995
|
+
const knownIds = new Set(input.session.mcpServers.map((server) => server.id));
|
|
1996
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
1997
|
+
const encryptedUpdates = input.updates.map((update) => {
|
|
1998
|
+
if (seenIds.has(update.id)) {
|
|
1999
|
+
throw new HTTPException8(422, { message: `duplicate session MCP credential update id: ${update.id}` });
|
|
2000
|
+
}
|
|
2001
|
+
seenIds.add(update.id);
|
|
2002
|
+
if (!knownIds.has(update.id)) {
|
|
2003
|
+
throw new HTTPException8(422, { message: `unknown session MCP server id: ${update.id}` });
|
|
2004
|
+
}
|
|
2005
|
+
const headers = normalizedSessionMcpCredentialHeaders(update.headers);
|
|
2006
|
+
return {
|
|
2007
|
+
id: update.id,
|
|
2008
|
+
headersEncrypted: Object.fromEntries(
|
|
2009
|
+
Object.entries(headers).map(([name, value]) => [name, encryptEnvironmentValue2(encryptionKey, value)])
|
|
2010
|
+
)
|
|
2011
|
+
};
|
|
2012
|
+
});
|
|
2013
|
+
return encryptedUpdates;
|
|
2014
|
+
}
|
|
1881
2015
|
async function createAndStartSession(input) {
|
|
1882
2016
|
const sessionMetadata = {
|
|
1883
2017
|
...input.metadata,
|
|
@@ -1903,7 +2037,8 @@ async function createAndStartSession(input) {
|
|
|
1903
2037
|
parentSessionId: input.parentSessionId ?? null,
|
|
1904
2038
|
createIdempotencyKey: input.createIdempotencyKey,
|
|
1905
2039
|
sandboxGroupId: input.sandboxGroupId ?? null,
|
|
1906
|
-
...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {}
|
|
2040
|
+
...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {},
|
|
2041
|
+
mcpServers: input.mcpServers ?? []
|
|
1907
2042
|
});
|
|
1908
2043
|
if (!created) {
|
|
1909
2044
|
return keyed;
|
|
@@ -1923,7 +2058,8 @@ async function createAndStartSession(input) {
|
|
|
1923
2058
|
firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
|
|
1924
2059
|
parentSessionId: input.parentSessionId ?? null,
|
|
1925
2060
|
sandboxGroupId: input.sandboxGroupId ?? null,
|
|
1926
|
-
...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {}
|
|
2061
|
+
...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {},
|
|
2062
|
+
mcpServers: input.mcpServers ?? []
|
|
1927
2063
|
});
|
|
1928
2064
|
return await finishStartSession(input, session);
|
|
1929
2065
|
}
|
|
@@ -1947,7 +2083,8 @@ async function finishStartSession(input, session) {
|
|
|
1947
2083
|
type: "session.created",
|
|
1948
2084
|
payload: {
|
|
1949
2085
|
status: "queued",
|
|
1950
|
-
...input.environment ? { environmentId: input.environment.id, environmentName: input.environment.name } : {}
|
|
2086
|
+
...input.environment ? { environmentId: input.environment.id, environmentName: input.environment.name } : {},
|
|
2087
|
+
...input.sessionMcpServers?.length ? { mcpServers: input.sessionMcpServers } : {}
|
|
1951
2088
|
}
|
|
1952
2089
|
},
|
|
1953
2090
|
...goal ? [{
|
|
@@ -2057,10 +2194,14 @@ async function postUserMessageTurn(input) {
|
|
|
2057
2194
|
const requestedModel = input.model ?? null;
|
|
2058
2195
|
const requestedReasoningEffort = input.reasoningEffort ?? null;
|
|
2059
2196
|
assertConfiguredModel(settings, requestedModel);
|
|
2060
|
-
const appended = await appendSessionEventsWithLockedSessionUpdate(db, workspaceId, sessionId, (lockedSession) => {
|
|
2197
|
+
const appended = await appendSessionEventsWithLockedSessionUpdate(db, workspaceId, sessionId, async (lockedSession, lockedUpdate) => {
|
|
2061
2198
|
if (lockedSession.status === "cancelled") {
|
|
2062
2199
|
throw new HTTPException8(409, { message: `session is ${lockedSession.status}; cannot accept a new user message` });
|
|
2063
2200
|
}
|
|
2201
|
+
const mcpCredentialUpdates = input.mcpCredentialUpdates?.length ? await lockedUpdate.updateSessionMcpServerCredentials(input.mcpCredentialUpdates) : { servers: [], missingIds: [] };
|
|
2202
|
+
if (mcpCredentialUpdates.missingIds.length > 0) {
|
|
2203
|
+
throw new HTTPException8(422, { message: `unknown session MCP server id: ${mcpCredentialUpdates.missingIds[0]}` });
|
|
2204
|
+
}
|
|
2064
2205
|
const nextResources = mergeResourceRefs(lockedSession.resources, input.resources);
|
|
2065
2206
|
const nextTools = mergeToolRefs(lockedSession.tools, input.tools);
|
|
2066
2207
|
const shouldQueueSession = lockedSession.status === "idle" || lockedSession.status === "failed";
|
|
@@ -2073,7 +2214,8 @@ async function postUserMessageTurn(input) {
|
|
|
2073
2214
|
...input.resources.length ? { resources: input.resources } : {},
|
|
2074
2215
|
...input.tools.length ? { tools: input.tools } : {},
|
|
2075
2216
|
...requestedModel ? { model: requestedModel } : {},
|
|
2076
|
-
...requestedReasoningEffort ? { reasoningEffort: requestedReasoningEffort } : {}
|
|
2217
|
+
...requestedReasoningEffort ? { reasoningEffort: requestedReasoningEffort } : {},
|
|
2218
|
+
...mcpCredentialUpdates.servers.length ? { mcpCredentialUpdates: mcpCredentialUpdates.servers } : {}
|
|
2077
2219
|
},
|
|
2078
2220
|
...input.clientEventId ? { clientEventId: input.clientEventId } : {}
|
|
2079
2221
|
},
|
|
@@ -2121,10 +2263,12 @@ async function postUserMessageTurn(input) {
|
|
|
2121
2263
|
async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
2122
2264
|
const { settings, db, bus, workflowClient, objectStorage } = deps;
|
|
2123
2265
|
const payload = CreateSessionRequest.parse(rawPayload);
|
|
2124
|
-
const
|
|
2266
|
+
const capabilityRuntimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings);
|
|
2267
|
+
const sessionMcpServers = validateSessionMcpServersForCreate(capabilityRuntimeSettings, grant, payload.mcpServers);
|
|
2268
|
+
const runtimeSettings = settingsWithSessionMcpServerConfigs(capabilityRuntimeSettings, sessionMcpServers.runtimeServers);
|
|
2125
2269
|
const resources = normalizeResources(payload.resources);
|
|
2126
2270
|
const requestedTools = validateToolRefs(payload.tools, runtimeSettings);
|
|
2127
|
-
const defaultedTools = hasOwnProperty(rawPayload, "tools") ? requestedTools : withDefaultEnabledCapabilityMcpTools(requestedTools, settings,
|
|
2271
|
+
const defaultedTools = hasOwnProperty(rawPayload, "tools") ? requestedTools : withDefaultEnabledCapabilityMcpTools(requestedTools, settings, capabilityRuntimeSettings);
|
|
2128
2272
|
const tools = withFirstPartyTools(defaultedTools, runtimeSettings);
|
|
2129
2273
|
await validateGitHubRepositorySelection(db, workspaceId, resources);
|
|
2130
2274
|
if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
@@ -2228,6 +2372,8 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
|
2228
2372
|
environment: environment ? { id: environment.id, name: environment.name } : null,
|
|
2229
2373
|
goal: payload.goal ?? null,
|
|
2230
2374
|
firstPartyMcpPermissions,
|
|
2375
|
+
mcpServers: sessionMcpServers.dbServers,
|
|
2376
|
+
sessionMcpServers: sessionMcpServers.metadata,
|
|
2231
2377
|
parentSessionId,
|
|
2232
2378
|
createIdempotencyKey: payload.idempotencyKey ?? null,
|
|
2233
2379
|
// Create-time machine targeting (A-2a): when a target sandbox is named, the
|
|
@@ -2251,11 +2397,12 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
|
|
|
2251
2397
|
}
|
|
2252
2398
|
async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, input) {
|
|
2253
2399
|
const { settings, db, bus, workflowClient, objectStorage } = deps;
|
|
2254
|
-
const
|
|
2400
|
+
const capabilityRuntimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings);
|
|
2401
|
+
const existingSession = await requireSession2(db, workspaceId, sessionId);
|
|
2402
|
+
const runtimeSettings = settingsWithSessionMcpServerMetadata(capabilityRuntimeSettings, existingSession.mcpServers);
|
|
2255
2403
|
const requestedResources = normalizeResources(input.resources ?? []);
|
|
2256
2404
|
const validatedTools = validateToolRefs(input.tools ?? [], runtimeSettings);
|
|
2257
|
-
const requestedTools = input.toolsProvided ? validatedTools : withDefaultEnabledCapabilityMcpTools(validatedTools, settings,
|
|
2258
|
-
const existingSession = await requireSession2(db, workspaceId, sessionId);
|
|
2405
|
+
const requestedTools = input.toolsProvided ? validatedTools : withDefaultEnabledCapabilityMcpTools(validatedTools, settings, capabilityRuntimeSettings);
|
|
2259
2406
|
await requireLimit(deps, {
|
|
2260
2407
|
accountId: grant.accountId,
|
|
2261
2408
|
workspaceId,
|
|
@@ -2268,6 +2415,12 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
|
|
|
2268
2415
|
}
|
|
2269
2416
|
await validateFileResources(db, workspaceId, requestedResources);
|
|
2270
2417
|
await validateGitHubRepositorySelection(db, workspaceId, [...existingSession.resources, ...requestedResources]);
|
|
2418
|
+
const mcpCredentialUpdates = validateSessionMcpCredentialUpdates({
|
|
2419
|
+
settings,
|
|
2420
|
+
grant,
|
|
2421
|
+
session: existingSession,
|
|
2422
|
+
updates: input.mcpCredentialUpdates ?? []
|
|
2423
|
+
});
|
|
2271
2424
|
const { accepted, turn } = await postUserMessageTurn({
|
|
2272
2425
|
db,
|
|
2273
2426
|
bus,
|
|
@@ -2281,6 +2434,7 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
|
|
|
2281
2434
|
tools: requestedTools,
|
|
2282
2435
|
model: input.model ?? null,
|
|
2283
2436
|
reasoningEffort: input.reasoningEffort ?? null,
|
|
2437
|
+
mcpCredentialUpdates,
|
|
2284
2438
|
...input.clientEventId ? { clientEventId: input.clientEventId } : {}
|
|
2285
2439
|
});
|
|
2286
2440
|
await recordWorkspaceUsage(deps, {
|
|
@@ -2608,6 +2762,7 @@ export {
|
|
|
2608
2762
|
scheduledTaskTriggerToken,
|
|
2609
2763
|
settingsWithEnabledCapabilityMcpServers,
|
|
2610
2764
|
settingsWithMcpCapabilityServers,
|
|
2765
|
+
settingsWithSessionMcpServerMetadata,
|
|
2611
2766
|
stableJson,
|
|
2612
2767
|
swapActiveSandbox,
|
|
2613
2768
|
syncCreatedScheduledTask,
|