@opengeni/core 2.6.4-canary.0 → 2.7.2-canary.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/dependencies.d.ts +2 -0
- package/dist/domain/host-mcp-authority-source-admission.d.ts +10 -0
- package/dist/domain/packs.d.ts +1 -0
- package/dist/domain/product-integration-pack.d.ts +50 -0
- package/dist/domain/sessions.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1194 -395
- package/dist/index.js.map +1 -1
- package/dist/model-catalog.d.ts +16 -0
- package/dist/sandbox/fleet.d.ts +19 -0
- package/package.json +10 -10
- package/src/dependencies.ts +2 -0
- package/src/domain/capabilities.ts +19 -3
- package/src/domain/host-mcp-authority-source-admission.ts +25 -0
- package/src/domain/insights.ts +32 -0
- package/src/domain/packs.ts +48 -18
- package/src/domain/personal-connection-delegations.ts +14 -6
- package/src/domain/product-integration-pack.ts +494 -0
- package/src/domain/scheduled-tasks.ts +6 -15
- package/src/domain/sessions.ts +197 -65
- package/src/index.ts +2 -0
- package/src/model-catalog.ts +183 -6
- package/src/sandbox/fleet.ts +85 -23
package/src/domain/sessions.ts
CHANGED
|
@@ -2,11 +2,13 @@ import { CODEX_MODEL_ID_PREFIX, isCodexBilledModel } from "@opengeni/codex";
|
|
|
2
2
|
import {
|
|
3
3
|
canonicalizeConfiguredModelId,
|
|
4
4
|
configuredAllowedModels,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
ORGANIZATION_GATEWAY_MODEL_ID_PREFIX,
|
|
6
|
+
ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX,
|
|
7
7
|
resolveFirstPartyMcpToolPolicy,
|
|
8
8
|
policyProviderIdForModel,
|
|
9
9
|
resolveTurnExecutionPolicyV1,
|
|
10
|
+
WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
|
|
11
|
+
WORKSPACE_OPENROUTER_MODEL_ID_PREFIX,
|
|
10
12
|
XAI_SUBSCRIPTION_MODEL_ID_PREFIX,
|
|
11
13
|
type Settings,
|
|
12
14
|
} from "@opengeni/config";
|
|
@@ -18,6 +20,7 @@ import {
|
|
|
18
20
|
DraftTimelineAnnotations,
|
|
19
21
|
FIRST_PARTY_MCP_TOOL_NAMES,
|
|
20
22
|
OPENGENI_SLACK_BOT_SESSION_METADATA_KEY,
|
|
23
|
+
SessionSkills,
|
|
21
24
|
SessionSpawnDenial,
|
|
22
25
|
ServiceTurnInitiator,
|
|
23
26
|
ServiceTurnInitiatorContext,
|
|
@@ -63,6 +66,7 @@ import {
|
|
|
63
66
|
type XaiProviderAccountAuthoritySnapshotV1,
|
|
64
67
|
} from "@opengeni/contracts";
|
|
65
68
|
import {
|
|
69
|
+
assertExactNewSessionDraftInTransaction,
|
|
66
70
|
createSession,
|
|
67
71
|
createSessionWithIdempotencyKeyResult,
|
|
68
72
|
canonicalSessionCommandHash,
|
|
@@ -74,6 +78,7 @@ import {
|
|
|
74
78
|
getWorkspaceDefaultRigId,
|
|
75
79
|
listDistinctVariableSetSelectionsInGroup,
|
|
76
80
|
listDistinctRigVersionIdsInGroup,
|
|
81
|
+
listInstalledPortableSkills,
|
|
77
82
|
getSandbox,
|
|
78
83
|
getSession,
|
|
79
84
|
getInitializedSessionCreateReplay,
|
|
@@ -91,9 +96,9 @@ import {
|
|
|
91
96
|
initializeSessionStartAtomically,
|
|
92
97
|
listSessionTurns,
|
|
93
98
|
listSessionMcpServersForChildInheritance,
|
|
94
|
-
lockActiveWorkspaceGatewayCustomModelForAdmission,
|
|
95
|
-
lockActiveWorkspaceOpenRouterCustomModelForAdmission,
|
|
96
99
|
requireSession,
|
|
100
|
+
setActiveSandbox,
|
|
101
|
+
setSubjectRlsContext,
|
|
97
102
|
replaySubmittedHumanPromptFromBoundaryReceipt,
|
|
98
103
|
submitHumanPromptInTransaction,
|
|
99
104
|
appendSessionEventsWithLockedSessionUpdate,
|
|
@@ -134,10 +139,16 @@ import {
|
|
|
134
139
|
requireSessionAuthorization,
|
|
135
140
|
SessionAuthorizationDeniedError,
|
|
136
141
|
} from "../session-authorization";
|
|
137
|
-
import {
|
|
142
|
+
import { assertHostMcpAuthoritySourceAdmissionEnabled } from "./host-mcp-authority-source-admission";
|
|
143
|
+
import {
|
|
144
|
+
preflightCreateTimeSandboxTarget,
|
|
145
|
+
swapActiveSandbox,
|
|
146
|
+
type FleetContext,
|
|
147
|
+
} from "../sandbox/fleet";
|
|
138
148
|
import { managedSessionGroupBackend } from "../sandbox/runtime-settings";
|
|
139
149
|
import {
|
|
140
150
|
isWorkspaceCustomModelId,
|
|
151
|
+
lockActiveCustomModelForAdmission,
|
|
141
152
|
resolveWorkspaceCatalogSettings,
|
|
142
153
|
workspaceCustomModelReference,
|
|
143
154
|
} from "../model-catalog";
|
|
@@ -170,6 +181,15 @@ const maxSessionMcpCredentialHeaderValueLength = 4096;
|
|
|
170
181
|
// Keep the durable snapshot below the shared event-preview array boundary so
|
|
171
182
|
// the generic lossy projection cannot silently rewrite this audit fact.
|
|
172
183
|
const maxToolPolicyAuditRefs = 40;
|
|
184
|
+
|
|
185
|
+
function isCatalogOverlayModel(modelId: string | null | undefined): boolean {
|
|
186
|
+
return (
|
|
187
|
+
modelId?.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX) === true ||
|
|
188
|
+
modelId?.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX) === true ||
|
|
189
|
+
modelId?.startsWith(ORGANIZATION_GATEWAY_MODEL_ID_PREFIX) === true ||
|
|
190
|
+
modelId?.startsWith(ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX) === true
|
|
191
|
+
);
|
|
192
|
+
}
|
|
173
193
|
// RFC 9110 field-name token characters.
|
|
174
194
|
const sessionMcpCredentialHeaderName = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
|
|
175
195
|
|
|
@@ -482,6 +502,7 @@ function validateSessionMcpServersForCreate(
|
|
|
482
502
|
const dbServers: CreateSessionMcpServerInput[] = [];
|
|
483
503
|
const metadata: SessionMcpServerMetadata[] = [];
|
|
484
504
|
for (const server of servers) {
|
|
505
|
+
assertHostMcpAuthoritySourceAdmissionEnabled(settings, server.connectionRef);
|
|
485
506
|
if (seenIds.has(server.id)) {
|
|
486
507
|
throw new HTTPException(422, {
|
|
487
508
|
message: `duplicate session MCP server id: ${server.id}`,
|
|
@@ -705,7 +726,11 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
705
726
|
createdByActor?: Extract<SessionCommandActor, { type: "agent_attempt" }> | null;
|
|
706
727
|
// Ordered low-to-high precedence. Names/ids only; session.created never
|
|
707
728
|
// carries variable values.
|
|
708
|
-
variableSets?: Array<{
|
|
729
|
+
variableSets?: Array<{
|
|
730
|
+
id: string;
|
|
731
|
+
name: string;
|
|
732
|
+
scope: VariableSet["scope"];
|
|
733
|
+
}>;
|
|
709
734
|
// The rig + frozen active rig version resolved at create (M3). Both null ⇒ a
|
|
710
735
|
// rig-less session (byte-for-byte today's behavior). Frozen here so a later
|
|
711
736
|
// rig promote never moves an existing session's version.
|
|
@@ -747,6 +772,9 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
747
772
|
// session. Every caller repairs or re-delivers the winner's one atomic start;
|
|
748
773
|
// the durable initializer prevents duplicate events or turns.
|
|
749
774
|
createIdempotencyKey?: string | null;
|
|
775
|
+
// Exact explicit installed-Skill selection. The database stores this only as
|
|
776
|
+
// keyed-create identity; runtime behavior comes from the frozen Skill content.
|
|
777
|
+
selectedInstalledSkillIds?: string[];
|
|
750
778
|
// The shared-sandbox group this session's box joins (addendum 05 §D). Null/
|
|
751
779
|
// omitted ⇒ a singleton group (the new row's own id, today's 1:1 behavior); a
|
|
752
780
|
// shared/{groupId} spawn passes the resolved group so both run in ONE box.
|
|
@@ -757,11 +785,11 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
757
785
|
// OS-labeling surfaces honestly reflect the machine.
|
|
758
786
|
sandboxOs?: Session["sandboxOs"];
|
|
759
787
|
// Create-time machine targeting (A-2a, RACE-FREE): the enrolled machine (a
|
|
760
|
-
// sandbox id) to run this session on. When set,
|
|
761
|
-
//
|
|
762
|
-
//
|
|
763
|
-
//
|
|
764
|
-
// target fails the create (422)
|
|
788
|
+
// sandbox id) to run this session on. When set, target liveness is preflighted
|
|
789
|
+
// before insertion, then the active-sandbox pointer is authority-checked and
|
|
790
|
+
// seeded (epoch-fenced) in the SAME transaction as the session row. The FIRST
|
|
791
|
+
// turn therefore routes to the chosen machine, while an invalid/unowned/offline
|
|
792
|
+
// target fails the create (422) without leaving a queued session shell.
|
|
765
793
|
// `workingDir` (optional) is the path/cwd base the chosen machine runs under,
|
|
766
794
|
// seeded alongside the pointer through the epoch-fenced CAS.
|
|
767
795
|
seedTargetSandbox?: {
|
|
@@ -814,36 +842,97 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
814
842
|
(input.workspaceCustomModel === true || input.workspaceGatewayCustomModel === true) &&
|
|
815
843
|
input.retainWorkspaceCustomModel !== true &&
|
|
816
844
|
input.retainWorkspaceGatewayModel !== true;
|
|
845
|
+
const seedTargetForNewSession = input.seedTargetSandbox ?? null;
|
|
846
|
+
const preflightTarget = seedTargetForNewSession
|
|
847
|
+
? await preflightCreateTimeSandboxTarget(
|
|
848
|
+
{
|
|
849
|
+
db: input.db,
|
|
850
|
+
settings: seedTargetForNewSession.settings,
|
|
851
|
+
bus: input.bus,
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
accountId: input.accountId,
|
|
855
|
+
workspaceId: input.workspaceId,
|
|
856
|
+
...(seedTargetForNewSession.resourceSubjectId
|
|
857
|
+
? { subjectId: seedTargetForNewSession.resourceSubjectId }
|
|
858
|
+
: {}),
|
|
859
|
+
},
|
|
860
|
+
seedTargetForNewSession.sandboxId,
|
|
861
|
+
seedTargetForNewSession.workingDir ?? null,
|
|
862
|
+
)
|
|
863
|
+
: null;
|
|
864
|
+
const targetPreflightFailureMessage =
|
|
865
|
+
preflightTarget && !preflightTarget.ok
|
|
866
|
+
? `cannot target sandbox ${seedTargetForNewSession!.sandboxId}: ${preflightTarget.reason}`
|
|
867
|
+
: null;
|
|
868
|
+
let targetSeededBeforeCreateCommit = false;
|
|
817
869
|
const beforeCreateCommit =
|
|
818
|
-
requiresActiveWorkspaceCustomModel ||
|
|
870
|
+
requiresActiveWorkspaceCustomModel ||
|
|
871
|
+
input.consumeNewSessionDraft ||
|
|
872
|
+
input.beforeCreateCommit ||
|
|
873
|
+
preflightTarget ||
|
|
874
|
+
targetPreflightFailureMessage
|
|
819
875
|
? async (tx: Database, sessionId: string, context?: { created: boolean }): Promise<void> => {
|
|
820
876
|
// A committed keyed replay already crossed this fence when its shell
|
|
821
877
|
// was first accepted. Revalidate only the transaction inserting a new
|
|
822
878
|
// session, while still running caller linkage on every replay.
|
|
879
|
+
if (targetPreflightFailureMessage && context?.created !== false) {
|
|
880
|
+
throw new HTTPException(422, {
|
|
881
|
+
message: targetPreflightFailureMessage,
|
|
882
|
+
});
|
|
883
|
+
}
|
|
823
884
|
if (requiresActiveWorkspaceCustomModel && context?.created !== false) {
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
workspaceId: input.workspaceId,
|
|
839
|
-
upstreamModelId,
|
|
840
|
-
});
|
|
885
|
+
const reference = {
|
|
886
|
+
scope: input.turnExecutionPolicy.providerId.startsWith("organization-")
|
|
887
|
+
? ("organization" as const)
|
|
888
|
+
: ("workspace" as const),
|
|
889
|
+
providerKind: input.turnExecutionPolicy.providerId.includes("openrouter")
|
|
890
|
+
? ("openrouter" as const)
|
|
891
|
+
: ("vercel_gateway" as const),
|
|
892
|
+
upstreamModelId: input.turnExecutionPolicy.upstreamModelId,
|
|
893
|
+
};
|
|
894
|
+
const active = await lockActiveCustomModelForAdmission(tx, {
|
|
895
|
+
accountId: input.accountId,
|
|
896
|
+
workspaceId: input.workspaceId,
|
|
897
|
+
reference,
|
|
898
|
+
});
|
|
841
899
|
if (!active) {
|
|
842
900
|
throw new HTTPException(422, {
|
|
843
901
|
message: `model is not available: ${input.model}`,
|
|
844
902
|
});
|
|
845
903
|
}
|
|
846
904
|
}
|
|
905
|
+
// Reject an already-stale browser draft before the newly inserted
|
|
906
|
+
// shell can commit. The initializer repeats this exact check while
|
|
907
|
+
// consuming the draft after it installs the first runnable unit.
|
|
908
|
+
if (input.consumeNewSessionDraft && context?.created !== false) {
|
|
909
|
+
await setSubjectRlsContext(tx, input.consumeNewSessionDraft.subjectId);
|
|
910
|
+
await assertExactNewSessionDraftInTransaction(tx, {
|
|
911
|
+
workspaceId: input.workspaceId,
|
|
912
|
+
subjectId: input.consumeNewSessionDraft.subjectId,
|
|
913
|
+
expectedRevision: input.consumeNewSessionDraft.expectedRevision,
|
|
914
|
+
expectedSnapshot: input.consumeNewSessionDraft.expectedSnapshot,
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
if (preflightTarget?.ok && context?.created !== false) {
|
|
918
|
+
const seeded = await setActiveSandbox(tx, {
|
|
919
|
+
accountId: input.accountId,
|
|
920
|
+
workspaceId: input.workspaceId,
|
|
921
|
+
sessionId,
|
|
922
|
+
targetSandboxId: preflightTarget.targetSandboxId,
|
|
923
|
+
expectedEpoch: 0,
|
|
924
|
+
...(seedTargetForNewSession?.resourceSubjectId
|
|
925
|
+
? { subjectId: seedTargetForNewSession.resourceSubjectId }
|
|
926
|
+
: {}),
|
|
927
|
+
workingDir: preflightTarget.workingDir,
|
|
928
|
+
});
|
|
929
|
+
if (!seeded.swapped) {
|
|
930
|
+
throw new HTTPException(422, {
|
|
931
|
+
message: `cannot target sandbox ${seedTargetForNewSession!.sandboxId}: target authority changed during session creation`,
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
targetSeededBeforeCreateCommit = true;
|
|
935
|
+
}
|
|
847
936
|
await input.beforeCreateCommit?.(tx, sessionId);
|
|
848
937
|
}
|
|
849
938
|
: undefined;
|
|
@@ -882,6 +971,7 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
882
971
|
policyRole: input.policyRole ?? null,
|
|
883
972
|
parentSessionId: input.parentSessionId ?? null,
|
|
884
973
|
createIdempotencyKey: input.createIdempotencyKey,
|
|
974
|
+
selectedInstalledSkillIds: input.selectedInstalledSkillIds ?? [],
|
|
885
975
|
sandboxGroupId: input.sandboxGroupId ?? null,
|
|
886
976
|
...(input.sandboxOs ? { sandboxOs: input.sandboxOs } : {}),
|
|
887
977
|
mcpServers: input.mcpServers ?? [],
|
|
@@ -928,7 +1018,10 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
928
1018
|
changed: finished.changed,
|
|
929
1019
|
};
|
|
930
1020
|
}
|
|
931
|
-
const finished = await finishStartSession(
|
|
1021
|
+
const finished = await finishStartSession(
|
|
1022
|
+
targetSeededBeforeCreateCommit ? { ...input, seedTargetSandbox: null } : input,
|
|
1023
|
+
keyed,
|
|
1024
|
+
);
|
|
932
1025
|
return {
|
|
933
1026
|
session: finished.session,
|
|
934
1027
|
outcome: "created",
|
|
@@ -989,7 +1082,10 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
989
1082
|
}
|
|
990
1083
|
throw error;
|
|
991
1084
|
}
|
|
992
|
-
const finished = await finishStartSession(
|
|
1085
|
+
const finished = await finishStartSession(
|
|
1086
|
+
targetSeededBeforeCreateCommit ? { ...input, seedTargetSandbox: null } : input,
|
|
1087
|
+
session,
|
|
1088
|
+
);
|
|
993
1089
|
return {
|
|
994
1090
|
session: finished.session,
|
|
995
1091
|
outcome: "created",
|
|
@@ -1027,7 +1123,11 @@ async function finishStartSession(
|
|
|
1027
1123
|
reasoningEffort: Settings["openaiReasoningEffort"];
|
|
1028
1124
|
turnExecutionPolicy: TurnExecutionPolicyV1;
|
|
1029
1125
|
sandboxBackend: Settings["sandboxBackend"];
|
|
1030
|
-
variableSets?: Array<{
|
|
1126
|
+
variableSets?: Array<{
|
|
1127
|
+
id: string;
|
|
1128
|
+
name: string;
|
|
1129
|
+
scope: VariableSet["scope"];
|
|
1130
|
+
}>;
|
|
1031
1131
|
goal?: GoalSpec | null;
|
|
1032
1132
|
initialAutomaticTitle?: string | null;
|
|
1033
1133
|
sessionMcpServers?: SessionMcpServerMetadata[];
|
|
@@ -1060,15 +1160,12 @@ async function finishStartSession(
|
|
|
1060
1160
|
): Promise<{ session: CreateSessionResponse; changed: boolean }> {
|
|
1061
1161
|
// Create-time machine targeting (A-2a): seed the active-sandbox pointer BEFORE
|
|
1062
1162
|
// the atomic initial turn transaction, so the FIRST turn routes to the chosen
|
|
1063
|
-
// machine.
|
|
1163
|
+
// machine. Home backend and active route are independent: a backend:none
|
|
1164
|
+
// session has no managed home but may still attach a valid Connected Machine.
|
|
1165
|
+
// swapActiveSandbox does
|
|
1064
1166
|
// the same ownership+liveness validation as the live swap; an invalid/unowned/
|
|
1065
1167
|
// offline target FAILS the create (422) — never a silent fall-back to the box.
|
|
1066
1168
|
if (input.seedTargetSandbox) {
|
|
1067
|
-
if (session.sandboxBackend === "none") {
|
|
1068
|
-
throw new HTTPException(422, {
|
|
1069
|
-
message: "cannot target a machine for a session with no sandbox (backend: none)",
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
1169
|
const ctx: FleetContext = {
|
|
1073
1170
|
accountId: session.accountId,
|
|
1074
1171
|
workspaceId: session.workspaceId,
|
|
@@ -1567,18 +1664,11 @@ export async function postUserMessageTurn(
|
|
|
1567
1664
|
if (!reference) {
|
|
1568
1665
|
throw new Error("workspace custom model reference disappeared");
|
|
1569
1666
|
}
|
|
1570
|
-
const active =
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
upstreamModelId: reference.upstreamModelId,
|
|
1576
|
-
})
|
|
1577
|
-
: await lockActiveWorkspaceGatewayCustomModelForAdmission(tx, {
|
|
1578
|
-
accountId,
|
|
1579
|
-
workspaceId,
|
|
1580
|
-
upstreamModelId: reference.upstreamModelId,
|
|
1581
|
-
});
|
|
1667
|
+
const active = await lockActiveCustomModelForAdmission(tx, {
|
|
1668
|
+
accountId,
|
|
1669
|
+
workspaceId,
|
|
1670
|
+
reference,
|
|
1671
|
+
});
|
|
1582
1672
|
if (!active) {
|
|
1583
1673
|
throw new HTTPException(422, {
|
|
1584
1674
|
message: `model is not available: ${freshWorkspaceCustomModel}`,
|
|
@@ -1676,9 +1766,7 @@ async function resolveWorkspaceModelBoundarySettings(
|
|
|
1676
1766
|
modelIds: readonly (string | null | undefined)[],
|
|
1677
1767
|
retainedProductModelId?: string | null,
|
|
1678
1768
|
): Promise<Settings> {
|
|
1679
|
-
const
|
|
1680
|
-
retainedProductModelId?.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX) === true ||
|
|
1681
|
-
retainedProductModelId?.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX) === true;
|
|
1769
|
+
const retainedCatalogModel = isCatalogOverlayModel(retainedProductModelId);
|
|
1682
1770
|
if (deps.catalogSourceSettings) {
|
|
1683
1771
|
// The adapter already resolved one exact workspace catalog snapshot for
|
|
1684
1772
|
// this request. Preserve it for fresh selections, but an existing session
|
|
@@ -1686,16 +1774,10 @@ async function resolveWorkspaceModelBoundarySettings(
|
|
|
1686
1774
|
// intentionally omitted. Re-open only the unoverlaid source for that
|
|
1687
1775
|
// retention lookup; never feed the synthetic workspace provider back
|
|
1688
1776
|
// through deployment validation.
|
|
1689
|
-
if (!
|
|
1777
|
+
if (!retainedCatalogModel) return deps.settings;
|
|
1690
1778
|
}
|
|
1691
|
-
const workspaceModelIds = modelIds.filter(
|
|
1692
|
-
(modelId): modelId is string =>
|
|
1693
|
-
typeof modelId === "string" &&
|
|
1694
|
-
(modelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX) ||
|
|
1695
|
-
modelId.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX)),
|
|
1696
|
-
);
|
|
1697
1779
|
const needsWorkspaceResolution =
|
|
1698
|
-
deps.settings.modelCatalogSource === "database" ||
|
|
1780
|
+
deps.settings.modelCatalogSource === "database" || modelIds.some(isCatalogOverlayModel);
|
|
1699
1781
|
if (!needsWorkspaceResolution) return deps.settings;
|
|
1700
1782
|
return (
|
|
1701
1783
|
await resolveWorkspaceCatalogSettings(deps.db, deps.catalogSourceSettings ?? deps.settings, {
|
|
@@ -1859,6 +1941,7 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1859
1941
|
? { activeManagedHumanSubjectId: replayManagedHumanSubjectId }
|
|
1860
1942
|
: {}),
|
|
1861
1943
|
createIdempotencyKey: payload.idempotencyKey,
|
|
1944
|
+
selectedInstalledSkillIds: payload.installedSkillIds ?? [],
|
|
1862
1945
|
...(payload.requestedSessionId ? { requestedSessionId: payload.requestedSessionId } : {}),
|
|
1863
1946
|
visibility: effectiveVisibility,
|
|
1864
1947
|
variableSetIds: payload.variableSetIds ?? [],
|
|
@@ -2021,9 +2104,43 @@ export async function createSessionForRequestWithOutcome(
|
|
|
2021
2104
|
? payload.resources
|
|
2022
2105
|
: (parentSession?.resources ?? payload.resources),
|
|
2023
2106
|
);
|
|
2024
|
-
const
|
|
2107
|
+
const inheritedOrSubmittedSkills = hasOwnProperty(rawPayload, "skills")
|
|
2025
2108
|
? payload.skills
|
|
2026
2109
|
: (parentSession?.skills ?? payload.skills);
|
|
2110
|
+
const selectedInstalledSkillIds = payload.installedSkillIds ?? [];
|
|
2111
|
+
const selectedInstalledSkills: SessionSkill[] = [];
|
|
2112
|
+
if (selectedInstalledSkillIds.length > 0) {
|
|
2113
|
+
const installedSkills = await listInstalledPortableSkills(db, workspaceId, {
|
|
2114
|
+
includeSessionSelected: true,
|
|
2115
|
+
});
|
|
2116
|
+
const installedById = new Map(installedSkills.map((skill) => [skill.capabilityId, skill]));
|
|
2117
|
+
for (const capabilityId of selectedInstalledSkillIds) {
|
|
2118
|
+
const installed = installedById.get(capabilityId);
|
|
2119
|
+
if (!installed) {
|
|
2120
|
+
throw new HTTPException(422, {
|
|
2121
|
+
message: `Session-selected Skill is not installed in this workspace: ${capabilityId}`,
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
if (installed.activationMode !== "session_selected") {
|
|
2125
|
+
throw new HTTPException(422, {
|
|
2126
|
+
message: `Installed Skill does not require explicit session selection: ${capabilityId}`,
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
selectedInstalledSkills.push({
|
|
2130
|
+
name: installed.name,
|
|
2131
|
+
description: installed.description,
|
|
2132
|
+
files: installed.files.map((file) => ({ path: file.path, content: file.content })),
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
let skills: SessionSkill[];
|
|
2137
|
+
try {
|
|
2138
|
+
skills = SessionSkills.parse([...inheritedOrSubmittedSkills, ...selectedInstalledSkills]);
|
|
2139
|
+
} catch (error) {
|
|
2140
|
+
throw new HTTPException(422, {
|
|
2141
|
+
message: error instanceof Error ? error.message : "invalid session Skill selection",
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2027
2144
|
const toolsProvided = hasOwnProperty(rawPayload, "tools");
|
|
2028
2145
|
// Visibility became durable draft state after older clients had already
|
|
2029
2146
|
// written rows without it. Compare it only when the create request supplied
|
|
@@ -2322,7 +2439,10 @@ export async function createSessionForRequestWithOutcome(
|
|
|
2322
2439
|
payload.firstPartyMcpTools,
|
|
2323
2440
|
parentSession ? parentSession.firstPartyMcpTools : undefined,
|
|
2324
2441
|
workspaceFirstPartyDefaults && !parentSession
|
|
2325
|
-
? {
|
|
2442
|
+
? {
|
|
2443
|
+
...deploymentFirstPartyMcpToolPolicy,
|
|
2444
|
+
default: workspaceFirstPartyDefaults,
|
|
2445
|
+
}
|
|
2326
2446
|
: deploymentFirstPartyMcpToolPolicy,
|
|
2327
2447
|
);
|
|
2328
2448
|
const googleDrivePublicationEnabled =
|
|
@@ -2398,7 +2518,10 @@ export async function createSessionForRequestWithOutcome(
|
|
|
2398
2518
|
let sandboxGroupId: string | null = null;
|
|
2399
2519
|
let inheritedBackend: Session["sandboxBackend"] | undefined;
|
|
2400
2520
|
let inheritedSandboxOs: Session["sandboxOs"] | undefined;
|
|
2401
|
-
let inheritedActiveTarget: {
|
|
2521
|
+
let inheritedActiveTarget: {
|
|
2522
|
+
sandboxId: string;
|
|
2523
|
+
workingDir: string | null;
|
|
2524
|
+
} | null = null;
|
|
2402
2525
|
// ENV-AWARE GROUPING: under the CURRENT mechanics the workspace VariableSet is
|
|
2403
2526
|
// creation-time box state — the box's manifest env is fixed when it is cold-
|
|
2404
2527
|
// created, and the SDK's provided-session guard rejects any manifest-env delta
|
|
@@ -2707,6 +2830,7 @@ export async function createSessionForRequestWithOutcome(
|
|
|
2707
2830
|
...(xaiProviderAccountAuthoritySnapshot ? { xaiProviderAccountAuthoritySnapshot } : {}),
|
|
2708
2831
|
parentSessionId,
|
|
2709
2832
|
createIdempotencyKey: payload.idempotencyKey ?? null,
|
|
2833
|
+
selectedInstalledSkillIds,
|
|
2710
2834
|
maxNestedAgentDepthOverride: payload.maxNestedAgentDepth ?? null,
|
|
2711
2835
|
allowNestedAgentDepthIncrease: hasPermission(grant.permissions, "workspace:admin"),
|
|
2712
2836
|
subjectId: grant.subjectId,
|
|
@@ -3158,7 +3282,15 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
3158
3282
|
? { schedulePostCommit: deps.schedulePromptPostCommit }
|
|
3159
3283
|
: {}),
|
|
3160
3284
|
});
|
|
3161
|
-
return {
|
|
3285
|
+
return {
|
|
3286
|
+
accepted,
|
|
3287
|
+
turn,
|
|
3288
|
+
draft,
|
|
3289
|
+
receipt,
|
|
3290
|
+
routing,
|
|
3291
|
+
interruptionCount,
|
|
3292
|
+
replay,
|
|
3293
|
+
};
|
|
3162
3294
|
} catch (error) {
|
|
3163
3295
|
if (input.clientEventId && boundaryRequestHash) {
|
|
3164
3296
|
const replay = await withWorkspaceSubjectSessionActivityRls(
|
package/src/index.ts
CHANGED
|
@@ -69,12 +69,14 @@ export * from "./billing/limits";
|
|
|
69
69
|
// postUserMessageTurn, createAndStartSession, capability/pack/environment/
|
|
70
70
|
// scheduled-task/workspace-member logic, …).
|
|
71
71
|
export * from "./domain/capabilities";
|
|
72
|
+
export * from "./domain/host-mcp-authority-source-admission";
|
|
72
73
|
export * from "./domain/skill-imports";
|
|
73
74
|
export * from "./domain/environments";
|
|
74
75
|
export * from "./rigs";
|
|
75
76
|
export * from "./domain/packs";
|
|
76
77
|
export * from "./domain/automations";
|
|
77
78
|
export * from "./domain/pr-review";
|
|
79
|
+
export * from "./domain/product-integration-pack";
|
|
78
80
|
export * from "./domain/personal-connection-delegations";
|
|
79
81
|
export * from "./domain/resources";
|
|
80
82
|
export * from "./domain/github-repository-bindings";
|