@opengeni/api-router 2.3.2-canary.2 → 2.5.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/app.js +1 -1
- package/dist/auth/managed-auth-attempt-context.d.ts +4 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +4 -0
- package/dist/{chunk-IBV7Z6F4.js → chunk-QESX7HDK.js} +3645 -470
- package/dist/chunk-QESX7HDK.js.map +1 -0
- package/dist/fatal-process-boundary.d.ts +25 -0
- package/dist/http/sse.d.ts +2 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +168 -6
- package/dist/index.js.map +1 -1
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/receipts.d.ts +9 -0
- package/dist/mcp/server.d.ts +33 -0
- package/dist/organization-recovery-notifications.d.ts +41 -0
- package/dist/routes/managed-auth-session-sets.d.ts +19 -0
- package/dist/routes/organization-recovery.d.ts +19 -0
- package/dist/routes/sessions.d.ts +17 -5
- package/dist/routes/workspaces.d.ts +1 -0
- package/dist/work-discovery-observability.d.ts +33 -0
- package/package.json +18 -18
- package/src/app.ts +133 -1
- package/src/auth/managed-auth-attempt-context.ts +24 -0
- package/src/auth/managed-auth-session-adapter.ts +205 -0
- package/src/auth/managed-auth.ts +52 -2
- package/src/fatal-process-boundary.ts +231 -0
- package/src/http/sse.ts +7 -0
- package/src/index.ts +25 -5
- package/src/integrations/slack-interactions.ts +30 -17
- package/src/mcp/receipts.ts +34 -0
- package/src/mcp/server.ts +349 -68
- package/src/organization-recovery-notifications.ts +103 -0
- package/src/routes/canonical-human-identities.ts +29 -14
- package/src/routes/codex.ts +5 -1
- package/src/routes/environments.ts +23 -0
- package/src/routes/interaction-resources.ts +3 -0
- package/src/routes/managed-auth-session-sets.ts +994 -0
- package/src/routes/managed-onboarding.ts +2 -0
- package/src/routes/organization-memberships.ts +2 -0
- package/src/routes/organization-recovery.ts +325 -0
- package/src/routes/sessions.ts +401 -120
- package/src/routes/supergrok.ts +5 -1
- package/src/routes/workspaces.ts +23 -1
- package/src/work-discovery-observability.ts +121 -0
- package/dist/chunk-IBV7Z6F4.js.map +0 -1
package/src/mcp/server.ts
CHANGED
|
@@ -45,6 +45,16 @@ import {
|
|
|
45
45
|
TASK_NOTE_MAX_LIFETIME_DAYS,
|
|
46
46
|
TASK_NOTE_REASON_MAX_BYTES,
|
|
47
47
|
TASK_NOTE_TEXT_MAX_BYTES,
|
|
48
|
+
WORK_CLAIM_CANONICAL_KEY_MAX_BYTES,
|
|
49
|
+
WORK_CLAIM_DISPLAY_LABEL_MAX_BYTES,
|
|
50
|
+
WORK_CLAIM_DISCOVERY_LIMIT,
|
|
51
|
+
WORK_CLAIM_NAMESPACE_MAX_BYTES,
|
|
52
|
+
WORK_CLAIM_VERSION_VALUE_MAX_BYTES,
|
|
53
|
+
WORK_DISCOVERY_QUERY_MAX_CHARS,
|
|
54
|
+
WORK_DISCOVERY_RECENT_HOURS_MAX,
|
|
55
|
+
WorkClaimSubjectType,
|
|
56
|
+
type WorkClaimSubjectFilter,
|
|
57
|
+
type SessionStatus,
|
|
48
58
|
SubmitHumanInputResponseRequest,
|
|
49
59
|
} from "@opengeni/contracts";
|
|
50
60
|
import {
|
|
@@ -104,6 +114,8 @@ import {
|
|
|
104
114
|
createTaskNote,
|
|
105
115
|
listTaskNotes,
|
|
106
116
|
replaceTaskNote,
|
|
117
|
+
releaseWorkClaim,
|
|
118
|
+
upsertWorkClaim,
|
|
107
119
|
acceptSessionHumanInputResponse,
|
|
108
120
|
HumanInputResponseValidationError,
|
|
109
121
|
} from "@opengeni/db";
|
|
@@ -186,7 +198,7 @@ import {
|
|
|
186
198
|
import {
|
|
187
199
|
acceptSessionUserMessageWithOutcome,
|
|
188
200
|
controlAgentSessionWorkstream,
|
|
189
|
-
|
|
201
|
+
controlHumanSessionWorkstreamWithOutcome,
|
|
190
202
|
createSessionForRequestWithOutcome,
|
|
191
203
|
SessionSpawnDeniedError,
|
|
192
204
|
sessionSpawnDenialEnvelope,
|
|
@@ -223,7 +235,11 @@ import {
|
|
|
223
235
|
SESSION_WAIT_MAX_TARGETS,
|
|
224
236
|
waitForSessionChanges,
|
|
225
237
|
} from "./session-wait";
|
|
226
|
-
import {
|
|
238
|
+
import {
|
|
239
|
+
mcpMutationReceipt,
|
|
240
|
+
sessionControlMutationReceipt,
|
|
241
|
+
sessionCreateMutationReceipt,
|
|
242
|
+
} from "./receipts";
|
|
227
243
|
import {
|
|
228
244
|
boundScheduledTaskDetailMcp,
|
|
229
245
|
boundScheduledTaskMcpPage,
|
|
@@ -247,6 +263,7 @@ import { registerCompanyProfileAgentAdminTools } from "./company-profile-agent-a
|
|
|
247
263
|
import { registerRememberTools } from "./remember";
|
|
248
264
|
import { mintSandboxCodemodeToken } from "@opengeni/runtime/sandbox";
|
|
249
265
|
import { deleteScheduledTaskWithDurableCleanup } from "../scheduled-task-deletion";
|
|
266
|
+
import { observeWorkDiscovery, summarizeWorkDiscoveryRows } from "../work-discovery-observability";
|
|
250
267
|
|
|
251
268
|
export type McpServerOptions = {
|
|
252
269
|
// Origin of the HTTP request that reached the MCP route. Browser-oriented
|
|
@@ -406,6 +423,8 @@ const FIRST_PARTY_TOOL_AUTHORIZATION = {
|
|
|
406
423
|
task_note_save: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
407
424
|
task_note_archive: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
408
425
|
task_note_replace: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
426
|
+
work_claim_upsert: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
427
|
+
work_claim_release: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
409
428
|
knowledge_propose: { sessionRequired: true, allOf: ["documents:search"] },
|
|
410
429
|
knowledge_correct: { sessionRequired: true, allOf: ["documents:search"] },
|
|
411
430
|
task_note_promote_knowledge: {
|
|
@@ -787,6 +806,9 @@ export function buildOpenGeniMcpServer(
|
|
|
787
806
|
if (sessionId !== null && exactAgentAttemptClaims(grant) !== null) {
|
|
788
807
|
registerPreferenceRegistryTools(server, deps, grant, json);
|
|
789
808
|
registerTaskNoteTools(server, deps, grant, sessionId, json);
|
|
809
|
+
if (deps.settings.workClaimMutationsEnabled) {
|
|
810
|
+
registerWorkClaimTools(server, deps, grant, sessionId, json);
|
|
811
|
+
}
|
|
790
812
|
const attempt = exactAgentAttemptClaims(grant)!;
|
|
791
813
|
registerCompanyBrainGovernedWriteTools({
|
|
792
814
|
server,
|
|
@@ -3237,6 +3259,133 @@ function registerTaskNoteTools(
|
|
|
3237
3259
|
);
|
|
3238
3260
|
}
|
|
3239
3261
|
|
|
3262
|
+
function registerWorkClaimTools(
|
|
3263
|
+
server: McpServer,
|
|
3264
|
+
deps: ApiRouteDeps,
|
|
3265
|
+
grant: AccessGrant,
|
|
3266
|
+
sessionId: string,
|
|
3267
|
+
json: JsonResult,
|
|
3268
|
+
): void {
|
|
3269
|
+
const attemptClaims = () => {
|
|
3270
|
+
const resolved = exactAgentAttemptClaims(grant);
|
|
3271
|
+
if (!resolved || resolved.sessionId !== sessionId) {
|
|
3272
|
+
throw new Error("Exact signed work-claim attempt authority is required.");
|
|
3273
|
+
}
|
|
3274
|
+
return {
|
|
3275
|
+
accountId: grant.accountId,
|
|
3276
|
+
workspaceId: grant.workspaceId,
|
|
3277
|
+
...resolved,
|
|
3278
|
+
};
|
|
3279
|
+
};
|
|
3280
|
+
const authorize = async () => {
|
|
3281
|
+
await authorizeFirstPartySession(deps, grant, sessionId, "session.first_party_mcp.call");
|
|
3282
|
+
};
|
|
3283
|
+
|
|
3284
|
+
server.registerTool(
|
|
3285
|
+
"work_claim_upsert",
|
|
3286
|
+
{
|
|
3287
|
+
description:
|
|
3288
|
+
"Create or refresh one typed, non-exclusive claim describing this session's current external work. Claims are advisory evidence, never locks or authority. Use stable public identifiers only; never put credentials, tokens, or other secrets in claim fields. expectedRevision=0 creates a new active claim; refreshing an existing active claim requires its exact revision. Use a fresh operationId; an exact retry from a replacement attempt on the same logical turn replays safely.",
|
|
3289
|
+
inputSchema: {
|
|
3290
|
+
operationId: z4.string().uuid(),
|
|
3291
|
+
expectedRevision: z4.number().int().min(0),
|
|
3292
|
+
subjectNamespace: z4.string().min(1).max(WORK_CLAIM_NAMESPACE_MAX_BYTES),
|
|
3293
|
+
subjectType: z4.enum([
|
|
3294
|
+
"repository",
|
|
3295
|
+
"branch",
|
|
3296
|
+
"pull_request",
|
|
3297
|
+
"issue",
|
|
3298
|
+
"artifact",
|
|
3299
|
+
"release",
|
|
3300
|
+
"ci_run",
|
|
3301
|
+
"other",
|
|
3302
|
+
]),
|
|
3303
|
+
canonicalKey: z4.string().min(1).max(WORK_CLAIM_CANONICAL_KEY_MAX_BYTES),
|
|
3304
|
+
displayLabel: z4.string().min(1).max(WORK_CLAIM_DISPLAY_LABEL_MAX_BYTES).optional(),
|
|
3305
|
+
role: z4.enum(["working", "reviewing", "monitoring", "delivering"]),
|
|
3306
|
+
versionKind: z4
|
|
3307
|
+
.enum([
|
|
3308
|
+
"git_commit",
|
|
3309
|
+
"branch_head",
|
|
3310
|
+
"pull_request_head",
|
|
3311
|
+
"artifact_version",
|
|
3312
|
+
"release_version",
|
|
3313
|
+
"ci_run",
|
|
3314
|
+
"other",
|
|
3315
|
+
])
|
|
3316
|
+
.optional(),
|
|
3317
|
+
versionValue: z4.string().min(1).max(WORK_CLAIM_VERSION_VALUE_MAX_BYTES).optional(),
|
|
3318
|
+
},
|
|
3319
|
+
},
|
|
3320
|
+
async ({
|
|
3321
|
+
operationId,
|
|
3322
|
+
expectedRevision,
|
|
3323
|
+
subjectNamespace,
|
|
3324
|
+
subjectType,
|
|
3325
|
+
canonicalKey,
|
|
3326
|
+
displayLabel,
|
|
3327
|
+
role,
|
|
3328
|
+
versionKind,
|
|
3329
|
+
versionValue,
|
|
3330
|
+
}) => {
|
|
3331
|
+
await authorize();
|
|
3332
|
+
if ((versionKind === undefined) !== (versionValue === undefined)) {
|
|
3333
|
+
throw new Error("work_claim_upsert versionKind and versionValue must be supplied together");
|
|
3334
|
+
}
|
|
3335
|
+
return json(
|
|
3336
|
+
await upsertWorkClaim(deps.db, {
|
|
3337
|
+
...attemptClaims(),
|
|
3338
|
+
operationId,
|
|
3339
|
+
expectedRevision,
|
|
3340
|
+
subjectNamespace,
|
|
3341
|
+
subjectType,
|
|
3342
|
+
canonicalKey,
|
|
3343
|
+
...(displayLabel === undefined ? {} : { displayLabel }),
|
|
3344
|
+
role,
|
|
3345
|
+
...(versionKind && versionValue
|
|
3346
|
+
? { version: { kind: versionKind, value: versionValue } }
|
|
3347
|
+
: {}),
|
|
3348
|
+
}),
|
|
3349
|
+
);
|
|
3350
|
+
},
|
|
3351
|
+
);
|
|
3352
|
+
|
|
3353
|
+
server.registerTool(
|
|
3354
|
+
"work_claim_release",
|
|
3355
|
+
{
|
|
3356
|
+
description:
|
|
3357
|
+
"Release one exact active claim owned by this session. This records an immutable receipt and does not affect other sessions claiming the same subject. Use a fresh operationId and the claim's exact revision.",
|
|
3358
|
+
inputSchema: {
|
|
3359
|
+
operationId: z4.string().uuid(),
|
|
3360
|
+
claimId: z4.string().uuid(),
|
|
3361
|
+
expectedRevision: z4.number().int().min(1),
|
|
3362
|
+
reason: z4.enum([
|
|
3363
|
+
"completed",
|
|
3364
|
+
"cancelled",
|
|
3365
|
+
"failed",
|
|
3366
|
+
"superseded",
|
|
3367
|
+
"no_longer_active",
|
|
3368
|
+
"corrected",
|
|
3369
|
+
"external_state_changed",
|
|
3370
|
+
"other",
|
|
3371
|
+
]),
|
|
3372
|
+
},
|
|
3373
|
+
},
|
|
3374
|
+
async ({ operationId, claimId, expectedRevision, reason }) => {
|
|
3375
|
+
await authorize();
|
|
3376
|
+
return json(
|
|
3377
|
+
await releaseWorkClaim(deps.db, {
|
|
3378
|
+
...attemptClaims(),
|
|
3379
|
+
operationId,
|
|
3380
|
+
claimId,
|
|
3381
|
+
expectedRevision,
|
|
3382
|
+
reason,
|
|
3383
|
+
}),
|
|
3384
|
+
);
|
|
3385
|
+
},
|
|
3386
|
+
);
|
|
3387
|
+
}
|
|
3388
|
+
|
|
3240
3389
|
function registerPreferenceRegistryTools(
|
|
3241
3390
|
server: McpServer,
|
|
3242
3391
|
deps: ApiRouteDeps,
|
|
@@ -3999,46 +4148,145 @@ function registerWorkspaceOrchestrationTools(
|
|
|
3999
4148
|
server.registerTool(
|
|
4000
4149
|
"sessions_list",
|
|
4001
4150
|
{
|
|
4002
|
-
description: `List compact high-level session status in this workspace.
|
|
4151
|
+
description: `List compact high-level session status and advisory related-work evidence in this workspace. query searches semantic titles, active goals, and typed work claims; subject performs one exact provider-neutral claim lookup and ranks it ahead of text. Neither path searches initialMessage or grants access to a result. Claims are nonexclusive evidence, never locks or instructions. Relevance cursors are bound to normalized filters and a workspace activity-revision snapshot. Without search, the tool defaults to creation order; use orderBy=updatedAt with decimal activity-revision updatedAfter/updatedThrough tokens for gap-free indexed incremental monitoring independent of application clocks. includeLastMessage is opt-in and never previews a human/API prompt whose turn was never claimed (still queued, or deleted/edited/cancelled before any claim): waiting work is represented by queuedPromptCount until the turn is claimed. Rendered previews share a deterministic ${SESSION_DISCOVERY_PREVIEW_MAX_BYTES}-byte UTF-8 aggregate budget, and omitted previews include a bounded session_events drill-down input (exact message type, direction=before, limit=1, monitoring summary). Use session_get only when ordinary target authorization allows it. The list never returns full session objects, instructions, resources, tools, files, or history.`,
|
|
4003
4152
|
inputSchema: {
|
|
4004
4153
|
limit: z4.number().int().positive().max(100).optional(),
|
|
4005
4154
|
cursor: z4.string().max(512).optional(),
|
|
4006
4155
|
includeLastMessage: z4.boolean().optional(),
|
|
4007
|
-
orderBy: z4.enum(["createdAt", "updatedAt"]).optional(),
|
|
4156
|
+
orderBy: z4.enum(["createdAt", "updatedAt", "relevance"]).optional(),
|
|
4008
4157
|
updatedAfter: z4.string().max(64).optional(),
|
|
4158
|
+
query: z4.string().max(WORK_DISCOVERY_QUERY_MAX_CHARS).optional(),
|
|
4159
|
+
statuses: z4
|
|
4160
|
+
.array(
|
|
4161
|
+
z4.enum([
|
|
4162
|
+
"queued",
|
|
4163
|
+
"running",
|
|
4164
|
+
"idle",
|
|
4165
|
+
"requires_action",
|
|
4166
|
+
"recovering",
|
|
4167
|
+
"waiting_capacity",
|
|
4168
|
+
"failed",
|
|
4169
|
+
"cancelled",
|
|
4170
|
+
]),
|
|
4171
|
+
)
|
|
4172
|
+
.max(8)
|
|
4173
|
+
.optional(),
|
|
4174
|
+
activeOnly: z4.boolean().optional(),
|
|
4175
|
+
recentHours: z4.number().int().positive().max(WORK_DISCOVERY_RECENT_HOURS_MAX).optional(),
|
|
4176
|
+
rootSessionId: z4.string().uuid().optional(),
|
|
4177
|
+
parentSessionId: z4.string().uuid().nullable().optional(),
|
|
4178
|
+
subject: z4
|
|
4179
|
+
.object({
|
|
4180
|
+
namespace: z4.string().min(1).max(WORK_CLAIM_NAMESPACE_MAX_BYTES),
|
|
4181
|
+
type: WorkClaimSubjectType,
|
|
4182
|
+
canonicalKey: z4.string().min(1).max(WORK_CLAIM_CANONICAL_KEY_MAX_BYTES),
|
|
4183
|
+
})
|
|
4184
|
+
.strict()
|
|
4185
|
+
.optional(),
|
|
4186
|
+
claimLimit: z4.number().int().positive().max(WORK_CLAIM_DISCOVERY_LIMIT).optional(),
|
|
4009
4187
|
},
|
|
4010
4188
|
},
|
|
4011
|
-
async ({
|
|
4189
|
+
async ({
|
|
4190
|
+
limit,
|
|
4191
|
+
cursor,
|
|
4192
|
+
includeLastMessage,
|
|
4193
|
+
orderBy: requestedOrderBy,
|
|
4194
|
+
updatedAfter,
|
|
4195
|
+
query,
|
|
4196
|
+
statuses,
|
|
4197
|
+
activeOnly,
|
|
4198
|
+
recentHours,
|
|
4199
|
+
rootSessionId,
|
|
4200
|
+
parentSessionId,
|
|
4201
|
+
subject,
|
|
4202
|
+
claimLimit,
|
|
4203
|
+
}) => {
|
|
4012
4204
|
const authorizationScope = await requireSessionAuthorizationListScope(
|
|
4013
4205
|
deps,
|
|
4014
4206
|
grant,
|
|
4015
4207
|
"first_party_mcp",
|
|
4016
4208
|
);
|
|
4209
|
+
const startedAtMs = performance.now();
|
|
4210
|
+
const mode = subject ? "subject" : query?.trim() ? "query" : "browse";
|
|
4211
|
+
const metricAuthorizationScope =
|
|
4212
|
+
authorizationScope?.kind === "scoped" ? "scoped" : "workspace";
|
|
4017
4213
|
const decodedCursor = cursor ? decodeSessionDiscoveryCursor(cursor) : undefined;
|
|
4018
|
-
const
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
:
|
|
4027
|
-
|
|
4028
|
-
|
|
4214
|
+
const relevanceRequested = Boolean(query?.trim() || subject);
|
|
4215
|
+
if (relevanceRequested && !deps.settings.workDiscoveryEnabled) {
|
|
4216
|
+
observeWorkDiscovery(deps.observability, {
|
|
4217
|
+
surface: "first_party_mcp",
|
|
4218
|
+
mode,
|
|
4219
|
+
outcome: "disabled",
|
|
4220
|
+
authorizationScope: metricAuthorizationScope,
|
|
4221
|
+
durationMs: performance.now() - startedAtMs,
|
|
4222
|
+
responseBytes: 0,
|
|
4223
|
+
resultCount: 0,
|
|
4224
|
+
overlapCount: 0,
|
|
4225
|
+
matchCounts: {},
|
|
4226
|
+
});
|
|
4227
|
+
throw new Error("sessions_list work discovery is disabled by the operator");
|
|
4029
4228
|
}
|
|
4030
|
-
|
|
4031
|
-
|
|
4229
|
+
try {
|
|
4230
|
+
const orderBy: SessionDiscoveryOrderBy =
|
|
4231
|
+
requestedOrderBy ??
|
|
4232
|
+
decodedCursor?.orderBy ??
|
|
4233
|
+
(relevanceRequested ? "relevance" : "createdAt");
|
|
4234
|
+
if (decodedCursor && decodedCursor.orderBy !== orderBy) {
|
|
4235
|
+
throw new Error("sessions_list cursor order does not match orderBy");
|
|
4236
|
+
}
|
|
4237
|
+
const normalizedUpdatedAfter =
|
|
4238
|
+
updatedAfter !== undefined
|
|
4239
|
+
? normalizeSessionDiscoveryRevision(updatedAfter, "updatedAfter")
|
|
4240
|
+
: (decodedCursor?.updatedAfter ?? undefined);
|
|
4241
|
+
if (normalizedUpdatedAfter !== undefined && orderBy !== "updatedAt") {
|
|
4242
|
+
throw new Error("sessions_list updatedAfter requires orderBy=updatedAt");
|
|
4243
|
+
}
|
|
4244
|
+
if (decodedCursor && decodedCursor.updatedAfter !== (normalizedUpdatedAfter ?? null)) {
|
|
4245
|
+
throw new Error("sessions_list cursor does not match updatedAfter");
|
|
4246
|
+
}
|
|
4247
|
+
const page = await listSessionDiscoverySummaries(deps.db, grant.workspaceId, {
|
|
4248
|
+
limit: boundedSessionDiscoveryLimit(limit),
|
|
4249
|
+
...(decodedCursor ? { cursor: decodedCursor } : {}),
|
|
4250
|
+
includeLastMessage: includeLastMessage === true,
|
|
4251
|
+
orderBy,
|
|
4252
|
+
...(normalizedUpdatedAfter ? { updatedAfter: normalizedUpdatedAfter } : {}),
|
|
4253
|
+
...(query?.trim() ? { query } : {}),
|
|
4254
|
+
...(statuses ? { statuses: statuses as SessionStatus[] } : {}),
|
|
4255
|
+
activeOnly: activeOnly === true,
|
|
4256
|
+
...(recentHours !== undefined ? { recentHours } : {}),
|
|
4257
|
+
...(rootSessionId ? { rootSessionId } : {}),
|
|
4258
|
+
...(parentSessionId !== undefined ? { parentSessionId } : {}),
|
|
4259
|
+
...(subject ? { subject: subject as WorkClaimSubjectFilter } : {}),
|
|
4260
|
+
...(claimLimit !== undefined ? { claimLimit } : {}),
|
|
4261
|
+
includeWorkDiscovery: deps.settings.workDiscoveryEnabled,
|
|
4262
|
+
subjectId: grant.subjectId,
|
|
4263
|
+
...(authorizationScope ? { authorizationScope } : {}),
|
|
4264
|
+
});
|
|
4265
|
+
const result = capSessionDiscoveryPage(page, includeLastMessage === true);
|
|
4266
|
+
observeWorkDiscovery(deps.observability, {
|
|
4267
|
+
surface: "first_party_mcp",
|
|
4268
|
+
mode,
|
|
4269
|
+
outcome: result.sessions.length === 0 ? "empty" : "ok",
|
|
4270
|
+
authorizationScope: metricAuthorizationScope,
|
|
4271
|
+
durationMs: performance.now() - startedAtMs,
|
|
4272
|
+
responseBytes: result.bytes,
|
|
4273
|
+
...summarizeWorkDiscoveryRows(result.sessions),
|
|
4274
|
+
});
|
|
4275
|
+
return json(result);
|
|
4276
|
+
} catch (error) {
|
|
4277
|
+
observeWorkDiscovery(deps.observability, {
|
|
4278
|
+
surface: "first_party_mcp",
|
|
4279
|
+
mode,
|
|
4280
|
+
outcome: "error",
|
|
4281
|
+
authorizationScope: metricAuthorizationScope,
|
|
4282
|
+
durationMs: performance.now() - startedAtMs,
|
|
4283
|
+
responseBytes: 0,
|
|
4284
|
+
resultCount: 0,
|
|
4285
|
+
overlapCount: 0,
|
|
4286
|
+
matchCounts: {},
|
|
4287
|
+
});
|
|
4288
|
+
throw error;
|
|
4032
4289
|
}
|
|
4033
|
-
const page = await listSessionDiscoverySummaries(deps.db, grant.workspaceId, {
|
|
4034
|
-
limit: boundedSessionDiscoveryLimit(limit),
|
|
4035
|
-
...(decodedCursor ? { cursor: decodedCursor } : {}),
|
|
4036
|
-
includeLastMessage: includeLastMessage === true,
|
|
4037
|
-
orderBy,
|
|
4038
|
-
...(normalizedUpdatedAfter ? { updatedAfter: normalizedUpdatedAfter } : {}),
|
|
4039
|
-
...(authorizationScope ? { authorizationScope } : {}),
|
|
4040
|
-
});
|
|
4041
|
-
return json(capSessionDiscoveryPage(page, includeLastMessage === true));
|
|
4042
4290
|
},
|
|
4043
4291
|
);
|
|
4044
4292
|
|
|
@@ -4537,27 +4785,18 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4537
4785
|
controlled.authorization?.relatedSessionAccess ?? "root",
|
|
4538
4786
|
);
|
|
4539
4787
|
return json(
|
|
4540
|
-
|
|
4788
|
+
sessionControlMutationReceipt({
|
|
4541
4789
|
operation: "session_pause",
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
resource: {
|
|
4546
|
-
type: "session",
|
|
4547
|
-
id: sessionId,
|
|
4548
|
-
state: effectiveControl.state,
|
|
4549
|
-
},
|
|
4550
|
-
relatedResources: [{ type: "session_command_receipt", id: controlled.receipt.id }],
|
|
4790
|
+
sessionId,
|
|
4791
|
+
state: effectiveControl.state,
|
|
4792
|
+
receiptId: controlled.receipt.id,
|
|
4551
4793
|
timestamp: controlled.receipt.createdAt.toISOString(),
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
},
|
|
4555
|
-
facts: { interruptionCount: controlled.interruptionCount },
|
|
4556
|
-
nextAction: { tool: "session_get", arguments: { sessionId } },
|
|
4794
|
+
outcome: controlled.outcome,
|
|
4795
|
+
interruptionCount: controlled.interruptionCount,
|
|
4557
4796
|
}),
|
|
4558
4797
|
);
|
|
4559
4798
|
}
|
|
4560
|
-
const controlled = await
|
|
4799
|
+
const controlled = await controlHumanSessionWorkstreamWithOutcome(
|
|
4561
4800
|
deps,
|
|
4562
4801
|
{
|
|
4563
4802
|
accountId: grant.accountId,
|
|
@@ -4572,7 +4811,17 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4572
4811
|
...(reason ? { reason } : {}),
|
|
4573
4812
|
},
|
|
4574
4813
|
);
|
|
4575
|
-
return json(
|
|
4814
|
+
return json(
|
|
4815
|
+
sessionControlMutationReceipt({
|
|
4816
|
+
operation: "session_pause",
|
|
4817
|
+
sessionId,
|
|
4818
|
+
state: controlled.response.effectiveControl.state,
|
|
4819
|
+
receiptId: controlled.response.receipt.id,
|
|
4820
|
+
timestamp: controlled.response.receipt.createdAt,
|
|
4821
|
+
outcome: controlled.outcome,
|
|
4822
|
+
interruptionCount: controlled.response.interruptionCount,
|
|
4823
|
+
}),
|
|
4824
|
+
);
|
|
4576
4825
|
},
|
|
4577
4826
|
);
|
|
4578
4827
|
|
|
@@ -4605,27 +4854,18 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4605
4854
|
controlled.authorization?.relatedSessionAccess ?? "root",
|
|
4606
4855
|
);
|
|
4607
4856
|
return json(
|
|
4608
|
-
|
|
4857
|
+
sessionControlMutationReceipt({
|
|
4609
4858
|
operation: "session_resume",
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
resource: {
|
|
4614
|
-
type: "session",
|
|
4615
|
-
id: sessionId,
|
|
4616
|
-
state: effectiveControl.state,
|
|
4617
|
-
},
|
|
4618
|
-
relatedResources: [{ type: "session_command_receipt", id: controlled.receipt.id }],
|
|
4859
|
+
sessionId,
|
|
4860
|
+
state: effectiveControl.state,
|
|
4861
|
+
receiptId: controlled.receipt.id,
|
|
4619
4862
|
timestamp: controlled.receipt.createdAt.toISOString(),
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
},
|
|
4623
|
-
facts: { interruptionCount: controlled.interruptionCount },
|
|
4624
|
-
nextAction: { tool: "session_get", arguments: { sessionId } },
|
|
4863
|
+
outcome: controlled.outcome,
|
|
4864
|
+
interruptionCount: controlled.interruptionCount,
|
|
4625
4865
|
}),
|
|
4626
4866
|
);
|
|
4627
4867
|
}
|
|
4628
|
-
const controlled = await
|
|
4868
|
+
const controlled = await controlHumanSessionWorkstreamWithOutcome(
|
|
4629
4869
|
deps,
|
|
4630
4870
|
{
|
|
4631
4871
|
accountId: grant.accountId,
|
|
@@ -4640,7 +4880,17 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4640
4880
|
...(reason ? { reason } : {}),
|
|
4641
4881
|
},
|
|
4642
4882
|
);
|
|
4643
|
-
return json(
|
|
4883
|
+
return json(
|
|
4884
|
+
sessionControlMutationReceipt({
|
|
4885
|
+
operation: "session_resume",
|
|
4886
|
+
sessionId,
|
|
4887
|
+
state: controlled.response.effectiveControl.state,
|
|
4888
|
+
receiptId: controlled.response.receipt.id,
|
|
4889
|
+
timestamp: controlled.response.receipt.createdAt,
|
|
4890
|
+
outcome: controlled.outcome,
|
|
4891
|
+
interruptionCount: controlled.response.interruptionCount,
|
|
4892
|
+
}),
|
|
4893
|
+
);
|
|
4644
4894
|
},
|
|
4645
4895
|
);
|
|
4646
4896
|
|
|
@@ -4741,13 +4991,13 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4741
4991
|
});
|
|
4742
4992
|
}
|
|
4743
4993
|
if (accepted.action === "conflict") {
|
|
4744
|
-
throw new Error(
|
|
4994
|
+
throw new Error("human-input request is not currently actionable");
|
|
4745
4995
|
}
|
|
4746
4996
|
return json(
|
|
4747
4997
|
mcpMutationReceipt({
|
|
4748
4998
|
operation: "session_human_input_respond",
|
|
4749
4999
|
committed: true,
|
|
4750
|
-
outcome: accepted.
|
|
5000
|
+
outcome: accepted.action === "completed" ? "replayed" : "updated",
|
|
4751
5001
|
changed: accepted.events.length > 0,
|
|
4752
5002
|
resource: {
|
|
4753
5003
|
type: "session_human_input_request",
|
|
@@ -4757,7 +5007,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4757
5007
|
relatedResources: [{ type: "session", id: sessionId }],
|
|
4758
5008
|
timestamp: accepted.request.respondedAt ?? new Date().toISOString(),
|
|
4759
5009
|
idempotency: {
|
|
4760
|
-
status: accepted.
|
|
5010
|
+
status: accepted.action === "completed" ? "replayed" : "applied",
|
|
4761
5011
|
},
|
|
4762
5012
|
facts: { outcome: accepted.request.response?.outcome ?? null },
|
|
4763
5013
|
nextAction: { tool: "session_get", arguments: { sessionId } },
|
|
@@ -5464,10 +5714,12 @@ function boundedSessionDiscoveryLimit(limit: number | undefined): number {
|
|
|
5464
5714
|
}
|
|
5465
5715
|
|
|
5466
5716
|
export function encodeSessionDiscoveryCursor(cursor: SessionDiscoveryCursor): string {
|
|
5717
|
+
const relevance = cursor.orderBy === "relevance";
|
|
5467
5718
|
return Buffer.from(
|
|
5468
5719
|
JSON.stringify({
|
|
5469
|
-
v: 2,
|
|
5720
|
+
v: relevance ? 3 : 2,
|
|
5470
5721
|
orderBy: cursor.orderBy,
|
|
5722
|
+
...(relevance ? { sortRank: cursor.sortRank, filterHash: cursor.filterHash } : {}),
|
|
5471
5723
|
sortRevision: cursor.sortRevision,
|
|
5472
5724
|
sortAt: cursor.sortAt,
|
|
5473
5725
|
id: cursor.id,
|
|
@@ -5517,6 +5769,8 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
5517
5769
|
snapshotAt?: unknown;
|
|
5518
5770
|
snapshotRevision?: unknown;
|
|
5519
5771
|
updatedAfter?: unknown;
|
|
5772
|
+
sortRank?: unknown;
|
|
5773
|
+
filterHash?: unknown;
|
|
5520
5774
|
};
|
|
5521
5775
|
if (
|
|
5522
5776
|
parsed.v === undefined &&
|
|
@@ -5530,12 +5784,14 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
5530
5784
|
);
|
|
5531
5785
|
return {
|
|
5532
5786
|
orderBy: "createdAt",
|
|
5787
|
+
sortRank: null,
|
|
5533
5788
|
sortRevision: "0",
|
|
5534
5789
|
sortAt: createdAt,
|
|
5535
5790
|
id: parsed.id,
|
|
5536
5791
|
snapshotAt: createdAt,
|
|
5537
5792
|
snapshotRevision: "0",
|
|
5538
5793
|
updatedAfter: null,
|
|
5794
|
+
filterHash: null,
|
|
5539
5795
|
};
|
|
5540
5796
|
}
|
|
5541
5797
|
// The timestamp-fenced v1 format was never safe for updated-order
|
|
@@ -5552,17 +5808,22 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
5552
5808
|
) {
|
|
5553
5809
|
return {
|
|
5554
5810
|
orderBy: "createdAt",
|
|
5811
|
+
sortRank: null,
|
|
5555
5812
|
sortRevision: "0",
|
|
5556
5813
|
sortAt: normalizeSessionDiscoveryTimestamp(parsed.sortAt, "cursor sortAt"),
|
|
5557
5814
|
id: parsed.id,
|
|
5558
5815
|
snapshotAt: normalizeSessionDiscoveryTimestamp(parsed.snapshotAt, "cursor snapshotAt"),
|
|
5559
5816
|
snapshotRevision: "0",
|
|
5560
5817
|
updatedAfter: null,
|
|
5818
|
+
filterHash: null,
|
|
5561
5819
|
};
|
|
5562
5820
|
}
|
|
5821
|
+
const isV2 = parsed.v === 2;
|
|
5822
|
+
const isV3 = parsed.v === 3;
|
|
5563
5823
|
if (
|
|
5564
|
-
|
|
5565
|
-
(parsed.orderBy !== "createdAt" && parsed.orderBy !== "updatedAt") ||
|
|
5824
|
+
(!isV2 && !isV3) ||
|
|
5825
|
+
(isV2 && parsed.orderBy !== "createdAt" && parsed.orderBy !== "updatedAt") ||
|
|
5826
|
+
(isV3 && parsed.orderBy !== "relevance") ||
|
|
5566
5827
|
typeof parsed.sortRevision !== "string" ||
|
|
5567
5828
|
typeof parsed.sortAt !== "string" ||
|
|
5568
5829
|
typeof parsed.snapshotAt !== "string" ||
|
|
@@ -5573,6 +5834,18 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
5573
5834
|
) {
|
|
5574
5835
|
throw new Error("invalid cursor fields");
|
|
5575
5836
|
}
|
|
5837
|
+
if (
|
|
5838
|
+
isV3 &&
|
|
5839
|
+
(!Number.isSafeInteger(parsed.sortRank) ||
|
|
5840
|
+
(parsed.sortRank as number) < 0 ||
|
|
5841
|
+
typeof parsed.filterHash !== "string" ||
|
|
5842
|
+
!/^[0-9a-f]{64}$/.test(parsed.filterHash))
|
|
5843
|
+
) {
|
|
5844
|
+
throw new Error("invalid relevance cursor fields");
|
|
5845
|
+
}
|
|
5846
|
+
if (isV2 && (parsed.sortRank !== undefined || parsed.filterHash !== undefined)) {
|
|
5847
|
+
throw new Error("chronological cursor cannot carry relevance fields");
|
|
5848
|
+
}
|
|
5576
5849
|
const sortAt = normalizeSessionDiscoveryTimestamp(parsed.sortAt, "cursor sortAt");
|
|
5577
5850
|
const snapshotAt = normalizeSessionDiscoveryTimestamp(parsed.snapshotAt, "cursor snapshotAt");
|
|
5578
5851
|
const sortRevision = normalizeSessionDiscoveryRevision(
|
|
@@ -5593,14 +5866,19 @@ export function decodeSessionDiscoveryCursor(value: string): SessionDiscoveryCur
|
|
|
5593
5866
|
if (parsed.orderBy === "createdAt" && (sortRevision !== "0" || snapshotRevision !== "0")) {
|
|
5594
5867
|
throw new Error("creation cursor cannot carry activity revisions");
|
|
5595
5868
|
}
|
|
5869
|
+
const orderBy: SessionDiscoveryOrderBy = isV3
|
|
5870
|
+
? "relevance"
|
|
5871
|
+
: (parsed.orderBy as "createdAt" | "updatedAt");
|
|
5596
5872
|
return {
|
|
5597
|
-
orderBy
|
|
5873
|
+
orderBy,
|
|
5874
|
+
sortRank: isV3 ? (parsed.sortRank as number) : null,
|
|
5598
5875
|
sortRevision,
|
|
5599
5876
|
sortAt,
|
|
5600
5877
|
id: parsed.id,
|
|
5601
5878
|
snapshotAt,
|
|
5602
5879
|
snapshotRevision,
|
|
5603
5880
|
updatedAfter: normalizedUpdatedAfter,
|
|
5881
|
+
filterHash: isV3 ? (parsed.filterHash as string) : null,
|
|
5604
5882
|
};
|
|
5605
5883
|
} catch {
|
|
5606
5884
|
throw new Error("sessions_list cursor is invalid");
|
|
@@ -5691,6 +5969,7 @@ export function capSessionDiscoveryPage(
|
|
|
5691
5969
|
: null,
|
|
5692
5970
|
queuedPromptCount: session.queuedPromptCount,
|
|
5693
5971
|
children: session.treeStats,
|
|
5972
|
+
relatedWork: session.workDiscovery,
|
|
5694
5973
|
...(includeLastMessage
|
|
5695
5974
|
? {
|
|
5696
5975
|
latestMessage: session.latestMessage
|
|
@@ -5768,12 +6047,14 @@ export function capSessionDiscoveryPage(
|
|
|
5768
6047
|
? sourceLast
|
|
5769
6048
|
? encodeSessionDiscoveryCursor({
|
|
5770
6049
|
orderBy: page.orderBy,
|
|
6050
|
+
sortRank: sourceLast.sortRank,
|
|
5771
6051
|
sortRevision: sourceLast.sortRevision,
|
|
5772
6052
|
sortAt: sourceLast.sortAt,
|
|
5773
6053
|
id: sourceLast.id,
|
|
5774
6054
|
snapshotAt: page.snapshotAt,
|
|
5775
6055
|
snapshotRevision: page.snapshotRevision,
|
|
5776
6056
|
updatedAfter: page.updatedAfter,
|
|
6057
|
+
filterHash: page.filterHash,
|
|
5777
6058
|
})
|
|
5778
6059
|
: null
|
|
5779
6060
|
: page.nextCursor
|