@opengeni/api-router 0.12.7 → 0.14.3
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/app.d.ts +18 -38
- package/dist/app.js +3 -1
- package/dist/auth/managed-auth.d.ts +35 -0
- package/dist/{chunk-SD5GJTZ4.js → chunk-36PW33ND.js} +3744 -638
- package/dist/chunk-36PW33ND.js.map +1 -0
- package/dist/codex-redemption-security.d.ts +15 -0
- package/dist/github-access.d.ts +12 -0
- package/dist/github-browser-flow.d.ts +14 -0
- package/dist/http/auth.d.ts +3 -0
- package/dist/http/common.d.ts +3 -0
- package/dist/http/sse.d.ts +70 -0
- package/dist/index.d.ts +11 -13
- package/dist/index.js +1 -1
- package/dist/integrations/google-drive.d.ts +76 -0
- package/dist/integrations/oauth-client.d.ts +42 -0
- package/dist/integrations/provider-domain.d.ts +7 -0
- package/dist/integrations/slack-bot.d.ts +343 -0
- package/dist/mcp/documents.d.ts +8 -0
- package/dist/mcp/files.d.ts +5 -0
- package/dist/mcp/server.d.ts +107 -0
- package/dist/mcp/session-view.d.ts +191 -0
- package/dist/mcp/toolspace.d.ts +61 -0
- package/dist/model-catalog.d.ts +39 -0
- package/dist/observability.d.ts +3 -0
- package/dist/routes/api-keys.d.ts +3 -0
- package/dist/routes/billing.d.ts +16 -0
- package/dist/routes/capabilities.d.ts +3 -0
- package/dist/routes/catalog-assets.d.ts +5 -0
- package/dist/routes/codex.d.ts +10 -0
- package/dist/routes/connections.d.ts +3 -0
- package/dist/routes/documents.d.ts +3 -0
- package/dist/routes/enrollments.d.ts +3 -0
- package/dist/routes/environments.d.ts +5 -0
- package/dist/routes/files.d.ts +4 -0
- package/dist/routes/github.d.ts +3 -0
- package/dist/routes/insights.d.ts +3 -0
- package/dist/routes/install.d.ts +5 -0
- package/dist/routes/machines.d.ts +3 -0
- package/dist/routes/packs.d.ts +3 -0
- package/dist/routes/preference-registry.d.ts +5 -0
- package/dist/routes/rigs.d.ts +3 -0
- package/dist/routes/scheduled-tasks.d.ts +3 -0
- package/dist/routes/sessions.d.ts +15 -0
- package/dist/routes/social.d.ts +3 -0
- package/dist/routes/transcriptions.d.ts +3 -0
- package/dist/routes/workspace-capture.d.ts +59 -0
- package/dist/routes/workspace-instruction-policies.d.ts +3 -0
- package/dist/routes/workspace-state.d.ts +3 -0
- package/dist/routes/workspaces.d.ts +5 -0
- package/dist/sandbox/access.d.ts +21 -0
- package/dist/sandbox/auth-callout.d.ts +30 -0
- package/dist/sandbox/channel-a.d.ts +37 -0
- package/dist/sandbox/enrollment.d.ts +120 -0
- package/dist/sandbox/machines.d.ts +29 -0
- package/dist/sandbox/metrics-ingestion.d.ts +128 -0
- package/dist/sandbox/rematerialize.d.ts +24 -0
- package/dist/sandbox/viewer.d.ts +251 -0
- package/dist/transcription/providers/azure-openai.d.ts +9 -0
- package/dist/transcription/providers/codex-subscription.d.ts +9 -0
- package/dist/transcription/providers/openai.d.ts +9 -0
- package/dist/transcription/service.d.ts +10 -0
- package/dist/workspace-state-projection.d.ts +16 -0
- package/package.json +13 -13
- package/src/app.ts +75 -5
- package/src/github-access.ts +1 -0
- package/src/integrations/google-drive.ts +869 -0
- package/src/integrations/oauth-client.ts +41 -6
- package/src/integrations/slack-bot.ts +756 -26
- package/src/mcp/server.ts +226 -42
- package/src/mcp/session-view.ts +1 -0
- package/src/mcp/toolspace.ts +17 -9
- package/src/routes/connections.ts +137 -7
- package/src/routes/documents.ts +13 -2
- package/src/routes/github.ts +327 -20
- package/src/routes/insights.ts +30 -0
- package/src/routes/preference-registry.ts +482 -0
- package/src/routes/sessions.ts +22 -23
- package/src/routes/transcriptions.ts +155 -0
- package/src/routes/workspace-capture.ts +108 -8
- package/src/routes/workspace-state.ts +61 -0
- package/src/sandbox/channel-a.ts +2 -0
- package/src/sandbox/rematerialize.ts +111 -4
- package/src/sandbox/viewer.ts +29 -12
- package/src/transcription/providers/azure-openai.ts +45 -0
- package/src/transcription/providers/codex-subscription.ts +100 -0
- package/src/transcription/providers/openai.ts +93 -0
- package/src/transcription/service.ts +121 -0
- package/src/workspace-state-projection.ts +244 -0
- package/dist/chunk-SD5GJTZ4.js.map +0 -1
package/src/mcp/server.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
type Permission,
|
|
22
22
|
type ResourceRef,
|
|
23
23
|
type SessionAuthorizationOperation,
|
|
24
|
+
type SessionAuthorizationSurface,
|
|
24
25
|
type Session,
|
|
25
26
|
UpdateScheduledTaskRequest,
|
|
26
27
|
} from "@opengeni/contracts";
|
|
@@ -35,6 +36,8 @@ import {
|
|
|
35
36
|
getSessionGoal,
|
|
36
37
|
getSessionQueueSnapshot,
|
|
37
38
|
getSessionTurn,
|
|
39
|
+
getOrCreatePreferenceRegistrySnapshot,
|
|
40
|
+
getPreferenceRegistryFullContent,
|
|
38
41
|
getVariableSet,
|
|
39
42
|
getVariableSetByName,
|
|
40
43
|
areGitHubRepositoriesAllowedForWorkspace,
|
|
@@ -185,6 +188,8 @@ const FIRST_PARTY_TOOL_AUTHORIZATION = {
|
|
|
185
188
|
memory_search: { sessionRequired: true, allOf: ["documents:search"] },
|
|
186
189
|
memory_save: { sessionRequired: true, allOf: ["documents:search"] },
|
|
187
190
|
memory_correct: { sessionRequired: true, allOf: ["documents:search"] },
|
|
191
|
+
preference_registry_summary: { sessionRequired: true, allOf: ["workspace:read"] },
|
|
192
|
+
preference_registry_get: { sessionRequired: true, allOf: ["workspace:read"] },
|
|
188
193
|
sandboxes_list: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
189
194
|
sandbox_attach: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
190
195
|
sandbox_swap: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
@@ -225,8 +230,13 @@ const FIRST_PARTY_TOOL_AUTHORIZATION = {
|
|
|
225
230
|
scheduled_task_runs_list: { anyOf: ["scheduled_tasks:manage", "scheduled_tasks:run"] },
|
|
226
231
|
slack_bot_list_channels: { allOf: ["connections:read"] },
|
|
227
232
|
slack_bot_channel_history: { allOf: ["connections:read"] },
|
|
233
|
+
slack_bot_thread_replies: { allOf: ["connections:read"] },
|
|
228
234
|
slack_bot_list_users: { allOf: ["connections:read"] },
|
|
235
|
+
slack_bot_list_files: { allOf: ["connections:read"] },
|
|
236
|
+
slack_bot_file_info: { allOf: ["connections:read"] },
|
|
237
|
+
slack_bot_file_content: { allOf: ["connections:read"] },
|
|
229
238
|
slack_bot_post_message: { allOf: ["connections:read"] },
|
|
239
|
+
slack_bot_delete_message: { allOf: ["connections:read"] },
|
|
230
240
|
} satisfies Record<FirstPartyMcpToolName, FirstPartyToolAuthorization>;
|
|
231
241
|
|
|
232
242
|
const FIRST_PARTY_MCP_TOOL_NAME_SET = new Set<string>(FIRST_PARTY_MCP_TOOL_NAMES);
|
|
@@ -365,6 +375,9 @@ export function buildOpenGeniMcpServer(
|
|
|
365
375
|
if (!toolspaceMode && sessionId !== null && options.workspaceMemoryEnabled === true) {
|
|
366
376
|
registerMemoryTools(server, deps, grant, sessionId, json);
|
|
367
377
|
}
|
|
378
|
+
if (!toolspaceMode && sessionId !== null && preferenceAttemptClaims(grant) !== null) {
|
|
379
|
+
registerPreferenceRegistryTools(server, deps, grant, json);
|
|
380
|
+
}
|
|
368
381
|
|
|
369
382
|
// Fleet tools (M7 bring-your-own-compute): list / attach / swap / run_on /
|
|
370
383
|
// provision over the session's Modal box + the workspace's enrolled machines.
|
|
@@ -837,6 +850,32 @@ function registerSlackBotTools(
|
|
|
837
850
|
),
|
|
838
851
|
);
|
|
839
852
|
|
|
853
|
+
server.registerTool(
|
|
854
|
+
"slack_bot_thread_replies",
|
|
855
|
+
{
|
|
856
|
+
description:
|
|
857
|
+
"Read a Slack thread as the workspace-shared OpenGeni bot. Pass the channel ID and the parent message timestamp returned by channel history. The result includes the parent followed by its replies.",
|
|
858
|
+
inputSchema: {
|
|
859
|
+
connectionId: z4.string().uuid().optional(),
|
|
860
|
+
channelId: z4.string().min(1).max(64),
|
|
861
|
+
threadTimestamp: z4.string().min(1).max(64),
|
|
862
|
+
cursor: z4.string().max(1024).optional(),
|
|
863
|
+
limit: z4.number().int().min(1).max(100).optional(),
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
async ({ connectionId, channelId, threadTimestamp, cursor, limit }) =>
|
|
867
|
+
json(
|
|
868
|
+
await (
|
|
869
|
+
await clientFor(connectionId)
|
|
870
|
+
).threadReplies({
|
|
871
|
+
channelId,
|
|
872
|
+
threadTimestamp,
|
|
873
|
+
...(cursor ? { cursor } : {}),
|
|
874
|
+
...(limit !== undefined ? { limit } : {}),
|
|
875
|
+
}),
|
|
876
|
+
),
|
|
877
|
+
);
|
|
878
|
+
|
|
840
879
|
server.registerTool(
|
|
841
880
|
"slack_bot_list_users",
|
|
842
881
|
{
|
|
@@ -858,20 +897,95 @@ function registerSlackBotTools(
|
|
|
858
897
|
),
|
|
859
898
|
);
|
|
860
899
|
|
|
900
|
+
server.registerTool(
|
|
901
|
+
"slack_bot_list_files",
|
|
902
|
+
{
|
|
903
|
+
description:
|
|
904
|
+
"List Slack files and canvases shared with a channel where the workspace-shared OpenGeni bot is already a member.",
|
|
905
|
+
inputSchema: {
|
|
906
|
+
connectionId: z4.string().uuid().optional(),
|
|
907
|
+
channelId: z4.string().min(1).max(64),
|
|
908
|
+
cursor: z4.string().max(1024).optional(),
|
|
909
|
+
limit: z4.number().int().min(1).max(200).optional(),
|
|
910
|
+
},
|
|
911
|
+
},
|
|
912
|
+
async ({ connectionId, channelId, cursor, limit }) =>
|
|
913
|
+
json(
|
|
914
|
+
await (
|
|
915
|
+
await clientFor(connectionId)
|
|
916
|
+
).listFiles({
|
|
917
|
+
channelId,
|
|
918
|
+
...(cursor ? { cursor } : {}),
|
|
919
|
+
...(limit !== undefined ? { limit } : {}),
|
|
920
|
+
}),
|
|
921
|
+
),
|
|
922
|
+
);
|
|
923
|
+
|
|
924
|
+
server.registerTool(
|
|
925
|
+
"slack_bot_file_info",
|
|
926
|
+
{
|
|
927
|
+
description:
|
|
928
|
+
"Read safe metadata for a Slack file or canvas shared with a channel where the workspace-shared OpenGeni bot is already a member. For an embedded huddle transcript, also pass the shared canvas file ID as parentFileId so OpenGeni can verify the indirect share.",
|
|
929
|
+
inputSchema: {
|
|
930
|
+
connectionId: z4.string().uuid().optional(),
|
|
931
|
+
channelId: z4.string().min(1).max(64),
|
|
932
|
+
fileId: z4.string().min(1).max(64),
|
|
933
|
+
parentFileId: z4.string().min(1).max(64).optional(),
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
async ({ connectionId, channelId, fileId, parentFileId }) =>
|
|
937
|
+
json(
|
|
938
|
+
await (
|
|
939
|
+
await clientFor(connectionId)
|
|
940
|
+
).fileInfo({
|
|
941
|
+
channelId,
|
|
942
|
+
fileId,
|
|
943
|
+
...(parentFileId ? { parentFileId } : {}),
|
|
944
|
+
}),
|
|
945
|
+
),
|
|
946
|
+
);
|
|
947
|
+
|
|
948
|
+
server.registerTool(
|
|
949
|
+
"slack_bot_file_content",
|
|
950
|
+
{
|
|
951
|
+
description:
|
|
952
|
+
"Read a bounded page of UTF-8 text from a Slack file or canvas shared with a channel where the workspace-shared OpenGeni bot is already a member. For an embedded huddle transcript, also pass the shared canvas file ID as parentFileId so OpenGeni can verify the channel-to-canvas-to-transcript chain. Slack may still restrict a huddle transcript body to participants; that returns huddle_transcript_requires_participant_access. Private Slack URLs and credentials are never returned. Continue with nextOffset when truncated is true.",
|
|
953
|
+
inputSchema: {
|
|
954
|
+
connectionId: z4.string().uuid().optional(),
|
|
955
|
+
channelId: z4.string().min(1).max(64),
|
|
956
|
+
fileId: z4.string().min(1).max(64),
|
|
957
|
+
parentFileId: z4.string().min(1).max(64).optional(),
|
|
958
|
+
offset: z4.number().int().min(0).max(4_000_000).optional(),
|
|
959
|
+
},
|
|
960
|
+
},
|
|
961
|
+
async ({ connectionId, channelId, fileId, parentFileId, offset }) =>
|
|
962
|
+
json(
|
|
963
|
+
await (
|
|
964
|
+
await clientFor(connectionId)
|
|
965
|
+
).fileContent({
|
|
966
|
+
channelId,
|
|
967
|
+
fileId,
|
|
968
|
+
...(parentFileId ? { parentFileId } : {}),
|
|
969
|
+
...(offset !== undefined ? { offset } : {}),
|
|
970
|
+
}),
|
|
971
|
+
),
|
|
972
|
+
);
|
|
973
|
+
|
|
861
974
|
server.registerTool(
|
|
862
975
|
"slack_bot_post_message",
|
|
863
976
|
{
|
|
864
977
|
description:
|
|
865
|
-
"Post as the workspace-shared OpenGeni bot. Pass channelId for a channel where the bot is already a member, or userId to open/post a DM; pass exactly one. Generate one operationId UUID per intended message and reuse that same UUID on every retry.",
|
|
978
|
+
"Post as the workspace-shared OpenGeni bot. Pass channelId for a channel where the bot is already a member, or userId to open/post a DM; pass exactly one. To reply in a thread, also pass its parent message timestamp as threadTimestamp. Generate one operationId UUID per intended message and reuse that same UUID on every retry.",
|
|
866
979
|
inputSchema: {
|
|
867
980
|
connectionId: z4.string().uuid().optional(),
|
|
868
981
|
operationId: z4.string().uuid(),
|
|
869
982
|
channelId: z4.string().min(1).max(64).optional(),
|
|
870
983
|
userId: z4.string().min(1).max(64).optional(),
|
|
984
|
+
threadTimestamp: z4.string().min(1).max(64).optional(),
|
|
871
985
|
text: z4.string().min(1).max(40_000),
|
|
872
986
|
},
|
|
873
987
|
},
|
|
874
|
-
async ({ connectionId, operationId, channelId, userId, text }) => {
|
|
988
|
+
async ({ connectionId, operationId, channelId, userId, threadTimestamp, text }) => {
|
|
875
989
|
if (Boolean(channelId) === Boolean(userId)) {
|
|
876
990
|
throw new Error("exactly one of channelId or userId is required");
|
|
877
991
|
}
|
|
@@ -882,11 +996,30 @@ function registerSlackBotTools(
|
|
|
882
996
|
operationId,
|
|
883
997
|
...(channelId ? { channelId } : {}),
|
|
884
998
|
...(userId ? { userId } : {}),
|
|
999
|
+
...(threadTimestamp ? { threadTimestamp } : {}),
|
|
885
1000
|
text,
|
|
886
1001
|
}),
|
|
887
1002
|
);
|
|
888
1003
|
},
|
|
889
1004
|
);
|
|
1005
|
+
|
|
1006
|
+
server.registerTool(
|
|
1007
|
+
"slack_bot_delete_message",
|
|
1008
|
+
{
|
|
1009
|
+
description:
|
|
1010
|
+
"Delete a message authored by the workspace-shared OpenGeni bot. Pass the channel ID and exact message timestamp returned by a prior post or channel/thread read. Generate one operationId UUID per intended deletion and reuse it on every retry, including after an unknown outcome. Slack refuses deletion of messages not authored by this bot.",
|
|
1011
|
+
inputSchema: {
|
|
1012
|
+
connectionId: z4.string().uuid().optional(),
|
|
1013
|
+
operationId: z4.string().uuid(),
|
|
1014
|
+
channelId: z4.string().min(1).max(64),
|
|
1015
|
+
timestamp: z4.string().min(1).max(64),
|
|
1016
|
+
},
|
|
1017
|
+
},
|
|
1018
|
+
async ({ connectionId, operationId, channelId, timestamp }) =>
|
|
1019
|
+
json(
|
|
1020
|
+
await (await clientFor(connectionId)).deleteMessage({ operationId, channelId, timestamp }),
|
|
1021
|
+
),
|
|
1022
|
+
);
|
|
890
1023
|
}
|
|
891
1024
|
|
|
892
1025
|
function registerToolspaceProxyTools(server: McpServer, surface: ToolspaceMcpSurface | null): void {
|
|
@@ -1149,6 +1282,70 @@ async function authorizeFirstPartySession(
|
|
|
1149
1282
|
});
|
|
1150
1283
|
}
|
|
1151
1284
|
|
|
1285
|
+
function preferenceAttemptClaims(grant: AccessGrant): {
|
|
1286
|
+
sessionId: string;
|
|
1287
|
+
turnId: string;
|
|
1288
|
+
attemptId: string;
|
|
1289
|
+
executionGeneration: number;
|
|
1290
|
+
} | null {
|
|
1291
|
+
const metadata = grant.metadata ?? {};
|
|
1292
|
+
if (
|
|
1293
|
+
typeof metadata["sessionId"] !== "string" ||
|
|
1294
|
+
typeof metadata["turnId"] !== "string" ||
|
|
1295
|
+
typeof metadata["attemptId"] !== "string" ||
|
|
1296
|
+
typeof metadata["executionGeneration"] !== "number" ||
|
|
1297
|
+
!Number.isSafeInteger(metadata["executionGeneration"]) ||
|
|
1298
|
+
metadata["executionGeneration"] < 1 ||
|
|
1299
|
+
metadata["executionGeneration"] > 2_147_483_647
|
|
1300
|
+
) {
|
|
1301
|
+
return null;
|
|
1302
|
+
}
|
|
1303
|
+
return {
|
|
1304
|
+
sessionId: metadata["sessionId"],
|
|
1305
|
+
turnId: metadata["turnId"],
|
|
1306
|
+
attemptId: metadata["attemptId"],
|
|
1307
|
+
executionGeneration: metadata["executionGeneration"],
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
function registerPreferenceRegistryTools(
|
|
1312
|
+
server: McpServer,
|
|
1313
|
+
deps: ApiRouteDeps,
|
|
1314
|
+
grant: AccessGrant,
|
|
1315
|
+
json: JsonResult,
|
|
1316
|
+
): void {
|
|
1317
|
+
const attemptClaims = () => {
|
|
1318
|
+
const resolved = preferenceAttemptClaims(grant);
|
|
1319
|
+
if (!resolved) throw new Error("Exact signed preference attempt authority is required.");
|
|
1320
|
+
return {
|
|
1321
|
+
accountId: grant.accountId,
|
|
1322
|
+
workspaceId: grant.workspaceId,
|
|
1323
|
+
...resolved,
|
|
1324
|
+
};
|
|
1325
|
+
};
|
|
1326
|
+
|
|
1327
|
+
server.registerTool(
|
|
1328
|
+
"preference_registry_summary",
|
|
1329
|
+
{
|
|
1330
|
+
description:
|
|
1331
|
+
"List bounded deterministic descriptors for organization, workspace, and immutable initiating-human preferences frozen to this exact attempt. Full content is omitted; retrieve only a relevant returned handle.",
|
|
1332
|
+
inputSchema: {},
|
|
1333
|
+
},
|
|
1334
|
+
async () => json(await getOrCreatePreferenceRegistrySnapshot(deps.db, attemptClaims())),
|
|
1335
|
+
);
|
|
1336
|
+
|
|
1337
|
+
server.registerTool(
|
|
1338
|
+
"preference_registry_get",
|
|
1339
|
+
{
|
|
1340
|
+
description:
|
|
1341
|
+
"Retrieve full content for one preference in this exact attempt snapshot. Handles from another account, workspace, human, or attempt are rejected.",
|
|
1342
|
+
inputSchema: { retrievalHandle: z4.string().min(1).max(512) },
|
|
1343
|
+
},
|
|
1344
|
+
async ({ retrievalHandle }) =>
|
|
1345
|
+
json(await getPreferenceRegistryFullContent(deps.db, attemptClaims(), retrievalHandle)),
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1152
1349
|
const MemoryKindSchema = z4.enum(["preference", "semantic", "procedural", "decision", "episodic"]);
|
|
1153
1350
|
|
|
1154
1351
|
function memoryPreview(text: string): string {
|
|
@@ -1348,7 +1545,7 @@ function registerFleetTools(
|
|
|
1348
1545
|
"run_on",
|
|
1349
1546
|
{
|
|
1350
1547
|
description:
|
|
1351
|
-
"Run a ONE-OFF op on a SPECIFIC enrolled selfhosted machine WITHOUT changing this session's active sandbox (a side-channel to another machine). Ops: exec (run a command), read (read a file), write (write a file). `target` = a selfhosted sandboxes_list `id`. To make a machine the active sandbox instead, use sandbox_swap.",
|
|
1548
|
+
"Run a ONE-OFF op on a SPECIFIC enrolled selfhosted machine WITHOUT changing this session's active sandbox (a side-channel to another machine). Ops: exec (run a command), read (read a file), write (write a file). Exec returns exact exitCode plus typed timedOut and the effective deadlineMs; a deadline kill or missing exit proof is never ok:true, and an ambiguous transport loss is not replayed. `target` = a selfhosted sandboxes_list `id`. To make a machine the active sandbox instead, use sandbox_swap.",
|
|
1352
1549
|
inputSchema: {
|
|
1353
1550
|
target: z4.string().min(1),
|
|
1354
1551
|
op: z4.discriminatedUnion("kind", [
|
|
@@ -1567,6 +1764,7 @@ function registerRigTools(
|
|
|
1567
1764
|
function exactAgentCommandContext(
|
|
1568
1765
|
grant: AccessGrant,
|
|
1569
1766
|
callerSessionId: string,
|
|
1767
|
+
authorizationSurface?: SessionAuthorizationSurface,
|
|
1570
1768
|
): AgentSessionCommandContext {
|
|
1571
1769
|
const turnId = grant.metadata?.["turnId"];
|
|
1572
1770
|
const attemptId = grant.metadata?.["attemptId"];
|
|
@@ -1588,6 +1786,7 @@ function exactAgentCommandContext(
|
|
|
1588
1786
|
callerTurnId: turnId,
|
|
1589
1787
|
callerAttemptId: attemptId,
|
|
1590
1788
|
callerExecutionGeneration: executionGeneration,
|
|
1789
|
+
...(authorizationSurface ? { authorizationSurface } : {}),
|
|
1591
1790
|
};
|
|
1592
1791
|
}
|
|
1593
1792
|
|
|
@@ -1822,6 +2021,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
1822
2021
|
rigId: z4.string().uuid().optional(),
|
|
1823
2022
|
model: z4.string().min(1).optional(),
|
|
1824
2023
|
reasoningEffort: z4.string().optional(),
|
|
2024
|
+
latencyMode: z4.enum(["standard", "priority", "fast"]).optional(),
|
|
1825
2025
|
sandboxBackend: z4.string().optional(),
|
|
1826
2026
|
// Create-time machine targeting: an enrolled sandbox id (from
|
|
1827
2027
|
// sandboxes_list) to run the spawned session on. Seeds the active-sandbox
|
|
@@ -1953,7 +2153,6 @@ function registerWorkspaceOrchestrationTools(
|
|
|
1953
2153
|
targetSessionId,
|
|
1954
2154
|
{
|
|
1955
2155
|
text,
|
|
1956
|
-
toolsProvided: false,
|
|
1957
2156
|
delivery: "send",
|
|
1958
2157
|
origin: "operator",
|
|
1959
2158
|
clientEventId: idempotencyKey,
|
|
@@ -1977,16 +2176,10 @@ function registerWorkspaceOrchestrationTools(
|
|
|
1977
2176
|
},
|
|
1978
2177
|
},
|
|
1979
2178
|
async ({ sessionId, idempotencyKey, reason }) => {
|
|
1980
|
-
const authorization = await authorizeFirstPartySession(
|
|
1981
|
-
deps,
|
|
1982
|
-
grant,
|
|
1983
|
-
sessionId,
|
|
1984
|
-
"session.control",
|
|
1985
|
-
);
|
|
1986
2179
|
if (callerSessionId !== null) {
|
|
1987
2180
|
const controlled = await controlAgentSessionWorkstream(
|
|
1988
2181
|
deps,
|
|
1989
|
-
exactAgentCommandContext(grant, callerSessionId),
|
|
2182
|
+
exactAgentCommandContext(grant, callerSessionId, "first_party_mcp"),
|
|
1990
2183
|
{
|
|
1991
2184
|
targetSessionId: sessionId,
|
|
1992
2185
|
action: "pause",
|
|
@@ -1999,7 +2192,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
1999
2192
|
effectiveControl: projectEffectiveControlForRelatedAccess(
|
|
2000
2193
|
serializeEffectiveSessionControl(controlled.control),
|
|
2001
2194
|
sessionId,
|
|
2002
|
-
authorization?.relatedSessionAccess ?? "root",
|
|
2195
|
+
controlled.authorization?.relatedSessionAccess ?? "root",
|
|
2003
2196
|
),
|
|
2004
2197
|
interruptionCount: controlled.interruptionCount,
|
|
2005
2198
|
replay: controlled.replay,
|
|
@@ -2012,6 +2205,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
2012
2205
|
workspaceId: grant.workspaceId,
|
|
2013
2206
|
sessionId,
|
|
2014
2207
|
subjectId: grant.subjectId,
|
|
2208
|
+
authorizationSurface: "first_party_mcp",
|
|
2015
2209
|
},
|
|
2016
2210
|
{
|
|
2017
2211
|
action: "pause",
|
|
@@ -2019,14 +2213,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
2019
2213
|
...(reason ? { reason } : {}),
|
|
2020
2214
|
},
|
|
2021
2215
|
);
|
|
2022
|
-
return json(
|
|
2023
|
-
...controlled,
|
|
2024
|
-
effectiveControl: projectEffectiveControlForRelatedAccess(
|
|
2025
|
-
controlled.effectiveControl,
|
|
2026
|
-
sessionId,
|
|
2027
|
-
authorization?.relatedSessionAccess ?? "root",
|
|
2028
|
-
),
|
|
2029
|
-
});
|
|
2216
|
+
return json(controlled);
|
|
2030
2217
|
},
|
|
2031
2218
|
);
|
|
2032
2219
|
|
|
@@ -2042,16 +2229,10 @@ function registerWorkspaceOrchestrationTools(
|
|
|
2042
2229
|
},
|
|
2043
2230
|
},
|
|
2044
2231
|
async ({ sessionId, idempotencyKey, reason }) => {
|
|
2045
|
-
const authorization = await authorizeFirstPartySession(
|
|
2046
|
-
deps,
|
|
2047
|
-
grant,
|
|
2048
|
-
sessionId,
|
|
2049
|
-
"session.control",
|
|
2050
|
-
);
|
|
2051
2232
|
if (callerSessionId !== null) {
|
|
2052
2233
|
const controlled = await controlAgentSessionWorkstream(
|
|
2053
2234
|
deps,
|
|
2054
|
-
exactAgentCommandContext(grant, callerSessionId),
|
|
2235
|
+
exactAgentCommandContext(grant, callerSessionId, "first_party_mcp"),
|
|
2055
2236
|
{
|
|
2056
2237
|
targetSessionId: sessionId,
|
|
2057
2238
|
action: "resume",
|
|
@@ -2064,7 +2245,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
2064
2245
|
effectiveControl: projectEffectiveControlForRelatedAccess(
|
|
2065
2246
|
serializeEffectiveSessionControl(controlled.control),
|
|
2066
2247
|
sessionId,
|
|
2067
|
-
authorization?.relatedSessionAccess ?? "root",
|
|
2248
|
+
controlled.authorization?.relatedSessionAccess ?? "root",
|
|
2068
2249
|
),
|
|
2069
2250
|
interruptionCount: controlled.interruptionCount,
|
|
2070
2251
|
replay: controlled.replay,
|
|
@@ -2077,6 +2258,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
2077
2258
|
workspaceId: grant.workspaceId,
|
|
2078
2259
|
sessionId,
|
|
2079
2260
|
subjectId: grant.subjectId,
|
|
2261
|
+
authorizationSurface: "first_party_mcp",
|
|
2080
2262
|
},
|
|
2081
2263
|
{
|
|
2082
2264
|
action: "resume",
|
|
@@ -2084,14 +2266,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
2084
2266
|
...(reason ? { reason } : {}),
|
|
2085
2267
|
},
|
|
2086
2268
|
);
|
|
2087
|
-
return json(
|
|
2088
|
-
...controlled,
|
|
2089
|
-
effectiveControl: projectEffectiveControlForRelatedAccess(
|
|
2090
|
-
controlled.effectiveControl,
|
|
2091
|
-
sessionId,
|
|
2092
|
-
authorization?.relatedSessionAccess ?? "root",
|
|
2093
|
-
),
|
|
2094
|
-
});
|
|
2269
|
+
return json(controlled);
|
|
2095
2270
|
},
|
|
2096
2271
|
);
|
|
2097
2272
|
|
|
@@ -2108,10 +2283,9 @@ function registerWorkspaceOrchestrationTools(
|
|
|
2108
2283
|
},
|
|
2109
2284
|
},
|
|
2110
2285
|
async ({ sessionId, instruction, idempotencyKey }) => {
|
|
2111
|
-
await authorizeFirstPartySession(deps, grant, sessionId, "session.steer");
|
|
2112
2286
|
const result = await steerAgentSession(
|
|
2113
2287
|
deps,
|
|
2114
|
-
exactAgentCommandContext(grant, callerSessionId),
|
|
2288
|
+
exactAgentCommandContext(grant, callerSessionId, "first_party_mcp"),
|
|
2115
2289
|
{ targetSessionId: sessionId, instruction, idempotencyKey },
|
|
2116
2290
|
);
|
|
2117
2291
|
return json({
|
|
@@ -2317,14 +2491,16 @@ function registerGitHubConnectTool(
|
|
|
2317
2491
|
const { settings } = deps;
|
|
2318
2492
|
const missing = githubAppMissingSettings(settings);
|
|
2319
2493
|
const slug = settings.githubAppSlug?.trim() || null;
|
|
2494
|
+
const setupMode = settings.productAccessMode === "managed" ? "platform" : "operator";
|
|
2320
2495
|
if (missing.length > 0 || !slug) {
|
|
2321
2496
|
return json({
|
|
2322
2497
|
configured: false,
|
|
2323
2498
|
status: "disabled",
|
|
2324
|
-
|
|
2499
|
+
setupMode,
|
|
2500
|
+
appSlug: setupMode === "operator" ? slug : null,
|
|
2325
2501
|
installUrl: null,
|
|
2326
2502
|
linkUrl: null,
|
|
2327
|
-
missing,
|
|
2503
|
+
missing: setupMode === "operator" ? missing : [],
|
|
2328
2504
|
});
|
|
2329
2505
|
}
|
|
2330
2506
|
const installations = await listWorkspaceGitHubInstallationBindings(deps, grant.workspaceId);
|
|
@@ -2342,13 +2518,21 @@ function registerGitHubConnectTool(
|
|
|
2342
2518
|
const connectUrl = state
|
|
2343
2519
|
? `${baseUrl}/v1/workspaces/${grant.workspaceId}/github/connect?state=${encodeURIComponent(state)}`
|
|
2344
2520
|
: null;
|
|
2521
|
+
const installationViews = installations.map((installation) => ({
|
|
2522
|
+
...installation,
|
|
2523
|
+
configureUrl:
|
|
2524
|
+
state && baseUrl
|
|
2525
|
+
? `${baseUrl}/v1/workspaces/${grant.workspaceId}/github/installations/${installation.installationId}/configure?state=${encodeURIComponent(state)}`
|
|
2526
|
+
: null,
|
|
2527
|
+
}));
|
|
2345
2528
|
return json({
|
|
2346
2529
|
configured: true,
|
|
2347
2530
|
status,
|
|
2348
|
-
|
|
2531
|
+
setupMode,
|
|
2532
|
+
appSlug: setupMode === "operator" ? slug : null,
|
|
2349
2533
|
installUrl: connectUrl,
|
|
2350
2534
|
linkUrl: connectUrl,
|
|
2351
|
-
installations,
|
|
2535
|
+
installations: installationViews,
|
|
2352
2536
|
missing: [],
|
|
2353
2537
|
});
|
|
2354
2538
|
},
|
package/src/mcp/session-view.ts
CHANGED
|
@@ -564,6 +564,7 @@ export function boundSessionDetailMcp(
|
|
|
564
564
|
lastSequence: session.lastSequence,
|
|
565
565
|
codexPinnedCredentialId: session.codexPinnedCredentialId,
|
|
566
566
|
codexLastCredentialId: session.codexLastCredentialId,
|
|
567
|
+
codexCompactionMode: session.codexCompactionMode,
|
|
567
568
|
pinned: session.pinned,
|
|
568
569
|
pinnedAt: session.pinnedAt,
|
|
569
570
|
pinVersion: session.pinVersion,
|
package/src/mcp/toolspace.ts
CHANGED
|
@@ -76,9 +76,12 @@ type McpTool = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
const APPROVAL_REQUIRED_MESSAGE = "requires approval - invoke via the agent";
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
const TOOLSPACE_AUTH_NEEDED_ERROR = {
|
|
80
|
+
// OpenGeni application-defined JSON-RPC code. Keep this positive so it cannot
|
|
81
|
+
// collide with MCP SDK transport errors such as RequestTimeout (-32001).
|
|
82
|
+
code: 40_101,
|
|
83
|
+
message: "Authentication required - a connection link was posted to the session.",
|
|
84
|
+
} as const;
|
|
82
85
|
const TOOLSPACE_NO_ACTIVE_TURN_MESSAGE =
|
|
83
86
|
"no active turn - toolspace calls require an in-flight turn";
|
|
84
87
|
// First-party OpenGeni MCP proxies (files/docs) route back through the same
|
|
@@ -721,7 +724,7 @@ async function callRemoteTool(
|
|
|
721
724
|
return mcpError("upstream tool result exceeded the safety limit");
|
|
722
725
|
}
|
|
723
726
|
if (isToolspaceAuthNeededError(error)) {
|
|
724
|
-
return mcpError(
|
|
727
|
+
return mcpError(TOOLSPACE_AUTH_NEEDED_ERROR.message);
|
|
725
728
|
}
|
|
726
729
|
// The raw upstream error can carry provider-specific detail; log it
|
|
727
730
|
// server-side and return only a generic result to the sandbox so no header
|
|
@@ -1037,8 +1040,8 @@ async function authNeededFetchResponse(
|
|
|
1037
1040
|
jsonrpc: "2.0",
|
|
1038
1041
|
id: request.id ?? null,
|
|
1039
1042
|
error: {
|
|
1040
|
-
code:
|
|
1041
|
-
message:
|
|
1043
|
+
code: TOOLSPACE_AUTH_NEEDED_ERROR.code,
|
|
1044
|
+
message: TOOLSPACE_AUTH_NEEDED_ERROR.message,
|
|
1042
1045
|
},
|
|
1043
1046
|
}),
|
|
1044
1047
|
{
|
|
@@ -1110,9 +1113,14 @@ function fetchInputForAttempt(input: string | URL | Request): string | URL | Req
|
|
|
1110
1113
|
}
|
|
1111
1114
|
|
|
1112
1115
|
function isToolspaceAuthNeededError(error: unknown): boolean {
|
|
1116
|
+
if (!(error instanceof Error)) {
|
|
1117
|
+
return false;
|
|
1118
|
+
}
|
|
1119
|
+
const code = (error as { code?: unknown }).code;
|
|
1113
1120
|
return (
|
|
1114
|
-
|
|
1115
|
-
(
|
|
1116
|
-
error.message
|
|
1121
|
+
code === TOOLSPACE_AUTH_NEEDED_ERROR.code &&
|
|
1122
|
+
(error.message === TOOLSPACE_AUTH_NEEDED_ERROR.message ||
|
|
1123
|
+
error.message ===
|
|
1124
|
+
`MCP error ${TOOLSPACE_AUTH_NEEDED_ERROR.code}: ${TOOLSPACE_AUTH_NEEDED_ERROR.message}`)
|
|
1117
1125
|
);
|
|
1118
1126
|
}
|