@opengeni/api-router 2.6.4 → 2.10.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-grant-rls.d.ts +3 -0
- package/dist/app.d.ts +2 -1
- package/dist/app.js +3 -1
- package/dist/auth/organization-user-setup.d.ts +23 -0
- package/dist/{chunk-XTLI3CBH.js → chunk-XXH7DW34.js} +6866 -3554
- package/dist/chunk-XXH7DW34.js.map +1 -0
- package/dist/codemode.d.ts +6 -0
- package/dist/github-access.d.ts +25 -2
- package/dist/http/request-source.d.ts +14 -0
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/integrations/oauth-client.d.ts +2 -11
- package/dist/integrations/personal-github-repositories.d.ts +41 -2
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/server.d.ts +158 -1
- package/dist/mcp/session-view.d.ts +56 -2
- package/dist/mcp/session-wait.d.ts +1 -1
- package/dist/mcp-oauth.d.ts +19 -0
- package/dist/model-catalog.d.ts +5 -31
- package/dist/routes/codex.d.ts +27 -2
- package/dist/routes/github.d.ts +2 -0
- package/dist/routes/organization-model-providers.d.ts +3 -0
- package/dist/routes/workspace-artifacts.d.ts +2 -1
- package/dist/work-discovery-observability.d.ts +1 -1
- package/dist/workspace-artifact-content.d.ts +28 -0
- package/dist/workspace-artifact-provenance.d.ts +7 -0
- package/dist/workspace-deletion.d.ts +6 -0
- package/dist/workspace-tool-gateway-observability.d.ts +17 -0
- package/dist/workspace-tool-gateway.d.ts +118 -0
- package/package.json +19 -18
- package/src/access-grant-rls.ts +40 -0
- package/src/app.ts +296 -53
- package/src/auth/organization-user-setup.ts +95 -5
- package/src/codemode.ts +33 -4
- package/src/github-access.ts +117 -1
- package/src/http/auth.ts +11 -0
- package/src/http/request-source.ts +37 -0
- package/src/http/sse.ts +15 -7
- package/src/index.ts +18 -2
- package/src/integrations/oauth-client.ts +168 -203
- package/src/integrations/personal-github-repositories.ts +238 -9
- package/src/integrations/slack-app-home.ts +2 -2
- package/src/integrations/slack-interactions.ts +77 -37
- package/src/mcp/company-brain-governed-writes.ts +2 -2
- package/src/mcp/company-profile-agent-admin.ts +1 -1
- package/src/mcp/remember.ts +1 -1
- package/src/mcp/server.ts +504 -133
- package/src/mcp/session-view.ts +20 -1
- package/src/mcp/session-wait.ts +3 -0
- package/src/mcp-oauth.ts +539 -0
- package/src/model-catalog.ts +45 -337
- package/src/routes/automations.ts +178 -19
- package/src/routes/codex.ts +242 -73
- package/src/routes/connections.ts +271 -16
- package/src/routes/documents.ts +67 -19
- package/src/routes/files.ts +54 -6
- package/src/routes/github.ts +116 -15
- package/src/routes/organization-memberships.ts +59 -16
- package/src/routes/organization-model-providers.ts +231 -0
- package/src/routes/packs.ts +1 -0
- package/src/routes/personal-github.ts +39 -0
- package/src/routes/pr-review.ts +72 -4
- package/src/routes/scheduled-tasks.ts +40 -13
- package/src/routes/sessions.ts +153 -65
- package/src/routes/supergrok.ts +3 -2
- package/src/routes/workspace-artifacts.ts +176 -67
- package/src/routes/workspaces.ts +405 -62
- package/src/sandbox/channel-a.ts +17 -4
- package/src/work-discovery-observability.ts +3 -3
- package/src/workspace-artifact-content.ts +163 -0
- package/src/workspace-artifact-provenance.ts +104 -0
- package/src/workspace-deletion.ts +64 -0
- package/src/workspace-tool-gateway-observability.ts +100 -0
- package/src/workspace-tool-gateway.ts +691 -0
- package/dist/chunk-XTLI3CBH.js.map +0 -1
package/src/routes/pr-review.ts
CHANGED
|
@@ -13,12 +13,15 @@ import {
|
|
|
13
13
|
canonicalConfiguredModel,
|
|
14
14
|
defaultPrReviewProviderBaseUrl,
|
|
15
15
|
getCapabilityPack,
|
|
16
|
+
workspaceCustomModelReference,
|
|
17
|
+
lockActiveCustomModelForAdmission,
|
|
16
18
|
prReviewWebhookAuthKind,
|
|
17
19
|
normalizePrReviewProviderBaseUrl,
|
|
18
20
|
prReviewPackConnectorId,
|
|
19
21
|
PR_REVIEW_AUTOMATION_TEMPLATE_ID,
|
|
20
22
|
requireAccessGrant,
|
|
21
23
|
requirePermission,
|
|
24
|
+
resolveWorkspaceCatalogSettings,
|
|
22
25
|
type ApiRouteDeps,
|
|
23
26
|
} from "@opengeni/core";
|
|
24
27
|
import {
|
|
@@ -385,10 +388,24 @@ export function registerPrReviewRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
385
388
|
throw error;
|
|
386
389
|
}
|
|
387
390
|
}
|
|
391
|
+
const catalogSettings = (
|
|
392
|
+
await resolveWorkspaceCatalogSettings(db, deps.settings, {
|
|
393
|
+
accountId: grant.accountId,
|
|
394
|
+
workspaceId,
|
|
395
|
+
})
|
|
396
|
+
).settings;
|
|
388
397
|
const model = payload.model
|
|
389
|
-
? (canonicalConfiguredModel(
|
|
398
|
+
? (canonicalConfiguredModel(catalogSettings, payload.model) ?? null)
|
|
390
399
|
: null;
|
|
391
|
-
if (model) await assertWorkspaceModelPolicyAllows(db,
|
|
400
|
+
if (model) await assertWorkspaceModelPolicyAllows(db, catalogSettings, workspaceId, model);
|
|
401
|
+
const beforeCreateCommit = model
|
|
402
|
+
? prReviewCustomModelCommitGuard({
|
|
403
|
+
settings: catalogSettings,
|
|
404
|
+
accountId: grant.accountId,
|
|
405
|
+
workspaceId,
|
|
406
|
+
modelId: model,
|
|
407
|
+
})
|
|
408
|
+
: undefined;
|
|
392
409
|
const template = getCapabilityPack(OPENGENI_PR_REVIEW_PACK_ID)?.automationTemplates?.find(
|
|
393
410
|
(candidate) => candidate.id === PR_REVIEW_AUTOMATION_TEMPLATE_ID,
|
|
394
411
|
);
|
|
@@ -417,6 +434,7 @@ export function registerPrReviewRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
417
434
|
eventTypes: template.eventTypes,
|
|
418
435
|
configuration: template.configuration,
|
|
419
436
|
sessionTemplate: template.sessionTemplate,
|
|
437
|
+
...(beforeCreateCommit ? { beforeCreateCommit } : {}),
|
|
420
438
|
}),
|
|
421
439
|
"This repository is already bound to the selected PR Review registration",
|
|
422
440
|
);
|
|
@@ -441,13 +459,40 @@ export function registerPrReviewRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
441
459
|
const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
|
|
442
460
|
await requirePrReviewPackActive(db, workspaceId);
|
|
443
461
|
const payload = UpdatePrReviewRepositoryBindingRequest.parse(await c.req.json());
|
|
462
|
+
const catalogSettings = (
|
|
463
|
+
await resolveWorkspaceCatalogSettings(db, deps.settings, {
|
|
464
|
+
accountId: grant.accountId,
|
|
465
|
+
workspaceId,
|
|
466
|
+
})
|
|
467
|
+
).settings;
|
|
444
468
|
const model =
|
|
445
469
|
payload.model === undefined
|
|
446
470
|
? undefined
|
|
447
471
|
: payload.model === null
|
|
448
472
|
? null
|
|
449
|
-
: (canonicalConfiguredModel(
|
|
450
|
-
if (model) await assertWorkspaceModelPolicyAllows(db,
|
|
473
|
+
: (canonicalConfiguredModel(catalogSettings, payload.model) ?? null);
|
|
474
|
+
if (model) await assertWorkspaceModelPolicyAllows(db, catalogSettings, workspaceId, model);
|
|
475
|
+
const beforeUpdateCommit = async (
|
|
476
|
+
tx: ApiRouteDeps["db"],
|
|
477
|
+
context: {
|
|
478
|
+
currentModel: string | null;
|
|
479
|
+
currentStatus: "active" | "disabled";
|
|
480
|
+
nextModel: string | null;
|
|
481
|
+
nextStatus: "active" | "disabled";
|
|
482
|
+
},
|
|
483
|
+
): Promise<void> => {
|
|
484
|
+
const materialExecutionChange =
|
|
485
|
+
payload.model !== undefined ||
|
|
486
|
+
payload.additionalInstructions !== undefined ||
|
|
487
|
+
(context.currentStatus === "disabled" && context.nextStatus === "active");
|
|
488
|
+
if (!materialExecutionChange || !context.nextModel) return;
|
|
489
|
+
await prReviewCustomModelCommitGuard({
|
|
490
|
+
settings: catalogSettings,
|
|
491
|
+
accountId: grant.accountId,
|
|
492
|
+
workspaceId,
|
|
493
|
+
modelId: context.nextModel,
|
|
494
|
+
})?.(tx);
|
|
495
|
+
};
|
|
451
496
|
const binding = await updatePrReviewRepositoryBinding(db, {
|
|
452
497
|
accountId: grant.accountId,
|
|
453
498
|
workspaceId,
|
|
@@ -458,6 +503,7 @@ export function registerPrReviewRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
458
503
|
? { additionalInstructions: payload.additionalInstructions }
|
|
459
504
|
: {}),
|
|
460
505
|
...(payload.status !== undefined ? { status: payload.status } : {}),
|
|
506
|
+
beforeUpdateCommit,
|
|
461
507
|
});
|
|
462
508
|
if (!binding)
|
|
463
509
|
throw new HTTPException(404, {
|
|
@@ -501,6 +547,28 @@ export function registerPrReviewRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
501
547
|
});
|
|
502
548
|
}
|
|
503
549
|
|
|
550
|
+
function prReviewCustomModelCommitGuard(input: {
|
|
551
|
+
settings: ApiRouteDeps["settings"];
|
|
552
|
+
accountId: string;
|
|
553
|
+
workspaceId: string;
|
|
554
|
+
modelId: string;
|
|
555
|
+
}): ((tx: ApiRouteDeps["db"]) => Promise<void>) | undefined {
|
|
556
|
+
const reference = workspaceCustomModelReference(input.settings, input.modelId);
|
|
557
|
+
if (!reference) return undefined;
|
|
558
|
+
return async (tx): Promise<void> => {
|
|
559
|
+
const active = await lockActiveCustomModelForAdmission(tx, {
|
|
560
|
+
accountId: input.accountId,
|
|
561
|
+
workspaceId: input.workspaceId,
|
|
562
|
+
reference,
|
|
563
|
+
});
|
|
564
|
+
if (!active) {
|
|
565
|
+
throw new HTTPException(422, {
|
|
566
|
+
message: `model is not available: ${input.modelId}`,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
504
572
|
function assertPrReviewSandboxBackend(deps: ApiRouteDeps): void {
|
|
505
573
|
if (deps.settings.sandboxBackend === "selfhosted") {
|
|
506
574
|
throw new HTTPException(409, {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import { listScheduledTaskRuns, listScheduledTasks } from "@opengeni/db";
|
|
7
7
|
import type { Hono } from "hono";
|
|
8
8
|
import { HTTPException } from "hono/http-exception";
|
|
9
|
-
import { requireAccessGrant } from "@opengeni/core";
|
|
9
|
+
import { requireAccessGrant, resolveWorkspaceCatalogSettings } from "@opengeni/core";
|
|
10
10
|
import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
|
|
11
11
|
import type { ApiRouteDeps } from "@opengeni/core";
|
|
12
12
|
import {
|
|
@@ -30,7 +30,7 @@ import { boundedLimit } from "../http/common";
|
|
|
30
30
|
import { deleteScheduledTaskWithDurableCleanup } from "../scheduled-task-deletion";
|
|
31
31
|
|
|
32
32
|
export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
33
|
-
const {
|
|
33
|
+
const { db, workflowClient, objectStorage } = deps;
|
|
34
34
|
|
|
35
35
|
app.post("/v1/workspaces/:workspaceId/scheduled-tasks", async (c) => {
|
|
36
36
|
const workspaceId = c.req.param("workspaceId");
|
|
@@ -43,6 +43,12 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
const payload = parsedPayload.data;
|
|
46
|
+
const catalogSettings = (
|
|
47
|
+
await resolveWorkspaceCatalogSettings(db, deps.settings, {
|
|
48
|
+
accountId: grant.accountId,
|
|
49
|
+
workspaceId,
|
|
50
|
+
})
|
|
51
|
+
).settings;
|
|
46
52
|
await requireLimit(deps, {
|
|
47
53
|
accountId: grant.accountId,
|
|
48
54
|
workspaceId,
|
|
@@ -50,7 +56,7 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
50
56
|
quantity: 1,
|
|
51
57
|
});
|
|
52
58
|
const task = await createValidatedScheduledTask({
|
|
53
|
-
settings,
|
|
59
|
+
settings: catalogSettings,
|
|
54
60
|
db,
|
|
55
61
|
objectStorage,
|
|
56
62
|
grant,
|
|
@@ -91,8 +97,14 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
91
97
|
});
|
|
92
98
|
}
|
|
93
99
|
const payload = parsedPayload.data;
|
|
100
|
+
const catalogSettings = (
|
|
101
|
+
await resolveWorkspaceCatalogSettings(db, deps.settings, {
|
|
102
|
+
accountId: grant.accountId,
|
|
103
|
+
workspaceId,
|
|
104
|
+
})
|
|
105
|
+
).settings;
|
|
94
106
|
const update = await validatedScheduledTaskUpdate({
|
|
95
|
-
settings,
|
|
107
|
+
settings: catalogSettings,
|
|
96
108
|
db,
|
|
97
109
|
objectStorage,
|
|
98
110
|
grant,
|
|
@@ -124,8 +136,14 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
124
136
|
const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
125
137
|
const existing = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
126
138
|
const previous = await captureScheduledTaskRestoreState(db, existing);
|
|
139
|
+
const catalogSettings = (
|
|
140
|
+
await resolveWorkspaceCatalogSettings(db, deps.settings, {
|
|
141
|
+
accountId: grant.accountId,
|
|
142
|
+
workspaceId,
|
|
143
|
+
})
|
|
144
|
+
).settings;
|
|
127
145
|
const update = await validatedScheduledTaskUpdate({
|
|
128
|
-
settings,
|
|
146
|
+
settings: catalogSettings,
|
|
129
147
|
db,
|
|
130
148
|
objectStorage,
|
|
131
149
|
grant,
|
|
@@ -146,6 +164,12 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
146
164
|
// recognised as codex-billed and skip the credit/cost gates at the edge.
|
|
147
165
|
const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
148
166
|
if (task.action.kind === "agent_turn") {
|
|
167
|
+
const catalogSettings = (
|
|
168
|
+
await resolveWorkspaceCatalogSettings(db, deps.settings, {
|
|
169
|
+
accountId: grant.accountId,
|
|
170
|
+
workspaceId,
|
|
171
|
+
})
|
|
172
|
+
).settings;
|
|
149
173
|
await validateScheduledTaskTarget({
|
|
150
174
|
db,
|
|
151
175
|
sessionAuthorization: deps.sessionAuthorization,
|
|
@@ -159,20 +183,23 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
159
183
|
missingTargetStatus: 404,
|
|
160
184
|
});
|
|
161
185
|
await validateScheduledTaskMachineTarget({
|
|
162
|
-
settings,
|
|
186
|
+
settings: catalogSettings,
|
|
163
187
|
db,
|
|
164
188
|
grant,
|
|
165
189
|
runMode: task.runMode,
|
|
166
190
|
agentConfig: task.agentConfig,
|
|
167
191
|
requireOnline: true,
|
|
168
192
|
});
|
|
169
|
-
await requireLimit(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
193
|
+
await requireLimit(
|
|
194
|
+
{ ...deps, settings: catalogSettings },
|
|
195
|
+
{
|
|
196
|
+
accountId: grant.accountId,
|
|
197
|
+
workspaceId,
|
|
198
|
+
action: "agent_run:create",
|
|
199
|
+
quantity: 1,
|
|
200
|
+
model: task.agentConfig.model ?? catalogSettings.openaiModel,
|
|
201
|
+
},
|
|
202
|
+
);
|
|
176
203
|
}
|
|
177
204
|
// Body is optional (a bare POST is still a valid trigger); only a present,
|
|
178
205
|
// non-empty body must parse against the contract.
|
package/src/routes/sessions.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
GatewayRealtimeConnectRequest,
|
|
13
13
|
ClientSessionEvent,
|
|
14
14
|
CompactSessionContextRequest,
|
|
15
|
+
CreateSessionRequest,
|
|
15
16
|
DeleteSessionQueueItemRequest,
|
|
16
17
|
EditSessionQueueItemRequest,
|
|
17
18
|
EndSessionRealtimeRequest,
|
|
@@ -47,6 +48,7 @@ import {
|
|
|
47
48
|
SessionEventSemanticClass,
|
|
48
49
|
SessionEventType,
|
|
49
50
|
SessionMcpServerId,
|
|
51
|
+
SaveNewSessionDraftRequest,
|
|
50
52
|
compactSessionEventResult,
|
|
51
53
|
sessionEventLatestClassToSemanticClass,
|
|
52
54
|
SaveComposerDraftRequest,
|
|
@@ -106,10 +108,12 @@ import {
|
|
|
106
108
|
getRetainedProcess,
|
|
107
109
|
getSandbox,
|
|
108
110
|
getSession,
|
|
111
|
+
readActiveSandbox,
|
|
109
112
|
deleteSessionTreeIfQuiescent,
|
|
110
113
|
getSessionEvent,
|
|
111
114
|
getSessionForSubject,
|
|
112
115
|
getSessionGoal,
|
|
116
|
+
getLatestSessionModelContext,
|
|
113
117
|
getSessionHumanInputRequest,
|
|
114
118
|
getSessionGoalWithContinuation,
|
|
115
119
|
getSessionGoalRevision,
|
|
@@ -195,7 +199,7 @@ import {
|
|
|
195
199
|
import {
|
|
196
200
|
appendAndPublishEvents,
|
|
197
201
|
boundSessionEventHttpPage,
|
|
198
|
-
|
|
202
|
+
coalesceSessionEventDeltasWithCoverage,
|
|
199
203
|
publishDurableSessionEvents,
|
|
200
204
|
} from "@opengeni/events";
|
|
201
205
|
import {
|
|
@@ -216,6 +220,7 @@ import {
|
|
|
216
220
|
NatsControlRpc,
|
|
217
221
|
negotiateCapabilities,
|
|
218
222
|
negotiateSelfhostedCapabilities,
|
|
223
|
+
resolveConnectedMachineWorkspaceRoot,
|
|
219
224
|
selectBackend,
|
|
220
225
|
SelfhostedSession,
|
|
221
226
|
} from "@opengeni/runtime/sandbox";
|
|
@@ -231,6 +236,7 @@ import {
|
|
|
231
236
|
requirePermission,
|
|
232
237
|
requireSessionAuthorization,
|
|
233
238
|
requireSessionAuthorizationListScope,
|
|
239
|
+
resolveWorkspaceCatalogSettings,
|
|
234
240
|
withResolvedSessionAuthorization,
|
|
235
241
|
SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS,
|
|
236
242
|
SessionAuthorizationDeniedError,
|
|
@@ -549,6 +555,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
549
555
|
}
|
|
550
556
|
let session: Session;
|
|
551
557
|
try {
|
|
558
|
+
CreateSessionRequest.parse(payload);
|
|
552
559
|
session = await createSessionForRequest(deps, grant, workspaceId, payload, authorization);
|
|
553
560
|
} catch (error) {
|
|
554
561
|
return sessionCreateErrorResponse(c, error);
|
|
@@ -562,7 +569,13 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
562
569
|
app.get("/v1/workspaces/:workspaceId/new-session-draft", async (c) => {
|
|
563
570
|
const workspaceId = c.req.param("workspaceId");
|
|
564
571
|
const grant = await requireAccessGrant(c, deps, workspaceId, "sessions:read");
|
|
565
|
-
|
|
572
|
+
const catalog = await resolveWorkspaceCatalogSettings(db, settings, {
|
|
573
|
+
accountId: grant.accountId,
|
|
574
|
+
workspaceId,
|
|
575
|
+
});
|
|
576
|
+
return c.json(
|
|
577
|
+
await getActorNewSessionDraft({ settings: catalog.settings, db }, grant, workspaceId),
|
|
578
|
+
);
|
|
566
579
|
});
|
|
567
580
|
|
|
568
581
|
app.put("/v1/workspaces/:workspaceId/new-session-draft", async (c) => {
|
|
@@ -587,9 +600,14 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
587
600
|
);
|
|
588
601
|
}
|
|
589
602
|
try {
|
|
603
|
+
SaveNewSessionDraftRequest.parse(payload);
|
|
604
|
+
const catalog = await resolveWorkspaceCatalogSettings(db, settings, {
|
|
605
|
+
accountId: grant.accountId,
|
|
606
|
+
workspaceId,
|
|
607
|
+
});
|
|
590
608
|
return c.json(
|
|
591
609
|
await saveActorNewSessionDraft(
|
|
592
|
-
{ settings, db, objectStorage },
|
|
610
|
+
{ settings: catalog.settings, db, objectStorage },
|
|
593
611
|
grant,
|
|
594
612
|
workspaceId,
|
|
595
613
|
payload,
|
|
@@ -873,6 +891,32 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
873
891
|
return c.json(await withEffectivePolicy(deps, workspaceId, grant.subjectId, session));
|
|
874
892
|
});
|
|
875
893
|
|
|
894
|
+
app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/model-context", async (c) => {
|
|
895
|
+
const workspaceId = c.req.param("workspaceId");
|
|
896
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "sessions:read");
|
|
897
|
+
const sessionId = c.req.param("sessionId");
|
|
898
|
+
if (!z.string().uuid().safeParse(sessionId).success) {
|
|
899
|
+
throw new HTTPException(404, { message: "session not found" });
|
|
900
|
+
}
|
|
901
|
+
const session = await getSessionForSubject(
|
|
902
|
+
db,
|
|
903
|
+
workspaceId,
|
|
904
|
+
sessionId,
|
|
905
|
+
grant.subjectId,
|
|
906
|
+
relatedSessionAccessFor(c),
|
|
907
|
+
);
|
|
908
|
+
if (!session) {
|
|
909
|
+
throw new HTTPException(404, { message: "session not found" });
|
|
910
|
+
}
|
|
911
|
+
return c.json(
|
|
912
|
+
await getLatestSessionModelContext(db, {
|
|
913
|
+
accountId: grant.accountId,
|
|
914
|
+
workspaceId,
|
|
915
|
+
sessionId,
|
|
916
|
+
}),
|
|
917
|
+
);
|
|
918
|
+
});
|
|
919
|
+
|
|
876
920
|
app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/background-commands", async (c) => {
|
|
877
921
|
const workspaceId = c.req.param("workspaceId");
|
|
878
922
|
const sessionId = c.req.param("sessionId");
|
|
@@ -2606,10 +2650,16 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2606
2650
|
c.header("X-OpenGeni-Covered-Last", String(result.coveredSequence.last));
|
|
2607
2651
|
return c.json(result);
|
|
2608
2652
|
}
|
|
2609
|
-
const
|
|
2653
|
+
const compactProjection = compact ? coalesceSessionEventDeltasWithCoverage(events) : null;
|
|
2654
|
+
const projected = compactProjection?.events ?? events;
|
|
2655
|
+
const forensicExact =
|
|
2656
|
+
!compact && mode === "forensic" && payloadMode === "full" && dbPage.fullPayloadsExact;
|
|
2610
2657
|
const page = boundSessionEventHttpPage(projected, {
|
|
2611
2658
|
direction,
|
|
2612
|
-
eventProjection:
|
|
2659
|
+
eventProjection: forensicExact ? "exact" : "bounded",
|
|
2660
|
+
...(compactProjection
|
|
2661
|
+
? { coveredThroughBySequence: compactProjection.coveredThroughBySequence }
|
|
2662
|
+
: {}),
|
|
2613
2663
|
});
|
|
2614
2664
|
const hasMore = dbPage.hasMore || page.truncated;
|
|
2615
2665
|
c.header("X-OpenGeni-Page-Bytes", String(page.bytes));
|
|
@@ -2619,9 +2669,14 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2619
2669
|
c.header("X-OpenGeni-Event-Mode", mode);
|
|
2620
2670
|
c.header("X-OpenGeni-Event-Direction", direction);
|
|
2621
2671
|
c.header("X-OpenGeni-Payload-Mode", payloadMode);
|
|
2622
|
-
c.header("X-OpenGeni-Forensic-Exact", String(
|
|
2672
|
+
c.header("X-OpenGeni-Forensic-Exact", String(forensicExact));
|
|
2623
2673
|
const coveredFirst = page.events[0]?.sequence;
|
|
2624
|
-
const
|
|
2674
|
+
const coveredLastEvent = page.events.at(-1);
|
|
2675
|
+
const coveredLast =
|
|
2676
|
+
coveredLastEvent === undefined
|
|
2677
|
+
? undefined
|
|
2678
|
+
: (compactProjection?.coveredThroughBySequence.get(coveredLastEvent.sequence) ??
|
|
2679
|
+
coveredLastEvent.sequence);
|
|
2625
2680
|
if (coveredFirst !== undefined) c.header("X-OpenGeni-Covered-First", String(coveredFirst));
|
|
2626
2681
|
if (coveredLast !== undefined) c.header("X-OpenGeni-Covered-Last", String(coveredLast));
|
|
2627
2682
|
const truncatedBy = page.truncated ? "http_bytes" : dbPage.truncatedBy;
|
|
@@ -2898,28 +2953,33 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2898
2953
|
const sessionId = c.req.param("sessionId");
|
|
2899
2954
|
await assertSessionExists(db, workspaceId, sessionId);
|
|
2900
2955
|
const payload = parseSteerSessionAdmission(await c.req.json().catch(() => null));
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
? {
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2956
|
+
let result: Awaited<ReturnType<typeof acceptSessionUserMessage>>;
|
|
2957
|
+
try {
|
|
2958
|
+
result = await acceptSessionUserMessage(deps, grant, workspaceId, sessionId, {
|
|
2959
|
+
text: payload.text,
|
|
2960
|
+
annotations: payload.annotations,
|
|
2961
|
+
modelContext: payload.modelContext ?? null,
|
|
2962
|
+
resources: payload.resources,
|
|
2963
|
+
model: payload.model ?? null,
|
|
2964
|
+
reasoningEffort: payload.reasoningEffort ?? null,
|
|
2965
|
+
latencyMode: payload.latencyMode ?? null,
|
|
2966
|
+
mcpCredentialUpdates: payload.mcpCredentialUpdates ?? [],
|
|
2967
|
+
connectionAuthorities: payload.connectionAuthorities,
|
|
2968
|
+
...(payload.personalResourceAttachment
|
|
2969
|
+
? { personalResourceAttachment: payload.personalResourceAttachment }
|
|
2970
|
+
: {}),
|
|
2971
|
+
authorization,
|
|
2972
|
+
delivery: "steer",
|
|
2973
|
+
origin: "human",
|
|
2974
|
+
...(payload.controlEtag !== undefined ? { controlEtag: payload.controlEtag } : {}),
|
|
2975
|
+
...(payload.expectedDraftRevision !== undefined
|
|
2976
|
+
? { expectedDraftRevision: payload.expectedDraftRevision }
|
|
2977
|
+
: {}),
|
|
2978
|
+
...(payload.clientEventId ? { clientEventId: payload.clientEventId } : {}),
|
|
2979
|
+
});
|
|
2980
|
+
} catch (error) {
|
|
2981
|
+
return commandConflictResponse(c, error);
|
|
2982
|
+
}
|
|
2923
2983
|
return c.json(result, 202);
|
|
2924
2984
|
});
|
|
2925
2985
|
|
|
@@ -2975,29 +3035,34 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2975
3035
|
}
|
|
2976
3036
|
}
|
|
2977
3037
|
if (event.type === "user.message") {
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3038
|
+
let result: Awaited<ReturnType<typeof acceptSessionUserMessage>>;
|
|
3039
|
+
try {
|
|
3040
|
+
result = await acceptSessionUserMessage(deps, grant, workspaceId, sessionId, {
|
|
3041
|
+
text: event.payload.text,
|
|
3042
|
+
annotations: event.payload.annotations,
|
|
3043
|
+
modelContext: event.payload.modelContext ?? null,
|
|
3044
|
+
resources: event.payload.resources ?? [],
|
|
3045
|
+
model: event.payload.model ?? null,
|
|
3046
|
+
reasoningEffort: event.payload.reasoningEffort ?? null,
|
|
3047
|
+
latencyMode: event.payload.latencyMode ?? null,
|
|
3048
|
+
mcpCredentialUpdates: event.payload.mcpCredentialUpdates ?? [],
|
|
3049
|
+
connectionAuthorities: event.payload.connectionAuthorities,
|
|
3050
|
+
...(event.payload.personalResourceAttachment
|
|
3051
|
+
? { personalResourceAttachment: event.payload.personalResourceAttachment }
|
|
3052
|
+
: {}),
|
|
3053
|
+
authorization,
|
|
3054
|
+
...(event.payload.controlEtag !== undefined
|
|
3055
|
+
? { controlEtag: event.payload.controlEtag }
|
|
3056
|
+
: {}),
|
|
3057
|
+
...(event.payload.expectedDraftRevision !== undefined
|
|
3058
|
+
? { expectedDraftRevision: event.payload.expectedDraftRevision }
|
|
3059
|
+
: {}),
|
|
3060
|
+
...(event.clientEventId ? { clientEventId: event.clientEventId } : {}),
|
|
3061
|
+
});
|
|
3062
|
+
} catch (error) {
|
|
3063
|
+
return commandConflictResponse(c, error);
|
|
3064
|
+
}
|
|
3065
|
+
return c.json(result.accepted, 202);
|
|
3001
3066
|
}
|
|
3002
3067
|
|
|
3003
3068
|
if (event.type === "user.approvalDecision") {
|
|
@@ -3218,6 +3283,10 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
3218
3283
|
getEnrollment(db, grant, activeSandbox.enrollmentId),
|
|
3219
3284
|
getLiveEnrollmentConnection(db, grant, activeSandbox.enrollmentId),
|
|
3220
3285
|
]);
|
|
3286
|
+
const reportedWorkspaceRoot = liveConnection?.workspaceRoot ?? enrollment?.workspaceRoot;
|
|
3287
|
+
const effectiveWorkspaceRoot = reportedWorkspaceRoot
|
|
3288
|
+
? resolveConnectedMachineWorkspaceRoot(reportedWorkspaceRoot, session.workingDir)
|
|
3289
|
+
: null;
|
|
3221
3290
|
let probeResponded = false;
|
|
3222
3291
|
if (liveConnection?.connectionInstanceId) {
|
|
3223
3292
|
const machine = new SelfhostedSession({
|
|
@@ -3226,7 +3295,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
3226
3295
|
connectionInstanceId: liveConnection.connectionInstanceId,
|
|
3227
3296
|
// Capability negotiation only pings, so a pre-root agent may still
|
|
3228
3297
|
// report upgrade guidance without exposing a false filesystem root.
|
|
3229
|
-
workspaceRoot: liveConnection.workspaceRoot ?? "/",
|
|
3298
|
+
workspaceRoot: effectiveWorkspaceRoot ?? liveConnection.workspaceRoot ?? "/",
|
|
3230
3299
|
controlRpc: new NatsControlRpc(async () => bus.getRequestConnection()),
|
|
3231
3300
|
relay: relayConfigFromSettings(settings),
|
|
3232
3301
|
epoch: session.activeEpoch,
|
|
@@ -3242,7 +3311,10 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
3242
3311
|
...commonNegotiation,
|
|
3243
3312
|
os: enrollment?.os ?? session.sandboxOs,
|
|
3244
3313
|
leaseEpoch: session.activeEpoch,
|
|
3245
|
-
enrollment
|
|
3314
|
+
enrollment:
|
|
3315
|
+
enrollment && effectiveWorkspaceRoot
|
|
3316
|
+
? { ...enrollment, workspaceRoot: effectiveWorkspaceRoot }
|
|
3317
|
+
: enrollment,
|
|
3246
3318
|
probeResponded,
|
|
3247
3319
|
});
|
|
3248
3320
|
} else {
|
|
@@ -3291,17 +3363,17 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
3291
3363
|
...capabilities,
|
|
3292
3364
|
FileSystem: {
|
|
3293
3365
|
...capabilities.FileSystem,
|
|
3294
|
-
root:
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3366
|
+
root: selfhostedActive
|
|
3367
|
+
? capabilities.FileSystem.root
|
|
3368
|
+
: selectBackend(
|
|
3369
|
+
(activeSandbox?.kind === "modal"
|
|
3370
|
+
? "modal"
|
|
3371
|
+
: session.sandboxBackend === "selfhosted" &&
|
|
3372
|
+
settings.sandboxBackend !== "selfhosted" &&
|
|
3373
|
+
settings.sandboxBackend !== "none"
|
|
3374
|
+
? settings.sandboxBackend
|
|
3375
|
+
: session.sandboxBackend) as SandboxBackend,
|
|
3376
|
+
).workspaceRoot,
|
|
3305
3377
|
},
|
|
3306
3378
|
Git: {
|
|
3307
3379
|
...capabilities.Git,
|
|
@@ -3318,6 +3390,21 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
3318
3390
|
DesktopStream: { ...capabilities.DesktopStream, ...wire },
|
|
3319
3391
|
};
|
|
3320
3392
|
}
|
|
3393
|
+
if (selfhostedActive) {
|
|
3394
|
+
const currentPointer = await readActiveSandbox(db, workspaceId, sessionId);
|
|
3395
|
+
if (
|
|
3396
|
+
!currentPointer ||
|
|
3397
|
+
currentPointer.activeSandboxId !== session.activeSandboxId ||
|
|
3398
|
+
currentPointer.activeEpoch !== session.activeEpoch ||
|
|
3399
|
+
currentPointer.workingDir !== session.workingDir
|
|
3400
|
+
) {
|
|
3401
|
+
throw new ApiHttpError(409, {
|
|
3402
|
+
code: "conflict",
|
|
3403
|
+
message: "sandbox route changed while capabilities were being negotiated; retry",
|
|
3404
|
+
retryable: true,
|
|
3405
|
+
});
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3321
3408
|
return c.json(responseCapabilities);
|
|
3322
3409
|
});
|
|
3323
3410
|
|
|
@@ -4290,6 +4377,7 @@ export function sessionAuthorizationOperationForHttp(
|
|
|
4290
4377
|
}
|
|
4291
4378
|
if (suffix === "/lineage" && verb === "GET") return "session.lineage.read";
|
|
4292
4379
|
if (suffix === "/background-commands" && verb === "GET") return "session.read";
|
|
4380
|
+
if (suffix === "/model-context" && verb === "GET") return "session.read";
|
|
4293
4381
|
if (/^\/background-commands\/[^/]+$/.test(suffix) && verb === "DELETE") {
|
|
4294
4382
|
return "session.control";
|
|
4295
4383
|
}
|
package/src/routes/supergrok.ts
CHANGED
|
@@ -552,8 +552,9 @@ export function registerSuperGrokRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
552
552
|
} catch {
|
|
553
553
|
valid = false;
|
|
554
554
|
}
|
|
555
|
-
const
|
|
556
|
-
|
|
555
|
+
const catalogSettings = valid ? (await deps.resolveCatalogSettings()).settings : null;
|
|
556
|
+
const catalog = catalogSettings
|
|
557
|
+
? configuredModels(withXaiSubscriptionCatalogProvider(catalogSettings))
|
|
557
558
|
.filter(
|
|
558
559
|
(model) =>
|
|
559
560
|
model.credentialSource.kind === "connected_subscription" &&
|