@opengeni/api-router 2.10.0-canary.0 → 2.10.0-canary.2
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.js +1 -1
- package/dist/{chunk-XXH7DW34.js → chunk-BVX5ASFY.js} +1559 -798
- package/dist/chunk-BVX5ASFY.js.map +1 -0
- package/dist/codemode.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/command-read.d.ts +24 -0
- package/dist/mcp/scheduled-task-view.d.ts +3 -3
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/session-event-view.d.ts +86 -0
- package/dist/sandbox/channel-a.d.ts +2 -0
- package/dist/sandbox/rematerialize.d.ts +2 -0
- package/dist/sandbox/viewer.d.ts +2 -0
- package/dist/session-event-view-MU3HMNV3.js +264 -0
- package/dist/session-event-view-MU3HMNV3.js.map +1 -0
- package/dist/site-uploads.d.ts +41 -0
- package/dist/workspace-artifact-content.d.ts +4 -4
- package/package.json +19 -19
- package/src/app.ts +20 -0
- package/src/codemode.ts +52 -0
- package/src/integrations/oauth-client.ts +51 -7
- package/src/mcp/command-read.ts +109 -0
- package/src/mcp/server.ts +287 -153
- package/src/mcp/session-event-view.ts +336 -0
- package/src/mcp/session-wait.ts +1 -1
- package/src/routes/github.ts +124 -1
- package/src/routes/scheduled-tasks.ts +19 -1
- package/src/routes/sessions.ts +150 -7
- package/src/routes/workspace-artifacts.ts +92 -21
- package/src/sandbox/channel-a.ts +5 -0
- package/src/sandbox/rematerialize.ts +46 -3
- package/src/sandbox/viewer.ts +3 -0
- package/src/site-uploads.ts +202 -0
- package/src/workspace-artifact-content.ts +20 -39
- package/dist/chunk-XXH7DW34.js.map +0 -1
package/src/mcp/server.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
prepareWorkspaceArtifactUpload,
|
|
4
|
+
prepareWorkspaceArtifactPublication,
|
|
5
|
+
workspaceArtifactDownloads,
|
|
6
|
+
} from "../site-uploads";
|
|
2
7
|
import {
|
|
3
8
|
CreateScheduledTaskRequest,
|
|
4
9
|
boundSessionMcpText as capSessionDiscoveryText,
|
|
@@ -169,7 +174,7 @@ import {
|
|
|
169
174
|
searchCapabilityCatalogItems,
|
|
170
175
|
type ResolvedSessionAuthorization,
|
|
171
176
|
} from "@opengeni/core";
|
|
172
|
-
import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
|
|
177
|
+
import { recordWorkspaceUsage, requireLimit, workflowIdForSession } from "@opengeni/core";
|
|
173
178
|
import type { ApiRouteDeps } from "@opengeni/core";
|
|
174
179
|
import {
|
|
175
180
|
githubBindingStatus,
|
|
@@ -245,7 +250,15 @@ import {
|
|
|
245
250
|
type FleetServices,
|
|
246
251
|
type RunOnOp,
|
|
247
252
|
} from "@opengeni/core";
|
|
248
|
-
import {
|
|
253
|
+
import {
|
|
254
|
+
readSessionBackgroundCommandOutput,
|
|
255
|
+
COMMAND_OUTPUT_MAX_BYTES,
|
|
256
|
+
} from "@opengeni/db/session-background-commands";
|
|
257
|
+
import {
|
|
258
|
+
readCommandWithWait,
|
|
259
|
+
COMMAND_READ_MAX_WAIT_SECONDS,
|
|
260
|
+
COMMAND_WAIT_DEFAULT_SECONDS,
|
|
261
|
+
} from "./command-read";
|
|
249
262
|
import {
|
|
250
263
|
boundSessionEventCompactResult,
|
|
251
264
|
boundSessionEventMcpPage,
|
|
@@ -293,10 +306,6 @@ import { registerRememberTools } from "./remember";
|
|
|
293
306
|
import { mintSandboxCodemodeToken } from "@opengeni/runtime/sandbox";
|
|
294
307
|
import { deleteScheduledTaskWithDurableCleanup } from "../scheduled-task-deletion";
|
|
295
308
|
import { observeWorkDiscovery, summarizeWorkDiscoveryRows } from "../work-discovery-observability";
|
|
296
|
-
import {
|
|
297
|
-
prepareWorkspaceArtifactContent,
|
|
298
|
-
readWorkspaceArtifactContent,
|
|
299
|
-
} from "../workspace-artifact-content";
|
|
300
309
|
|
|
301
310
|
export type McpServerOptions = {
|
|
302
311
|
// Origin of the HTTP request that reached the MCP route. Browser-oriented
|
|
@@ -447,6 +456,7 @@ const FIRST_PARTY_TOOL_AUTHORIZATION = {
|
|
|
447
456
|
wait_for_input: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
448
457
|
goal_complete: { sessionRequired: true, allOf: ["goals:manage"] },
|
|
449
458
|
goal_pause: { sessionRequired: true, allOf: ["goals:manage"] },
|
|
459
|
+
goal_resume: { sessionRequired: true, allOf: ["goals:manage"] },
|
|
450
460
|
memory_search: { sessionRequired: true, allOf: ["documents:search"] },
|
|
451
461
|
memory_save: { sessionRequired: true, allOf: ["documents:search"] },
|
|
452
462
|
memory_correct: { sessionRequired: true, allOf: ["documents:search"] },
|
|
@@ -519,6 +529,7 @@ const FIRST_PARTY_TOOL_AUTHORIZATION = {
|
|
|
519
529
|
// self target, so the tool exists only for session-scoped grants.
|
|
520
530
|
session_wait: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
521
531
|
command_wait: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
532
|
+
command_read: { sessionRequired: true, allOf: ["sessions:read"] },
|
|
522
533
|
session_create: { allOf: ["sessions:create"] },
|
|
523
534
|
session_send_message: { allOf: ["sessions:control"] },
|
|
524
535
|
session_pause: { allOf: ["sessions:control"] },
|
|
@@ -633,8 +644,9 @@ const FIRST_PARTY_TOOL_AUTHORIZATION = {
|
|
|
633
644
|
atlassian_sources_list: { allOf: ["connections:read"] },
|
|
634
645
|
atlassian_search: { allOf: ["connections:read"] },
|
|
635
646
|
atlassian_get: { allOf: ["connections:read"] },
|
|
636
|
-
artifacts_list: {
|
|
647
|
+
artifacts_list: { allOf: ["artifacts:read"] },
|
|
637
648
|
artifacts_get_source: { sessionRequired: true, allOf: ["artifacts:read"] },
|
|
649
|
+
artifacts_prepare_upload: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
638
650
|
artifacts_create: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
639
651
|
artifacts_publish: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
640
652
|
artifacts_rollback: { sessionRequired: true, allOf: ["artifacts:publish"] },
|
|
@@ -845,6 +857,24 @@ export function buildOpenGeniMcpServer(
|
|
|
845
857
|
options.workspaceMemoryPromptMode ?? "retrieval_only",
|
|
846
858
|
);
|
|
847
859
|
}
|
|
860
|
+
server.registerTool(
|
|
861
|
+
"artifacts_list",
|
|
862
|
+
{
|
|
863
|
+
description:
|
|
864
|
+
"List the generic published artifacts in this workspace and their current versions.",
|
|
865
|
+
inputSchema: {},
|
|
866
|
+
},
|
|
867
|
+
async () => {
|
|
868
|
+
if (sessionId !== null && exactAgentAttemptClaims(grant) !== null) {
|
|
869
|
+
await authorizeFirstPartySession(deps, grant, sessionId, "session.first_party_mcp.call");
|
|
870
|
+
}
|
|
871
|
+
return json(
|
|
872
|
+
redactWorkspaceArtifactListProvenance(
|
|
873
|
+
await listWorkspaceArtifacts(deps.db, grant.workspaceId),
|
|
874
|
+
),
|
|
875
|
+
);
|
|
876
|
+
},
|
|
877
|
+
);
|
|
848
878
|
if (sessionId !== null && exactAgentAttemptClaims(grant) !== null) {
|
|
849
879
|
registerPreferenceRegistryTools(server, deps, grant, json);
|
|
850
880
|
registerTaskNoteTools(server, deps, grant, sessionId, json);
|
|
@@ -2764,7 +2794,7 @@ function registerGoalTools(
|
|
|
2764
2794
|
"wait_for_input",
|
|
2765
2795
|
{
|
|
2766
2796
|
description:
|
|
2767
|
-
"End the current turn and wait out of turn for relevant session input. This is self-only and does not require a goal. Use it for long or uncertain waits instead of sleeping or repeatedly calling session_wait/command_wait. timeoutSeconds is a relative safety-wake duration; OpenGeni persists
|
|
2797
|
+
"End the current turn and wait out of turn for relevant session input. This is self-only and does not require a goal. After success, the production runtime ends the turn at the tool-batch boundary without another model step or final message. Use it for long or uncertain waits instead of sleeping or repeatedly calling session_wait/command_wait. timeoutSeconds is a relative safety-wake duration; OpenGeni persists the first absolute deadline for the turn, and repeated calls do not extend it. Timeout never cancels a background command. A human/API prompt, agent message or Steer, child terminal result, scheduled input, terminal background-command result, or the deadline wakes the session. Use goal_pause instead when the active goal itself should stop pending a human decision.",
|
|
2768
2798
|
inputSchema: {
|
|
2769
2799
|
reason: inputWaitReasonSchema,
|
|
2770
2800
|
timeoutSeconds: z4
|
|
@@ -2811,7 +2841,7 @@ function registerGoalTools(
|
|
|
2811
2841
|
operationId,
|
|
2812
2842
|
replay,
|
|
2813
2843
|
nextAction:
|
|
2814
|
-
"
|
|
2844
|
+
"The runtime yields this turn after the tool batch settles. Relevant input or the timeout deadline will start a new turn; the timeout does not cancel commands.",
|
|
2815
2845
|
});
|
|
2816
2846
|
},
|
|
2817
2847
|
);
|
|
@@ -2917,6 +2947,61 @@ function registerGoalTools(
|
|
|
2917
2947
|
);
|
|
2918
2948
|
},
|
|
2919
2949
|
);
|
|
2950
|
+
server.registerTool(
|
|
2951
|
+
"goal_resume",
|
|
2952
|
+
{
|
|
2953
|
+
description:
|
|
2954
|
+
"Resume this session's paused goal regardless of who paused it or why. Already active is a successful no-op. Preserves the objective and resets continuation counters.",
|
|
2955
|
+
inputSchema: {},
|
|
2956
|
+
},
|
|
2957
|
+
async () => {
|
|
2958
|
+
await authorizeFirstPartySession(deps, grant, sessionId, "session.goal.write");
|
|
2959
|
+
await requireSession(deps.db, grant.workspaceId, sessionId);
|
|
2960
|
+
const existing = await getSessionGoal(deps.db, grant.workspaceId, sessionId);
|
|
2961
|
+
if (!existing) {
|
|
2962
|
+
throw new Error("this session has no goal; use goal_set first");
|
|
2963
|
+
}
|
|
2964
|
+
const { goal, events, workflowWakeRevision } = await setSessionGoalStatusWithEvent(
|
|
2965
|
+
deps.db,
|
|
2966
|
+
grant.workspaceId,
|
|
2967
|
+
sessionId,
|
|
2968
|
+
{
|
|
2969
|
+
status: "active",
|
|
2970
|
+
event: { type: "goal.resumed", actor: "agent" },
|
|
2971
|
+
},
|
|
2972
|
+
);
|
|
2973
|
+
const changed = events.length > 0;
|
|
2974
|
+
if (events.length > 0) {
|
|
2975
|
+
await deps.bus.publish(grant.workspaceId, sessionId, events);
|
|
2976
|
+
}
|
|
2977
|
+
if (workflowWakeRevision !== null) {
|
|
2978
|
+
await deps.workflowClient.wakeSessionWorkflow({
|
|
2979
|
+
accountId: grant.accountId,
|
|
2980
|
+
workspaceId: grant.workspaceId,
|
|
2981
|
+
sessionId,
|
|
2982
|
+
workflowId: workflowIdForSession(sessionId),
|
|
2983
|
+
wakeRevision: workflowWakeRevision,
|
|
2984
|
+
});
|
|
2985
|
+
}
|
|
2986
|
+
return json(
|
|
2987
|
+
mcpMutationReceipt({
|
|
2988
|
+
operation: "goal_resume",
|
|
2989
|
+
committed: true,
|
|
2990
|
+
outcome: changed ? "updated" : "unchanged",
|
|
2991
|
+
changed,
|
|
2992
|
+
resource: {
|
|
2993
|
+
type: "session_goal",
|
|
2994
|
+
id: goal.id,
|
|
2995
|
+
version: goal.version,
|
|
2996
|
+
state: goal.status,
|
|
2997
|
+
},
|
|
2998
|
+
timestamp: goal.updatedAt,
|
|
2999
|
+
idempotency: { status: "not_supported" },
|
|
3000
|
+
nextAction: { tool: "session_get", arguments: { sessionId } },
|
|
3001
|
+
}),
|
|
3002
|
+
);
|
|
3003
|
+
},
|
|
3004
|
+
);
|
|
2920
3005
|
}
|
|
2921
3006
|
|
|
2922
3007
|
type JsonResult = (value: unknown) => {
|
|
@@ -2961,13 +3046,15 @@ function registerWorkspaceArtifactTools(
|
|
|
2961
3046
|
const mutation = async (response: WorkspaceArtifactMutationResponse) =>
|
|
2962
3047
|
await projectWorkspaceArtifactMutationProvenance(response, canReadProvenanceSession);
|
|
2963
3048
|
const prepare = (
|
|
2964
|
-
html: string,
|
|
3049
|
+
html: string | undefined,
|
|
2965
3050
|
source?: { entrypoint: string; files: Array<{ path: string; content: string }> },
|
|
2966
3051
|
requestedTools?: Array<{ serverId: string; toolName: string }>,
|
|
3052
|
+
uploadId?: string,
|
|
2967
3053
|
) => {
|
|
2968
3054
|
if (!deps.objectStorage) throw new Error("Object storage is not configured");
|
|
2969
|
-
return
|
|
2970
|
-
html,
|
|
3055
|
+
return prepareWorkspaceArtifactPublication(deps, grant, {
|
|
3056
|
+
...(html === undefined ? {} : { html }),
|
|
3057
|
+
...(uploadId ? { uploadId } : {}),
|
|
2971
3058
|
...(source ? { source } : {}),
|
|
2972
3059
|
...(requestedTools ? { requestedTools } : {}),
|
|
2973
3060
|
});
|
|
@@ -2998,19 +3085,15 @@ function registerWorkspaceArtifactTools(
|
|
|
2998
3085
|
};
|
|
2999
3086
|
|
|
3000
3087
|
server.registerTool(
|
|
3001
|
-
"
|
|
3088
|
+
"artifacts_prepare_upload",
|
|
3002
3089
|
{
|
|
3003
3090
|
description:
|
|
3004
|
-
"
|
|
3091
|
+
"Get signed upload URLs for a Site's HTML and optional source JSON. Upload with curl or fetch, then pass uploadId to artifacts_create/artifacts_publish. No hashes or sizes needed. Source upload may be omitted for HTML-only Sites.",
|
|
3005
3092
|
inputSchema: {},
|
|
3006
3093
|
},
|
|
3007
3094
|
async () => {
|
|
3008
3095
|
await authorize();
|
|
3009
|
-
return json(
|
|
3010
|
-
redactWorkspaceArtifactListProvenance(
|
|
3011
|
-
await listWorkspaceArtifacts(deps.db, grant.workspaceId),
|
|
3012
|
-
),
|
|
3013
|
-
);
|
|
3096
|
+
return json(await prepareWorkspaceArtifactUpload(deps, grant));
|
|
3014
3097
|
},
|
|
3015
3098
|
);
|
|
3016
3099
|
|
|
@@ -3018,7 +3101,7 @@ function registerWorkspaceArtifactTools(
|
|
|
3018
3101
|
"artifacts_get_source",
|
|
3019
3102
|
{
|
|
3020
3103
|
description:
|
|
3021
|
-
"
|
|
3104
|
+
"Get artifact metadata, version id, and signed download URLs for HTML and optional source JSON. Download with curl or Bun. If source is null, edit the HTML instead. Omit versionId for the current version.",
|
|
3022
3105
|
inputSchema: {
|
|
3023
3106
|
artifactId: z4.string().uuid(),
|
|
3024
3107
|
versionId: z4.string().uuid().optional(),
|
|
@@ -3043,11 +3126,11 @@ function registerWorkspaceArtifactTools(
|
|
|
3043
3126
|
projectWorkspaceArtifactDetailProvenance(rawDetail, canReadSourceSession),
|
|
3044
3127
|
projectWorkspaceArtifactVersionProvenance(ref.version, canReadSourceSession),
|
|
3045
3128
|
]);
|
|
3046
|
-
const
|
|
3129
|
+
const downloads = await workspaceArtifactDownloads(deps.objectStorage, ref, "sandbox");
|
|
3047
3130
|
return json({
|
|
3048
3131
|
detail,
|
|
3049
3132
|
version: projectedVersion,
|
|
3050
|
-
|
|
3133
|
+
downloads,
|
|
3051
3134
|
});
|
|
3052
3135
|
},
|
|
3053
3136
|
);
|
|
@@ -3065,7 +3148,8 @@ function registerWorkspaceArtifactTools(
|
|
|
3065
3148
|
.regex(/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/)
|
|
3066
3149
|
.max(96)
|
|
3067
3150
|
.optional(),
|
|
3068
|
-
html: z4.string().min(1).max(WORKSPACE_ARTIFACT_HTML_MAX_UTF8_BYTES),
|
|
3151
|
+
html: z4.string().min(1).max(WORKSPACE_ARTIFACT_HTML_MAX_UTF8_BYTES).optional(),
|
|
3152
|
+
uploadId: z4.string().uuid().optional(),
|
|
3069
3153
|
source: z4
|
|
3070
3154
|
.object({
|
|
3071
3155
|
entrypoint: z4.string().min(1).max(256),
|
|
@@ -3092,7 +3176,16 @@ function registerWorkspaceArtifactTools(
|
|
|
3092
3176
|
idempotencyKey: z4.string().min(1).max(200),
|
|
3093
3177
|
},
|
|
3094
3178
|
},
|
|
3095
|
-
async ({
|
|
3179
|
+
async ({
|
|
3180
|
+
title,
|
|
3181
|
+
description,
|
|
3182
|
+
slug,
|
|
3183
|
+
html,
|
|
3184
|
+
source,
|
|
3185
|
+
requestedTools,
|
|
3186
|
+
idempotencyKey,
|
|
3187
|
+
uploadId,
|
|
3188
|
+
}) => {
|
|
3096
3189
|
await authorize();
|
|
3097
3190
|
const artifactId = crypto.randomUUID();
|
|
3098
3191
|
const slugBase = slug ?? (normalizeWorkspaceArtifactSlug(title) || "artifact");
|
|
@@ -3105,7 +3198,7 @@ function registerWorkspaceArtifactTools(
|
|
|
3105
3198
|
requestedSlug: slug ?? null,
|
|
3106
3199
|
title,
|
|
3107
3200
|
description: description ?? null,
|
|
3108
|
-
...prepare(html, source, requestedTools),
|
|
3201
|
+
...(await prepare(html, source, requestedTools, uploadId)),
|
|
3109
3202
|
...provenance(idempotencyKey, "artifacts_create"),
|
|
3110
3203
|
}),
|
|
3111
3204
|
),
|
|
@@ -3123,7 +3216,8 @@ function registerWorkspaceArtifactTools(
|
|
|
3123
3216
|
expectedCurrentVersionId: z4.string().uuid(),
|
|
3124
3217
|
title: z4.string().min(1).max(120).optional(),
|
|
3125
3218
|
description: z4.string().max(2000).nullable().optional(),
|
|
3126
|
-
html: z4.string().min(1).max(WORKSPACE_ARTIFACT_HTML_MAX_UTF8_BYTES),
|
|
3219
|
+
html: z4.string().min(1).max(WORKSPACE_ARTIFACT_HTML_MAX_UTF8_BYTES).optional(),
|
|
3220
|
+
uploadId: z4.string().uuid().optional(),
|
|
3127
3221
|
source: z4
|
|
3128
3222
|
.object({
|
|
3129
3223
|
entrypoint: z4.string().min(1).max(256),
|
|
@@ -3156,6 +3250,7 @@ function registerWorkspaceArtifactTools(
|
|
|
3156
3250
|
title,
|
|
3157
3251
|
description,
|
|
3158
3252
|
html,
|
|
3253
|
+
uploadId,
|
|
3159
3254
|
source,
|
|
3160
3255
|
requestedTools,
|
|
3161
3256
|
idempotencyKey,
|
|
@@ -3168,7 +3263,7 @@ function registerWorkspaceArtifactTools(
|
|
|
3168
3263
|
expectedCurrentVersionId,
|
|
3169
3264
|
...(title !== undefined ? { title } : {}),
|
|
3170
3265
|
...(description !== undefined ? { description } : {}),
|
|
3171
|
-
...prepare(html, source, requestedTools),
|
|
3266
|
+
...(await prepare(html, source, requestedTools, uploadId)),
|
|
3172
3267
|
...provenance(idempotencyKey, "artifacts_publish"),
|
|
3173
3268
|
}),
|
|
3174
3269
|
),
|
|
@@ -4747,13 +4842,25 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4747
4842
|
"session_get",
|
|
4748
4843
|
{
|
|
4749
4844
|
description:
|
|
4750
|
-
"Get
|
|
4845
|
+
"Get an authorized session. Omit sessionId to read only the authenticated current agent session (a child reads itself, never its parent or root); sessionless/operator callers must provide an explicit ID. Both forms retain live-attempt and target authorization checks. Default detail=compact returns status, goal (including completion evidence or pause rationale), latest recorded goal progress, meaningful pause/wait state, queue counts, active turn and snapshot lastSequence. Goal completion is not a terminal child result: join with session_wait waitFor=completion from the last consumed event cursor (0 if none), not this snapshot lastSequence, which may already include an unread completion. For an already-settled child, retrieve its result-bearing completion with session_events or join from the last consumed cursor. Use detail=full for the legacy bounded configuration including resources, persisted tool refs, effectiveToolPolicy and variableSet ids (never variable values). Full mode is configuration, not a substitute for compact goal/progress facts. Self reads inspect session state, not conversation history, which is supplied directly. Text loss is explicit; REST/UI defaults are unchanged.",
|
|
4751
4846
|
inputSchema: {
|
|
4752
|
-
sessionId: z4
|
|
4847
|
+
sessionId: z4
|
|
4848
|
+
.string()
|
|
4849
|
+
.uuid()
|
|
4850
|
+
.optional()
|
|
4851
|
+
.describe(
|
|
4852
|
+
"Omit only to read the authenticated current agent session, never its parent or root. Sessionless callers must provide an explicit ID.",
|
|
4853
|
+
),
|
|
4753
4854
|
detail: z4.enum(["compact", "full"]).optional(),
|
|
4754
4855
|
},
|
|
4755
4856
|
},
|
|
4756
|
-
async ({ sessionId, detail }) => {
|
|
4857
|
+
async ({ sessionId: requestedSessionId, detail }) => {
|
|
4858
|
+
const sessionId = requestedSessionId ?? exactAgentAttemptClaims(grant)?.sessionId;
|
|
4859
|
+
if (sessionId === undefined) {
|
|
4860
|
+
throw new Error(
|
|
4861
|
+
"session_get requires an explicit sessionId without authenticated current agent session context",
|
|
4862
|
+
);
|
|
4863
|
+
}
|
|
4757
4864
|
const authorization = await authorizeFirstPartySession(
|
|
4758
4865
|
deps,
|
|
4759
4866
|
grant,
|
|
@@ -4793,9 +4900,14 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4793
4900
|
"session_events",
|
|
4794
4901
|
{
|
|
4795
4902
|
description:
|
|
4796
|
-
"Read
|
|
4903
|
+
"Read session history. Default view=conversation returns roughly ten complete user/assistant messages, including completed commentary, in a 16 KiB envelope; no token deltas or execution records. Prefer fewer complete messages; a single oversized message has fragment offsets and a lossless nextCursor continuation over retained source text, including large legacy rows. Fragment unit is codepoint for plain text or utf16 for codec text; pass the opaque v2 cursor unchanged (v1 cursors must restart). Pass cursor=nextCursor with sessionId, omitting other selectors; it binds the view, detail and direction. after/nextAfter and before/nextBefore only change position, never view or detail; use nextCursor when present to avoid skipping a message fragment. view=results returns final turn answers and actionable outcomes without duplicate message-completion text. view=tools returns compact call/result identities; includeArguments/includeOutput opt into one text or JSON-encoded value, and callId selects an exact call (sparse scans can return an empty advancing page). sourceExact=false and sourceOmitted identify oversized structured values that were omitted, never partial JSON presented as complete; scalar text remains resumable. Conversation/results/tools omit never-claimed human/API prompts and stale duplicate events. view=debug exposes the existing authorized audit query with explicit type/class filters, mode=monitoring|forensic and payloadMode=none|summary|full; raw deltas and never-claimed prompts require mode=forensic. Explicit legacy audit selectors remain supported without view. latest is an exclusive semantic-class lookup; resultMode=compact requires latest. No read observes commands or changes append-only history. REST behavior is unchanged.",
|
|
4797
4904
|
inputSchema: {
|
|
4798
4905
|
sessionId: z4.string().uuid(),
|
|
4906
|
+
view: z4.enum(["conversation", "results", "tools", "debug"]).optional(),
|
|
4907
|
+
cursor: z4.string().max(4096).optional(),
|
|
4908
|
+
callId: z4.string().max(512).optional(),
|
|
4909
|
+
includeArguments: z4.boolean().optional(),
|
|
4910
|
+
includeOutput: z4.boolean().optional(),
|
|
4799
4911
|
after: z4.number().int().nonnegative().optional(),
|
|
4800
4912
|
before: z4.number().int().positive().optional(),
|
|
4801
4913
|
limit: z4.number().int().positive().optional(),
|
|
@@ -4803,8 +4915,34 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4803
4915
|
mode: z4.enum(SessionEventReadMode.options).optional(),
|
|
4804
4916
|
payloadMode: z4.enum(SessionEventPayloadMode.options).optional(),
|
|
4805
4917
|
resultMode: z4.enum(SessionEventResultMode.options).optional(),
|
|
4806
|
-
includeTypes: z4
|
|
4807
|
-
|
|
4918
|
+
includeTypes: z4
|
|
4919
|
+
.array(
|
|
4920
|
+
z4
|
|
4921
|
+
.string()
|
|
4922
|
+
.refine(
|
|
4923
|
+
(value) => SessionEventType.safeParse(value).success,
|
|
4924
|
+
"Unknown session event type",
|
|
4925
|
+
),
|
|
4926
|
+
)
|
|
4927
|
+
.max(100)
|
|
4928
|
+
.describe(
|
|
4929
|
+
"Debug audit event types, e.g. turn.completed, user.message, agent.message.completed, agent.toolCall.output. Validated against the canonical event-type registry.",
|
|
4930
|
+
)
|
|
4931
|
+
.optional(),
|
|
4932
|
+
excludeTypes: z4
|
|
4933
|
+
.array(
|
|
4934
|
+
z4
|
|
4935
|
+
.string()
|
|
4936
|
+
.refine(
|
|
4937
|
+
(value) => SessionEventType.safeParse(value).success,
|
|
4938
|
+
"Unknown session event type",
|
|
4939
|
+
),
|
|
4940
|
+
)
|
|
4941
|
+
.max(100)
|
|
4942
|
+
.describe(
|
|
4943
|
+
"Debug audit event types to exclude; validated against the canonical event-type registry.",
|
|
4944
|
+
)
|
|
4945
|
+
.optional(),
|
|
4808
4946
|
includeClasses: z4
|
|
4809
4947
|
.array(z4.enum(SessionEventSemanticClass.options))
|
|
4810
4948
|
.max(SessionEventSemanticClass.options.length)
|
|
@@ -4818,6 +4956,11 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4818
4956
|
},
|
|
4819
4957
|
async ({
|
|
4820
4958
|
sessionId,
|
|
4959
|
+
view,
|
|
4960
|
+
cursor,
|
|
4961
|
+
callId,
|
|
4962
|
+
includeArguments,
|
|
4963
|
+
includeOutput,
|
|
4821
4964
|
after,
|
|
4822
4965
|
before,
|
|
4823
4966
|
limit,
|
|
@@ -4825,19 +4968,75 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4825
4968
|
mode: requestedMode,
|
|
4826
4969
|
payloadMode: requestedPayloadMode,
|
|
4827
4970
|
resultMode: requestedResultMode,
|
|
4828
|
-
includeTypes,
|
|
4829
|
-
excludeTypes,
|
|
4971
|
+
includeTypes: requestedIncludeTypes,
|
|
4972
|
+
excludeTypes: requestedExcludeTypes,
|
|
4830
4973
|
includeClasses,
|
|
4831
4974
|
excludeClasses,
|
|
4832
4975
|
latest,
|
|
4833
4976
|
}) => {
|
|
4834
4977
|
await authorizeFirstPartySession(deps, grant, sessionId, "session.events.read");
|
|
4978
|
+
// Keep the model schema compact without weakening either MCP validation
|
|
4979
|
+
// or direct adapter calls: the canonical registry owns accepted types.
|
|
4980
|
+
const includeTypes = requestedIncludeTypes?.map((type) => SessionEventType.parse(type));
|
|
4981
|
+
const excludeTypes = requestedExcludeTypes?.map((type) => SessionEventType.parse(type));
|
|
4835
4982
|
const latestClass =
|
|
4836
4983
|
latest === undefined ? undefined : sessionEventLatestClassToSemanticClass(latest);
|
|
4837
4984
|
if (requestedResultMode === "compact" && latestClass === undefined) {
|
|
4838
4985
|
throw new Error("resultMode=compact requires latest");
|
|
4839
4986
|
}
|
|
4840
4987
|
await requireSession(deps.db, grant.workspaceId, sessionId);
|
|
4988
|
+
const auditRequested = [
|
|
4989
|
+
requestedMode,
|
|
4990
|
+
requestedPayloadMode,
|
|
4991
|
+
requestedResultMode,
|
|
4992
|
+
includeTypes,
|
|
4993
|
+
excludeTypes,
|
|
4994
|
+
includeClasses,
|
|
4995
|
+
excludeClasses,
|
|
4996
|
+
latest,
|
|
4997
|
+
].some((value) => value !== undefined);
|
|
4998
|
+
if (((view !== undefined && view !== "debug") || cursor !== undefined) && auditRequested) {
|
|
4999
|
+
throw new Error(
|
|
5000
|
+
"Audit selectors require view=debug and cannot change a conversation cursor",
|
|
5001
|
+
);
|
|
5002
|
+
}
|
|
5003
|
+
if (view !== "debug" && !auditRequested) {
|
|
5004
|
+
const { readSessionEventView } = await import("./session-event-view");
|
|
5005
|
+
const { listSessionEventSlices } = await import("@opengeni/db/session-event-slices");
|
|
5006
|
+
return json(
|
|
5007
|
+
await readSessionEventView(
|
|
5008
|
+
{
|
|
5009
|
+
sessionId,
|
|
5010
|
+
view,
|
|
5011
|
+
cursor,
|
|
5012
|
+
callId,
|
|
5013
|
+
includeArguments,
|
|
5014
|
+
includeOutput,
|
|
5015
|
+
after,
|
|
5016
|
+
before,
|
|
5017
|
+
direction: requestedDirection,
|
|
5018
|
+
limit,
|
|
5019
|
+
},
|
|
5020
|
+
(options) =>
|
|
5021
|
+
listSessionEventSlices(
|
|
5022
|
+
deps.db,
|
|
5023
|
+
grant.workspaceId,
|
|
5024
|
+
sessionId,
|
|
5025
|
+
options,
|
|
5026
|
+
(legacyOptions) =>
|
|
5027
|
+
listSessionEventPage(deps.db, grant.workspaceId, sessionId, legacyOptions),
|
|
5028
|
+
),
|
|
5029
|
+
),
|
|
5030
|
+
);
|
|
5031
|
+
}
|
|
5032
|
+
if (
|
|
5033
|
+
cursor !== undefined ||
|
|
5034
|
+
callId !== undefined ||
|
|
5035
|
+
includeArguments !== undefined ||
|
|
5036
|
+
includeOutput !== undefined
|
|
5037
|
+
) {
|
|
5038
|
+
throw new Error("cursor/callId/includeArguments/includeOutput require a non-debug view");
|
|
5039
|
+
}
|
|
4841
5040
|
if (
|
|
4842
5041
|
latest &&
|
|
4843
5042
|
[includeTypes, excludeTypes, includeClasses, excludeClasses].some(
|
|
@@ -4846,7 +5045,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4846
5045
|
) {
|
|
4847
5046
|
throw new Error("latest cannot be combined with event filters");
|
|
4848
5047
|
}
|
|
4849
|
-
const mode = requestedMode ??
|
|
5048
|
+
const mode = requestedMode ?? "monitoring";
|
|
4850
5049
|
const direction = latestClass
|
|
4851
5050
|
? "before"
|
|
4852
5051
|
: (requestedDirection ??
|
|
@@ -4905,7 +5104,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4905
5104
|
server.registerTool(
|
|
4906
5105
|
"session_wait",
|
|
4907
5106
|
{
|
|
4908
|
-
description: `
|
|
5107
|
+
description: `Wait once for durable session changes, your pending machine input, or maxWaitSeconds (default ${SESSION_WAIT_DEFAULT_SECONDS}, max ${SESSION_WAIT_MAX_SECONDS}). Pass targets with sessionId and afterSequence: the last consumed cursor, or 0. Never substitute session_get.lastSequence, which may include an unread completion. waitFor=change returns on turn lifecycle, completed messages, terminal commands, blockers, goal facts, or session control. waitFor=completion joins a child result: only a result-bearing final turn or blocker qualifies, not commentary, goal.completed, background commands, maintenance turns, or continuation segments. Neither mode wakes on raw deltas or tool receipts. Each target contains up to ${SESSION_WAIT_EVENTS_PER_TARGET} bounded summaries, latestSequence (the next afterSequence), and hasMore. For omitted rows use session_events view=results after=latestSequence for final outcomes, or view=debug with explicit filters for diagnostics; the default conversation view does not contain execution records. Byte limits can leave events=[] with hasMore=true. ownPendingUpdates > 0 means input will arrive when your next turn is claimed: finish this turn, or use includeOwnPendingUpdates=false to keep waiting. timedOut=true means no matching change; liveFanout=false means the deadline re-check supplied durable truth without the live bus. Do not immediately repeat a timeout without new evidence. For long or uncertain waits call wait_for_input once and end the turn.`,
|
|
4909
5108
|
inputSchema: {
|
|
4910
5109
|
targets: z4
|
|
4911
5110
|
.array(
|
|
@@ -5007,124 +5206,53 @@ function registerWorkspaceOrchestrationTools(
|
|
|
5007
5206
|
);
|
|
5008
5207
|
|
|
5009
5208
|
if (callerSessionId !== null) {
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5209
|
+
for (const toolName of ["command_read", "command_wait"] as const) {
|
|
5210
|
+
server.registerTool(
|
|
5211
|
+
toolName,
|
|
5212
|
+
{
|
|
5213
|
+
description: `${toolName === "command_read" ? "Read retained output immediately, or briefly wait" : "Briefly wait using the same read operation as command_read"} for one command owned by the current session, across sandbox and Connected Machine providers. Returns bounded stdout/stderr chunks, durable state and exitCode, nextCursor and hasMore, plus explicit retention limitations. Pass nextCursor to continue; output remains readable after the process exits. A terminal read observes completion and suppresses only its still-pending inbox notification; running reads and already claimed history are unchanged. waitSeconds is at most 50; command_wait defaults to 45, command_read to 0. For long waits use wait_for_input. Neither tool sends stdin or cancels the command.`,
|
|
5214
|
+
inputSchema: {
|
|
5215
|
+
commandId: z4.string().uuid(),
|
|
5216
|
+
cursor: z4.string().max(128).optional(),
|
|
5217
|
+
waitSeconds: z4.number().int().min(0).max(COMMAND_READ_MAX_WAIT_SECONDS).optional(),
|
|
5218
|
+
maxOutputBytes: z4.number().int().min(4).max(COMMAND_OUTPUT_MAX_BYTES).optional(),
|
|
5219
|
+
},
|
|
5017
5220
|
},
|
|
5018
|
-
|
|
5019
|
-
async ({ commandId, maxWaitSeconds }, extra) => {
|
|
5020
|
-
await authorizeFirstPartySession(deps, grant, callerSessionId, "session.events.read");
|
|
5021
|
-
const initialSession = await requireSession(deps.db, grant.workspaceId, callerSessionId);
|
|
5022
|
-
let command = await getSessionBackgroundCommand(deps.db, {
|
|
5023
|
-
accountId: grant.accountId,
|
|
5024
|
-
workspaceId: grant.workspaceId,
|
|
5025
|
-
sessionId: callerSessionId,
|
|
5026
|
-
commandId,
|
|
5027
|
-
});
|
|
5028
|
-
if (!command) throw new Error("Background command not found in this session");
|
|
5029
|
-
|
|
5030
|
-
const terminal = () => command?.state === "exited" || command?.state === "lost";
|
|
5031
|
-
let waitResult: Awaited<ReturnType<typeof waitForSessionChanges>> | null = null;
|
|
5032
|
-
if (!terminal()) {
|
|
5033
|
-
waitResult = await waitForSessionChanges({
|
|
5034
|
-
targets: [{ sessionId: callerSessionId, afterSequence: initialSession.lastSequence }],
|
|
5035
|
-
ownSessionId: callerSessionId,
|
|
5036
|
-
maxWaitMs: (maxWaitSeconds ?? SESSION_WAIT_DEFAULT_SECONDS) * 1_000,
|
|
5037
|
-
targetEventTypes: ["session.command.finished"],
|
|
5038
|
-
targetEventMatches: (event) => {
|
|
5039
|
-
const payload = event.payload;
|
|
5040
|
-
return (
|
|
5041
|
-
payload !== null &&
|
|
5042
|
-
typeof payload === "object" &&
|
|
5043
|
-
!Array.isArray(payload) &&
|
|
5044
|
-
(payload as Record<string, unknown>).commandId === commandId
|
|
5045
|
-
);
|
|
5046
|
-
},
|
|
5047
|
-
signal: extra?.signal,
|
|
5048
|
-
source: {
|
|
5049
|
-
readTargetEvents: async (target) => {
|
|
5050
|
-
const page = await listSessionEventPage(
|
|
5051
|
-
deps.db,
|
|
5052
|
-
grant.workspaceId,
|
|
5053
|
-
target.sessionId,
|
|
5054
|
-
{
|
|
5055
|
-
after: target.afterSequence,
|
|
5056
|
-
direction: "after",
|
|
5057
|
-
limit: SESSION_WAIT_EVENTS_PER_TARGET,
|
|
5058
|
-
payloadMode: "full",
|
|
5059
|
-
includeTypes: ["session.command.finished"],
|
|
5060
|
-
maxBytes: SESSION_EVENT_MCP_MAX_BYTES * 4,
|
|
5061
|
-
},
|
|
5062
|
-
);
|
|
5063
|
-
return { events: page.events, hasMore: page.hasMore };
|
|
5064
|
-
},
|
|
5065
|
-
readOwnPendingUpdateKinds: async () =>
|
|
5066
|
-
(
|
|
5067
|
-
await listOutstandingSessionSystemUpdates(
|
|
5068
|
-
deps.db,
|
|
5069
|
-
grant.workspaceId,
|
|
5070
|
-
callerSessionId,
|
|
5071
|
-
)
|
|
5072
|
-
).map((update) => update.kind),
|
|
5073
|
-
subscribe: (targetSessionId, onEvents) =>
|
|
5074
|
-
deps.bus.subscribe(grant.workspaceId, targetSessionId, onEvents),
|
|
5075
|
-
},
|
|
5076
|
-
});
|
|
5077
|
-
}
|
|
5078
|
-
|
|
5079
|
-
command =
|
|
5080
|
-
(await getSessionBackgroundCommand(deps.db, {
|
|
5081
|
-
accountId: grant.accountId,
|
|
5082
|
-
workspaceId: grant.workspaceId,
|
|
5083
|
-
sessionId: callerSessionId,
|
|
5084
|
-
commandId,
|
|
5085
|
-
})) ?? command;
|
|
5086
|
-
const isTerminal = terminal();
|
|
5087
|
-
const ownPendingUpdateKinds = (
|
|
5088
|
-
await listOutstandingSessionSystemUpdates(deps.db, grant.workspaceId, callerSessionId)
|
|
5089
|
-
).map((update) => update.kind);
|
|
5090
|
-
const ownPendingUpdates = ownPendingUpdateKinds.length;
|
|
5091
|
-
if (waitResult) {
|
|
5092
|
-
// A bounded wait may cross a host/session revocation boundary. The
|
|
5093
|
-
// durable command and pending-input reads above are not returned
|
|
5094
|
-
// until the target is authorized again at the response boundary.
|
|
5221
|
+
async ({ commandId, cursor, waitSeconds, maxOutputBytes }, extra) => {
|
|
5095
5222
|
await authorizeFirstPartySession(deps, grant, callerSessionId, "session.events.read");
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5223
|
+
let initialRead = true;
|
|
5224
|
+
return json(
|
|
5225
|
+
await readCommandWithWait({
|
|
5226
|
+
commandId,
|
|
5227
|
+
waitSeconds:
|
|
5228
|
+
waitSeconds ?? (toolName === "command_wait" ? COMMAND_WAIT_DEFAULT_SECONDS : 0),
|
|
5229
|
+
...(maxOutputBytes === undefined ? {} : { maxOutputBytes }),
|
|
5230
|
+
...(extra?.signal === undefined ? {} : { signal: extra.signal }),
|
|
5231
|
+
read: async () => {
|
|
5232
|
+
if (!initialRead)
|
|
5233
|
+
await authorizeFirstPartySession(
|
|
5234
|
+
deps,
|
|
5235
|
+
grant,
|
|
5236
|
+
callerSessionId,
|
|
5237
|
+
"session.events.read",
|
|
5238
|
+
);
|
|
5239
|
+
initialRead = false;
|
|
5240
|
+
return await readSessionBackgroundCommandOutput(deps.db, {
|
|
5241
|
+
accountId: grant.accountId,
|
|
5242
|
+
workspaceId: grant.workspaceId,
|
|
5243
|
+
sessionId: callerSessionId,
|
|
5112
5244
|
commandId,
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
: "The wait was aborted; follow the newer session control or input.",
|
|
5125
|
-
});
|
|
5126
|
-
},
|
|
5127
|
-
);
|
|
5245
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
5246
|
+
...(maxOutputBytes === undefined ? {} : { maxOutputBytes }),
|
|
5247
|
+
});
|
|
5248
|
+
},
|
|
5249
|
+
subscribe: (onEvents) =>
|
|
5250
|
+
deps.bus.subscribe(grant.workspaceId, callerSessionId, onEvents),
|
|
5251
|
+
}),
|
|
5252
|
+
);
|
|
5253
|
+
},
|
|
5254
|
+
);
|
|
5255
|
+
}
|
|
5128
5256
|
}
|
|
5129
5257
|
}
|
|
5130
5258
|
|
|
@@ -5142,7 +5270,12 @@ function registerWorkspaceOrchestrationTools(
|
|
|
5142
5270
|
),
|
|
5143
5271
|
instructions: z4.string().min(1).max(SESSION_INSTRUCTIONS_MAX_CHARACTERS).optional(),
|
|
5144
5272
|
goal: z4.unknown().optional(),
|
|
5145
|
-
resources: z4
|
|
5273
|
+
resources: z4
|
|
5274
|
+
.array(z4.unknown())
|
|
5275
|
+
.optional()
|
|
5276
|
+
.describe(
|
|
5277
|
+
"Omit to inherit parent repositories only. Files are never inherited automatically; explicitly include file resources to attach them. An empty array inherits no resources.",
|
|
5278
|
+
),
|
|
5146
5279
|
tools: z4.array(z4.unknown()).optional(),
|
|
5147
5280
|
mcpServers: z4.array(z4.unknown()).optional(),
|
|
5148
5281
|
variableSetId: z4.string().uuid().optional(),
|
|
@@ -6303,6 +6436,7 @@ const SESSION_DISCOVERY_PREVIEW_OMISSION_REASON = "aggregatePreviewBudget" as co
|
|
|
6303
6436
|
const SESSION_DISCOVERY_PAGE_MAX_BYTES = 128_000;
|
|
6304
6437
|
const SESSION_DISCOVERY_PREVIEW_DRILL_DOWN_TOOL = "session_events" as const;
|
|
6305
6438
|
const SESSION_DISCOVERY_PREVIEW_DRILL_DOWN_BASE_INPUT = {
|
|
6439
|
+
view: "debug",
|
|
6306
6440
|
direction: "before",
|
|
6307
6441
|
limit: 1,
|
|
6308
6442
|
mode: "monitoring",
|