@opengeni/core 0.21.10 → 0.28.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/index.d.ts +1 -1
- package/dist/application/new-session-drafts.d.ts +1 -1
- package/dist/application/session-commands.d.ts +2 -2
- package/dist/billing/limits.d.ts +1 -1
- package/dist/canonical-human-identities.d.ts +12 -0
- package/dist/canonical-human-identities.js +23 -0
- package/dist/canonical-human-identities.js.map +1 -0
- package/dist/chunk-6WKPWE5S.js +2380 -0
- package/dist/chunk-6WKPWE5S.js.map +1 -0
- package/dist/chunk-IBOEYG6N.js +34 -0
- package/dist/chunk-IBOEYG6N.js.map +1 -0
- package/dist/chunk-JJU6I5XD.js +2713 -0
- package/dist/chunk-JJU6I5XD.js.map +1 -0
- package/dist/chunk-NYPMQ7NO.js +2270 -0
- package/dist/chunk-NYPMQ7NO.js.map +1 -0
- package/dist/dependencies.d.ts +30 -6
- package/dist/domain/capabilities.d.ts +28 -3
- package/dist/domain/company-profile-durable-learning-adapter.d.ts +39 -0
- package/dist/domain/conversation-integrations.d.ts +225 -0
- package/dist/domain/durable-learning-slack-publication.d.ts +72 -0
- package/dist/domain/editable-artifacts/agent-application.d.ts +192 -0
- package/dist/domain/editable-artifacts/durable-export-postgres.d.ts +4 -0
- package/dist/domain/editable-artifacts/durable-export.d.ts +212 -0
- package/dist/domain/editable-artifacts/errors.d.ts +47 -0
- package/dist/domain/editable-artifacts/hash.d.ts +11 -0
- package/dist/domain/editable-artifacts/in-memory.d.ts +99 -0
- package/dist/domain/editable-artifacts/index.d.ts +14 -0
- package/dist/domain/editable-artifacts/ports.d.ts +341 -0
- package/dist/domain/editable-artifacts/postgres-adapters.d.ts +13 -0
- package/dist/domain/editable-artifacts/service.d.ts +105 -0
- package/dist/domain/editable-artifacts/snapshot-compaction.d.ts +44 -0
- package/dist/domain/editable-artifacts/snapshot-verifier-genesis.d.ts +50 -0
- package/dist/domain/editable-artifacts/snapshot-verifier-materialization.d.ts +21 -0
- package/dist/domain/editable-artifacts/snapshot-verifier.d.ts +118 -0
- package/dist/domain/editable-artifacts/types.d.ts +410 -0
- package/dist/domain/fiken.d.ts +35 -0
- package/dist/domain/memory-slack-delivery.d.ts +19 -0
- package/dist/domain/packs.d.ts +29 -1
- package/dist/domain/personal-connection-delegations.d.ts +9 -1
- package/dist/domain/scheduled-tasks.d.ts +2 -2
- package/dist/domain/session-tool-policy.d.ts +2 -2
- package/dist/domain/sessions.d.ts +24 -8
- package/dist/domain/skill-imports.d.ts +32 -0
- package/dist/domain/slack-publication-secret-safety.d.ts +8 -0
- package/dist/domain/timeline-annotations.d.ts +5 -0
- package/dist/domain/video-generation-capabilities.d.ts +10 -0
- package/dist/domain/video-generation.d.ts +47 -0
- package/dist/editable-artifact-live/application.d.ts +69 -0
- package/dist/editable-artifact-live/errors.d.ts +1 -0
- package/dist/editable-artifact-live/index.d.ts +8 -0
- package/dist/editable-artifact-live/outbox-dispatcher.d.ts +162 -0
- package/dist/editable-artifact-live/ports.d.ts +100 -0
- package/dist/editable-artifact-live/server.d.ts +71 -0
- package/dist/editable-artifact-live/ticket.d.ts +40 -0
- package/dist/editable-artifact-live/types.d.ts +213 -0
- package/dist/editable-artifact-live/wire.d.ts +34 -0
- package/dist/editable-artifact-live.d.ts +2 -0
- package/dist/editable-artifact-live.js +58 -0
- package/dist/editable-artifact-live.js.map +1 -0
- package/dist/editable-artifacts.d.ts +2 -0
- package/dist/editable-artifacts.js +179 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/index.d.ts +37 -26
- package/dist/index.js +4145 -959
- package/dist/index.js.map +1 -1
- package/dist/managed-session.d.ts +6 -2
- package/dist/rigs/index.d.ts +1 -1
- package/dist/rigs/provider-images.d.ts +31 -0
- package/dist/sandbox/fleet.d.ts +8 -5
- package/dist/sandbox/routing.d.ts +1 -0
- package/dist/session-authorization.d.ts +4 -2
- package/dist/transcription.d.ts +5 -0
- package/dist/workflow-wake-contract.d.ts +7 -0
- package/package.json +22 -10
- package/src/access/index.ts +14 -2
- package/src/application/new-session-drafts.ts +2 -0
- package/src/application/session-commands.ts +188 -107
- package/src/canonical-human-identities.ts +27 -0
- package/src/dependencies.ts +34 -2
- package/src/domain/capabilities.ts +729 -408
- package/src/domain/company-profile-durable-learning-adapter.ts +221 -0
- package/src/domain/conversation-integrations.ts +1238 -0
- package/src/domain/durable-learning-slack-publication.ts +202 -0
- package/src/domain/editable-artifacts/agent-application.ts +687 -0
- package/src/domain/editable-artifacts/durable-export-postgres.ts +139 -0
- package/src/domain/editable-artifacts/durable-export.ts +734 -0
- package/src/domain/editable-artifacts/errors.ts +115 -0
- package/src/domain/editable-artifacts/hash.ts +131 -0
- package/src/domain/editable-artifacts/in-memory.ts +1662 -0
- package/src/domain/editable-artifacts/index.ts +14 -0
- package/src/domain/editable-artifacts/ports.ts +462 -0
- package/src/domain/editable-artifacts/postgres-adapters.ts +49 -0
- package/src/domain/editable-artifacts/service.ts +2402 -0
- package/src/domain/editable-artifacts/snapshot-compaction.ts +246 -0
- package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +235 -0
- package/src/domain/editable-artifacts/snapshot-verifier-materialization.ts +74 -0
- package/src/domain/editable-artifacts/snapshot-verifier.ts +687 -0
- package/src/domain/editable-artifacts/types.ts +835 -0
- package/src/domain/fiken.ts +88 -0
- package/src/domain/insights.ts +163 -50
- package/src/domain/memory-slack-delivery.ts +204 -0
- package/src/domain/memory-slack-publication.ts +5 -2
- package/src/domain/packs.ts +266 -5
- package/src/domain/personal-connection-delegations.ts +97 -6
- package/src/domain/scheduled-tasks.ts +171 -29
- package/src/domain/session-tool-policy.ts +8 -2
- package/src/domain/sessions.ts +395 -148
- package/src/domain/skill-imports.ts +350 -0
- package/src/domain/slack-publication-secret-safety.ts +42 -0
- package/src/domain/timeline-annotations.ts +204 -0
- package/src/domain/video-generation-capabilities.ts +104 -0
- package/src/domain/video-generation.ts +249 -0
- package/src/editable-artifact-live/application.ts +182 -0
- package/src/editable-artifact-live/errors.ts +4 -0
- package/src/editable-artifact-live/index.ts +8 -0
- package/src/editable-artifact-live/outbox-dispatcher.ts +916 -0
- package/src/editable-artifact-live/ports.ts +139 -0
- package/src/editable-artifact-live/server.ts +1707 -0
- package/src/editable-artifact-live/ticket.ts +217 -0
- package/src/editable-artifact-live/types.ts +286 -0
- package/src/editable-artifact-live/wire.ts +95 -0
- package/src/editable-artifact-live.ts +2 -0
- package/src/editable-artifacts.ts +2 -0
- package/src/index.ts +15 -0
- package/src/managed-session.ts +19 -2
- package/src/rigs/index.ts +26 -19
- package/src/rigs/provider-images.ts +110 -0
- package/src/sandbox/fleet.ts +72 -66
- package/src/sandbox/routing.ts +6 -3
- package/src/session-authorization.ts +187 -52
- package/src/transcription.ts +5 -0
- package/src/workflow-wake-contract.ts +8 -0
package/src/domain/sessions.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { CODEX_MODEL_ID_PREFIX, isCodexBilledModel } from "@opengeni/codex";
|
|
|
2
2
|
import {
|
|
3
3
|
canonicalizeConfiguredModelId,
|
|
4
4
|
configuredAllowedModels,
|
|
5
|
+
resolveFirstPartyMcpToolPolicy,
|
|
5
6
|
policyProviderIdForModel,
|
|
6
7
|
resolveTurnExecutionPolicyV1,
|
|
7
8
|
WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
|
|
9
|
+
XAI_SUBSCRIPTION_MODEL_ID_PREFIX,
|
|
8
10
|
type Settings,
|
|
9
11
|
} from "@opengeni/config";
|
|
10
12
|
import {
|
|
@@ -35,6 +37,8 @@ import {
|
|
|
35
37
|
type SessionMcpCredentialUpdateInput,
|
|
36
38
|
type SessionMcpServerInput,
|
|
37
39
|
type SessionMcpServerMetadata,
|
|
40
|
+
type SubmittedTimelineAnnotation,
|
|
41
|
+
type TimelineAnnotation,
|
|
38
42
|
type UpdateSessionMcpApprovalPolicyResponse,
|
|
39
43
|
type UpdateSessionToolPolicyRequest,
|
|
40
44
|
type SessionAuthorizationPort,
|
|
@@ -44,6 +48,7 @@ import {
|
|
|
44
48
|
type TurnInitiator,
|
|
45
49
|
type TurnInitiatorContext,
|
|
46
50
|
type TurnExecutionPolicyV1,
|
|
51
|
+
type XaiProviderAccountAuthoritySnapshotV1,
|
|
47
52
|
} from "@opengeni/contracts";
|
|
48
53
|
import {
|
|
49
54
|
createSession,
|
|
@@ -51,6 +56,7 @@ import {
|
|
|
51
56
|
encryptVariableSetValue,
|
|
52
57
|
getAnySessionInGroup,
|
|
53
58
|
getEnrollment,
|
|
59
|
+
getChannel,
|
|
54
60
|
getRig,
|
|
55
61
|
getWorkspaceDefaultRigId,
|
|
56
62
|
listDistinctVariableSetIdsInGroup,
|
|
@@ -59,10 +65,10 @@ import {
|
|
|
59
65
|
getSession,
|
|
60
66
|
SessionIdConflictError,
|
|
61
67
|
getSessionSpawnDenialByIdempotencyKey,
|
|
62
|
-
getSessionEvent,
|
|
63
68
|
getWorkspaceControlEvent,
|
|
64
69
|
getSessionLineage,
|
|
65
70
|
getSessionTurn,
|
|
71
|
+
getSessionTurnXaiProviderAccountAuthoritySnapshot,
|
|
66
72
|
getWorkspaceModelPolicy,
|
|
67
73
|
initializeSessionStartAtomically,
|
|
68
74
|
listSessionTurns,
|
|
@@ -71,12 +77,13 @@ import {
|
|
|
71
77
|
submitHumanPromptInTransaction,
|
|
72
78
|
appendSessionEventsWithLockedSessionUpdate,
|
|
73
79
|
updateSessionTitle as updateSessionTitleRow,
|
|
74
|
-
|
|
80
|
+
withWorkspaceSubjectSessionActivityRls,
|
|
75
81
|
type CreateSessionMcpServerInput,
|
|
76
82
|
type Database,
|
|
77
83
|
type UpdateSessionMcpServerCredentialsInput,
|
|
78
84
|
QueueCommandConflictError,
|
|
79
85
|
AgentCommandAuthorityError,
|
|
86
|
+
runIdempotentPersistenceTransaction,
|
|
80
87
|
SessionSpawnDeniedDbError,
|
|
81
88
|
SessionControlConflictError,
|
|
82
89
|
SessionToolPolicyVersionConflictError,
|
|
@@ -96,9 +103,13 @@ import type {
|
|
|
96
103
|
ApiRouteDeps,
|
|
97
104
|
SessionWorkflowClient,
|
|
98
105
|
} from "../dependencies";
|
|
99
|
-
import {
|
|
106
|
+
import {
|
|
107
|
+
requireSessionAuthorization,
|
|
108
|
+
SessionAuthorizationDeniedError,
|
|
109
|
+
} from "../session-authorization";
|
|
100
110
|
import { swapActiveSandbox, type FleetContext } from "../sandbox/fleet";
|
|
101
111
|
import { settingsWithEnabledCapabilityMcpServers } from "./capabilities";
|
|
112
|
+
import { validateSubmittedTimelineAnnotations } from "./timeline-annotations";
|
|
102
113
|
import { requireVariableSetEncryption, validateVariableSetAttachment } from "./environments";
|
|
103
114
|
import {
|
|
104
115
|
freezePersonalConnectionDelegations,
|
|
@@ -145,10 +156,18 @@ export class SessionSpawnDeniedError extends Error {
|
|
|
145
156
|
export function resolveFirstPartyMcpToolsForCreate(
|
|
146
157
|
requested: FirstPartyMcpToolName[] | undefined,
|
|
147
158
|
parentStored: FirstPartyMcpToolName[] | null | undefined,
|
|
159
|
+
policy: {
|
|
160
|
+
default: readonly FirstPartyMcpToolName[];
|
|
161
|
+
allowed: readonly FirstPartyMcpToolName[];
|
|
162
|
+
} = {
|
|
163
|
+
default: DEFAULT_FIRST_PARTY_MCP_TOOLS,
|
|
164
|
+
allowed: FIRST_PARTY_MCP_TOOL_NAMES,
|
|
165
|
+
},
|
|
148
166
|
): FirstPartyMcpToolName[] {
|
|
149
167
|
if (requested !== undefined) return [...requested];
|
|
150
|
-
|
|
151
|
-
|
|
168
|
+
const allowed = new Set(policy.allowed);
|
|
169
|
+
const inherited = parentStored === undefined ? policy.default : (parentStored ?? policy.default);
|
|
170
|
+
return [...inherited].filter((tool) => allowed.has(tool));
|
|
152
171
|
}
|
|
153
172
|
|
|
154
173
|
function sessionSpawnDeniedMessage(denial: SessionSpawnDenial): string {
|
|
@@ -241,7 +260,9 @@ export function creationInitiatorForGrant(grant: AccessGrant): FrozenCreationIni
|
|
|
241
260
|
!Number.isSafeInteger(callerExecutionGeneration) ||
|
|
242
261
|
callerExecutionGeneration < 1
|
|
243
262
|
) {
|
|
244
|
-
throw new HTTPException(403, {
|
|
263
|
+
throw new HTTPException(403, {
|
|
264
|
+
message: "caller attempt claims are incomplete",
|
|
265
|
+
});
|
|
245
266
|
}
|
|
246
267
|
const actor = {
|
|
247
268
|
type: "agent_attempt",
|
|
@@ -284,11 +305,15 @@ function normalizedSessionMcpCredentialHeaders(
|
|
|
284
305
|
const seen = new Set<string>();
|
|
285
306
|
for (const [name, value] of entries) {
|
|
286
307
|
if (!sessionMcpCredentialHeaderName.test(name)) {
|
|
287
|
-
throw new HTTPException(422, {
|
|
308
|
+
throw new HTTPException(422, {
|
|
309
|
+
message: `invalid credential header name: ${name}`,
|
|
310
|
+
});
|
|
288
311
|
}
|
|
289
312
|
const lower = name.toLowerCase();
|
|
290
313
|
if (seen.has(lower)) {
|
|
291
|
-
throw new HTTPException(422, {
|
|
314
|
+
throw new HTTPException(422, {
|
|
315
|
+
message: `duplicate credential header name: ${name}`,
|
|
316
|
+
});
|
|
292
317
|
}
|
|
293
318
|
seen.add(lower);
|
|
294
319
|
if (value.length === 0 || value.length > maxSessionMcpCredentialHeaderValueLength) {
|
|
@@ -388,11 +413,15 @@ function validateSessionMcpServersForCreate(
|
|
|
388
413
|
const metadata: SessionMcpServerMetadata[] = [];
|
|
389
414
|
for (const server of servers) {
|
|
390
415
|
if (seenIds.has(server.id)) {
|
|
391
|
-
throw new HTTPException(422, {
|
|
416
|
+
throw new HTTPException(422, {
|
|
417
|
+
message: `duplicate session MCP server id: ${server.id}`,
|
|
418
|
+
});
|
|
392
419
|
}
|
|
393
420
|
seenIds.add(server.id);
|
|
394
421
|
if (reservedSessionMcpServerIds.has(server.id) || existingIds.has(server.id)) {
|
|
395
|
-
throw new HTTPException(422, {
|
|
422
|
+
throw new HTTPException(422, {
|
|
423
|
+
message: `MCP server id already exists: ${server.id}`,
|
|
424
|
+
});
|
|
396
425
|
}
|
|
397
426
|
const headers = normalizedSessionMcpCredentialHeaders(server.headers);
|
|
398
427
|
const headersEncrypted = Object.fromEntries(
|
|
@@ -490,7 +519,9 @@ function validateSessionMcpCredentialUpdates(input: {
|
|
|
490
519
|
}
|
|
491
520
|
seenIds.add(update.id);
|
|
492
521
|
if (!knownIds.has(update.id)) {
|
|
493
|
-
throw new HTTPException(422, {
|
|
522
|
+
throw new HTTPException(422, {
|
|
523
|
+
message: `unknown session MCP server id: ${update.id}`,
|
|
524
|
+
});
|
|
494
525
|
}
|
|
495
526
|
const headers = normalizedSessionMcpCredentialHeaders(update.headers);
|
|
496
527
|
return {
|
|
@@ -525,7 +556,10 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
525
556
|
requestedSessionId?: string;
|
|
526
557
|
db: Database;
|
|
527
558
|
bus: EventBus;
|
|
528
|
-
workflowClient: SessionWorkflowClient
|
|
559
|
+
workflowClient: Pick<SessionWorkflowClient, "wakeSessionWorkflow">;
|
|
560
|
+
/** Internal database-only composition seam. The exact session shell and this
|
|
561
|
+
* linkage commit together before its first event/turn can be initialized. */
|
|
562
|
+
beforeCreateCommit?: (tx: Database, sessionId: string) => Promise<void>;
|
|
529
563
|
accountId: string;
|
|
530
564
|
workspaceId: string;
|
|
531
565
|
initialMessage: string;
|
|
@@ -557,6 +591,9 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
557
591
|
// rig promote never moves an existing session's version.
|
|
558
592
|
rigId?: string | null;
|
|
559
593
|
rigVersionId?: string | null;
|
|
594
|
+
// The workspace channel the session is filed under (rail organization only;
|
|
595
|
+
// resolved workspace-scoped by the caller). Null/omitted ⇒ unfiled (inbox).
|
|
596
|
+
channelId?: string | null;
|
|
560
597
|
goal?: GoalSpec | null;
|
|
561
598
|
// Per-session agent persona/system instructions (org-visible metadata, not a
|
|
562
599
|
// secret). Persisted on the session row and composed system-level AFTER the
|
|
@@ -576,6 +613,7 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
576
613
|
mcpServers?: CreateSessionMcpServerInput[];
|
|
577
614
|
sessionMcpServers?: SessionMcpServerMetadata[];
|
|
578
615
|
personalConnectionDelegations?: McpPersonalConnectionDelegation[];
|
|
616
|
+
xaiProviderAccountAuthoritySnapshot?: XaiProviderAccountAuthoritySnapshotV1;
|
|
579
617
|
// The manager session spawning this worker (a worker-signed sessionId claim
|
|
580
618
|
// on the creating grant); null for direct API creates and scheduled runs.
|
|
581
619
|
// When set, the worker's terminal-for-now transitions wake this parent.
|
|
@@ -602,10 +640,17 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
602
640
|
// target fails the create (422) — never a silent fall-back to the default box.
|
|
603
641
|
// `workingDir` (optional) is the path/cwd base the chosen machine runs under,
|
|
604
642
|
// seeded alongside the pointer through the epoch-fenced CAS.
|
|
605
|
-
seedTargetSandbox?: {
|
|
643
|
+
seedTargetSandbox?: {
|
|
644
|
+
sandboxId: string;
|
|
645
|
+
settings: Settings;
|
|
646
|
+
workingDir?: string | null;
|
|
647
|
+
} | null;
|
|
606
648
|
// Exact actor-private pre-session draft represented by this create. The
|
|
607
649
|
// initializer consumes it only after the first durable runnable unit commits.
|
|
608
|
-
consumeNewSessionDraft?: {
|
|
650
|
+
consumeNewSessionDraft?: {
|
|
651
|
+
subjectId: string;
|
|
652
|
+
expectedRevision: number;
|
|
653
|
+
} | null;
|
|
609
654
|
// A child may lower its inherited nested-agent depth limit freely; increases
|
|
610
655
|
// are authorized by the caller's workspace:admin grant and checked again by
|
|
611
656
|
// the database admission transaction.
|
|
@@ -643,6 +688,7 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
643
688
|
variableSetId: input.variableSet?.id ?? null,
|
|
644
689
|
rigId: input.rigId ?? null,
|
|
645
690
|
rigVersionId: input.rigVersionId ?? null,
|
|
691
|
+
channelId: input.channelId ?? null,
|
|
646
692
|
firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
|
|
647
693
|
firstPartyMcpTools: input.firstPartyMcpTools,
|
|
648
694
|
instructions: input.instructions ?? null,
|
|
@@ -653,9 +699,15 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
653
699
|
...(input.sandboxOs ? { sandboxOs: input.sandboxOs } : {}),
|
|
654
700
|
mcpServers: input.mcpServers ?? [],
|
|
655
701
|
personalConnectionDelegations: input.personalConnectionDelegations ?? [],
|
|
702
|
+
...(input.xaiProviderAccountAuthoritySnapshot
|
|
703
|
+
? {
|
|
704
|
+
initialXaiProviderAccountAuthoritySnapshot: input.xaiProviderAccountAuthoritySnapshot,
|
|
705
|
+
}
|
|
706
|
+
: {}),
|
|
656
707
|
maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
|
|
657
708
|
allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
|
|
658
709
|
subjectId: input.subjectId ?? null,
|
|
710
|
+
...(input.beforeCreateCommit ? { beforeCreateCommit: input.beforeCreateCommit } : {}),
|
|
659
711
|
});
|
|
660
712
|
if (keyedResult.denied) {
|
|
661
713
|
throw new SessionSpawnDeniedError(SessionSpawnDenial.parse(keyedResult.denial));
|
|
@@ -702,6 +754,7 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
702
754
|
variableSetId: input.variableSet?.id ?? null,
|
|
703
755
|
rigId: input.rigId ?? null,
|
|
704
756
|
rigVersionId: input.rigVersionId ?? null,
|
|
757
|
+
channelId: input.channelId ?? null,
|
|
705
758
|
firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
|
|
706
759
|
firstPartyMcpTools: input.firstPartyMcpTools,
|
|
707
760
|
instructions: input.instructions ?? null,
|
|
@@ -711,9 +764,15 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
711
764
|
...(input.sandboxOs ? { sandboxOs: input.sandboxOs } : {}),
|
|
712
765
|
mcpServers: input.mcpServers ?? [],
|
|
713
766
|
personalConnectionDelegations: input.personalConnectionDelegations ?? [],
|
|
767
|
+
...(input.xaiProviderAccountAuthoritySnapshot
|
|
768
|
+
? {
|
|
769
|
+
initialXaiProviderAccountAuthoritySnapshot: input.xaiProviderAccountAuthoritySnapshot,
|
|
770
|
+
}
|
|
771
|
+
: {}),
|
|
714
772
|
maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
|
|
715
773
|
allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
|
|
716
774
|
subjectId: input.subjectId ?? null,
|
|
775
|
+
...(input.beforeCreateCommit ? { beforeCreateCommit: input.beforeCreateCommit } : {}),
|
|
717
776
|
});
|
|
718
777
|
} catch (error) {
|
|
719
778
|
if (error instanceof SessionSpawnDeniedDbError) {
|
|
@@ -747,7 +806,7 @@ async function finishStartSession(
|
|
|
747
806
|
input: {
|
|
748
807
|
db: Database;
|
|
749
808
|
bus: EventBus;
|
|
750
|
-
workflowClient: SessionWorkflowClient
|
|
809
|
+
workflowClient: Pick<SessionWorkflowClient, "wakeSessionWorkflow">;
|
|
751
810
|
initialMessage: string;
|
|
752
811
|
deferInitialTurn?: boolean;
|
|
753
812
|
turnInstructions?: string | null;
|
|
@@ -767,7 +826,10 @@ async function finishStartSession(
|
|
|
767
826
|
settings: Settings;
|
|
768
827
|
workingDir?: string | null;
|
|
769
828
|
} | null;
|
|
770
|
-
consumeNewSessionDraft?: {
|
|
829
|
+
consumeNewSessionDraft?: {
|
|
830
|
+
subjectId: string;
|
|
831
|
+
expectedRevision: number;
|
|
832
|
+
} | null;
|
|
771
833
|
},
|
|
772
834
|
session: Session,
|
|
773
835
|
): Promise<{ session: CreateSessionResponse; changed: boolean }> {
|
|
@@ -790,7 +852,11 @@ async function finishStartSession(
|
|
|
790
852
|
sessionGroupId: session.sandboxGroupId,
|
|
791
853
|
};
|
|
792
854
|
const seeded = await swapActiveSandbox(
|
|
793
|
-
{
|
|
855
|
+
{
|
|
856
|
+
db: input.db,
|
|
857
|
+
settings: input.seedTargetSandbox.settings,
|
|
858
|
+
bus: input.bus,
|
|
859
|
+
},
|
|
794
860
|
ctx,
|
|
795
861
|
input.seedTargetSandbox.sandboxId,
|
|
796
862
|
// The working dir is committed in the SAME epoch-fenced CAS that seeds the
|
|
@@ -813,7 +879,10 @@ async function finishStartSession(
|
|
|
813
879
|
createdEventPayload: {
|
|
814
880
|
toolPolicy: input.toolPolicy,
|
|
815
881
|
...(input.variableSet
|
|
816
|
-
? {
|
|
882
|
+
? {
|
|
883
|
+
variableSetId: input.variableSet.id,
|
|
884
|
+
variableSetName: input.variableSet.name,
|
|
885
|
+
}
|
|
817
886
|
: {}),
|
|
818
887
|
...(input.sessionMcpServers?.length ? { mcpServers: input.sessionMcpServers } : {}),
|
|
819
888
|
},
|
|
@@ -863,10 +932,10 @@ export function workflowIdForSession(sessionId: string): string {
|
|
|
863
932
|
* it cannot be resolved to a provider at run time, so we fail the request at
|
|
864
933
|
* the API edge with 422 rather than enqueuing a turn the worker can't honor.
|
|
865
934
|
*
|
|
866
|
-
* `model` is the
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
*
|
|
935
|
+
* `model` is the effective value selected by the caller's boundary. Top-level
|
|
936
|
+
* omission defaults to `settings.openaiModel`; child-session omission inherits
|
|
937
|
+
* the worker-signed calling turn before reaching this helper. Centralized here
|
|
938
|
+
* so every model-carrying choke point
|
|
870
939
|
* (create-session, user-message/turn-accept, queued-turn update, and
|
|
871
940
|
* scheduled-task agentConfig — a scheduled task is a session the worker runs
|
|
872
941
|
* later) and the MCP surfaces that share them validate identically and cannot
|
|
@@ -892,6 +961,16 @@ export function canonicalConfiguredModel(
|
|
|
892
961
|
if (settings.codexSubscriptionEnabled && canonicalModel.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
893
962
|
return canonicalModel;
|
|
894
963
|
}
|
|
964
|
+
// SuperGrok subscription models are also discovered per workspace rather
|
|
965
|
+
// than stored in the deployment-global allow-list. Connection availability
|
|
966
|
+
// is enforced by the workspace policy and worker; this edge guard only needs
|
|
967
|
+
// to admit the product-model namespace when the feature is enabled.
|
|
968
|
+
if (
|
|
969
|
+
settings.supergrokSubscriptionEnabled &&
|
|
970
|
+
canonicalModel.startsWith(XAI_SUBSCRIPTION_MODEL_ID_PREFIX)
|
|
971
|
+
) {
|
|
972
|
+
return canonicalModel;
|
|
973
|
+
}
|
|
895
974
|
if (canonicalModel.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
|
|
896
975
|
return canonicalModel;
|
|
897
976
|
}
|
|
@@ -1023,6 +1102,7 @@ export async function postUserMessageTurn(input: {
|
|
|
1023
1102
|
workspaceId: string;
|
|
1024
1103
|
sessionId: string;
|
|
1025
1104
|
text: string;
|
|
1105
|
+
annotations?: TimelineAnnotation[];
|
|
1026
1106
|
turnInstructions?: string | null;
|
|
1027
1107
|
resources: ResourceRef[];
|
|
1028
1108
|
model?: string | null;
|
|
@@ -1040,7 +1120,14 @@ export async function postUserMessageTurn(input: {
|
|
|
1040
1120
|
expectedDraftRevision?: number | null;
|
|
1041
1121
|
reasoningEffortFallback?: Settings["openaiReasoningEffort"];
|
|
1042
1122
|
turnExecutionPolicy: TurnExecutionPolicyV1;
|
|
1043
|
-
|
|
1123
|
+
recordAgentRunUsage?: boolean;
|
|
1124
|
+
schedulePostCommit?: (task: () => Promise<void>) => void;
|
|
1125
|
+
}): Promise<{
|
|
1126
|
+
accepted: SessionEvent;
|
|
1127
|
+
turn: SessionTurn;
|
|
1128
|
+
interruptionCount: number;
|
|
1129
|
+
replay: boolean;
|
|
1130
|
+
}> {
|
|
1044
1131
|
const { db, bus, workflowClient, settings, accountId, workspaceId, sessionId } = input;
|
|
1045
1132
|
const requestedModel = canonicalConfiguredModel(settings, input.model ?? null) ?? null;
|
|
1046
1133
|
const requestedReasoningEffort = input.reasoningEffort ?? null;
|
|
@@ -1061,35 +1148,50 @@ export async function postUserMessageTurn(input: {
|
|
|
1061
1148
|
const operationKey = input.clientEventId ?? crypto.randomUUID();
|
|
1062
1149
|
let result;
|
|
1063
1150
|
try {
|
|
1064
|
-
result = await
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1151
|
+
result = await runIdempotentPersistenceTransaction(
|
|
1152
|
+
{
|
|
1153
|
+
stage: "session.prompt.submit",
|
|
1154
|
+
eventTypes: ["user.message", "turn.queued", "session.status.changed"],
|
|
1155
|
+
maxAttempts: 3,
|
|
1156
|
+
},
|
|
1157
|
+
async () =>
|
|
1158
|
+
await withWorkspaceSubjectSessionActivityRls(
|
|
1159
|
+
db,
|
|
1068
1160
|
workspaceId,
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1161
|
+
input.actor ?? accountId,
|
|
1162
|
+
(scoped) =>
|
|
1163
|
+
submitHumanPromptInTransaction(scoped, {
|
|
1164
|
+
accountId,
|
|
1165
|
+
workspaceId,
|
|
1166
|
+
sessionId,
|
|
1167
|
+
subjectId: input.actor ?? accountId,
|
|
1168
|
+
...(input.actorLabel ? { subjectLabel: input.actorLabel } : {}),
|
|
1169
|
+
actor: input.commandActor ?? {
|
|
1170
|
+
type: "human",
|
|
1171
|
+
subjectId: input.actor ?? accountId,
|
|
1172
|
+
},
|
|
1173
|
+
operationKey,
|
|
1174
|
+
delivery: input.delivery ?? "send",
|
|
1175
|
+
controlEtag: input.controlEtag ?? null,
|
|
1176
|
+
expectedDraftRevision: input.expectedDraftRevision ?? null,
|
|
1177
|
+
text: input.text,
|
|
1178
|
+
annotations: input.annotations ?? [],
|
|
1179
|
+
turnInstructions: input.turnInstructions ?? null,
|
|
1180
|
+
resources: input.resources,
|
|
1181
|
+
model: requestedModel,
|
|
1182
|
+
reasoningEffort: requestedReasoningEffort,
|
|
1183
|
+
latencyMode: input.latencyMode ?? null,
|
|
1184
|
+
reasoningEffortFallback:
|
|
1185
|
+
input.reasoningEffortFallback ?? settings.openaiReasoningEffort,
|
|
1186
|
+
turnExecutionPolicy: input.turnExecutionPolicy,
|
|
1187
|
+
source: input.origin === "operator" ? "api" : "user",
|
|
1188
|
+
...(input.recordAgentRunUsage !== undefined
|
|
1189
|
+
? { recordAgentRunUsage: input.recordAgentRunUsage }
|
|
1190
|
+
: {}),
|
|
1191
|
+
personalConnectionDelegations: input.personalConnectionDelegations ?? [],
|
|
1192
|
+
mcpCredentialUpdates: input.mcpCredentialUpdates ?? [],
|
|
1193
|
+
}),
|
|
1194
|
+
),
|
|
1093
1195
|
);
|
|
1094
1196
|
} catch (error) {
|
|
1095
1197
|
if (
|
|
@@ -1106,54 +1208,68 @@ export async function postUserMessageTurn(input: {
|
|
|
1106
1208
|
}
|
|
1107
1209
|
throw error;
|
|
1108
1210
|
}
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
if (!controlEvent) {
|
|
1132
|
-
throw new Error(
|
|
1133
|
-
`Committed workspace control event disappeared: ${result.workspaceControlEventId}`,
|
|
1134
|
-
);
|
|
1211
|
+
const postCommitTask = async () => {
|
|
1212
|
+
try {
|
|
1213
|
+
await publishDurableSessionEvents(bus, workspaceId, sessionId, result.events);
|
|
1214
|
+
if (result.workspaceControlEventId) {
|
|
1215
|
+
const controlEvent = await getWorkspaceControlEvent(
|
|
1216
|
+
db,
|
|
1217
|
+
workspaceId,
|
|
1218
|
+
result.workspaceControlEventId,
|
|
1219
|
+
);
|
|
1220
|
+
if (!controlEvent) {
|
|
1221
|
+
throw new Error(
|
|
1222
|
+
`Committed workspace control event disappeared: ${result.workspaceControlEventId}`,
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
await publishDurableWorkspaceControlEvent(bus, workspaceId, controlEvent);
|
|
1226
|
+
}
|
|
1227
|
+
} catch {
|
|
1228
|
+
console.warn("[sessions] prompt event fanout failed; durable rows remain replayable", {
|
|
1229
|
+
errorClass: "PromptEventFanoutOperationError",
|
|
1230
|
+
errorCode: "session_prompt_event_fanout_failed",
|
|
1231
|
+
origin: "core",
|
|
1232
|
+
});
|
|
1135
1233
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1234
|
+
try {
|
|
1235
|
+
await workflowClient.wakeSessionWorkflow({
|
|
1236
|
+
accountId,
|
|
1237
|
+
workspaceId,
|
|
1238
|
+
sessionId,
|
|
1239
|
+
workflowId: result.turn.temporalWorkflowId,
|
|
1240
|
+
wakeRevision: result.wakeRevision,
|
|
1241
|
+
...((input.delivery ?? "send") === "steer" || result.interruptionCount > 0
|
|
1242
|
+
? { interruptionRequested: true }
|
|
1243
|
+
: {}),
|
|
1244
|
+
});
|
|
1245
|
+
} catch {
|
|
1246
|
+
console.warn("[sessions] workflow wake failed; durable outbox will retry", {
|
|
1247
|
+
errorClass: "WorkflowWakeOperationError",
|
|
1248
|
+
errorCode: "session_workflow_wake_failed",
|
|
1249
|
+
origin: "core",
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
const schedulePostCommit =
|
|
1254
|
+
input.schedulePostCommit ??
|
|
1255
|
+
((task: () => Promise<void>) => {
|
|
1256
|
+
void task();
|
|
1148
1257
|
});
|
|
1258
|
+
try {
|
|
1259
|
+
schedulePostCommit(postCommitTask);
|
|
1149
1260
|
} catch {
|
|
1150
|
-
console.warn("[sessions]
|
|
1151
|
-
errorClass: "
|
|
1152
|
-
errorCode: "
|
|
1261
|
+
console.warn("[sessions] prompt post-commit scheduling failed; durable recovery remains", {
|
|
1262
|
+
errorClass: "PromptPostCommitScheduleError",
|
|
1263
|
+
errorCode: "session_prompt_post_commit_schedule_failed",
|
|
1153
1264
|
origin: "core",
|
|
1154
1265
|
});
|
|
1155
1266
|
}
|
|
1156
|
-
return {
|
|
1267
|
+
return {
|
|
1268
|
+
accepted: result.accepted,
|
|
1269
|
+
turn: result.turn,
|
|
1270
|
+
interruptionCount: result.interruptionCount,
|
|
1271
|
+
replay: result.replay,
|
|
1272
|
+
};
|
|
1157
1273
|
}
|
|
1158
1274
|
|
|
1159
1275
|
/**
|
|
@@ -1201,11 +1317,18 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1201
1317
|
? (grant.metadata["sessionId"] as string)
|
|
1202
1318
|
: null;
|
|
1203
1319
|
if (parentSessionId) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1320
|
+
try {
|
|
1321
|
+
await requireSessionAuthorization(deps, grant, {
|
|
1322
|
+
sessionId: parentSessionId,
|
|
1323
|
+
operation: "session.child.create",
|
|
1324
|
+
surface: "core",
|
|
1325
|
+
});
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
if (error instanceof SessionAuthorizationDeniedError) {
|
|
1328
|
+
throw new HTTPException(403, { message: error.message, cause: error });
|
|
1329
|
+
}
|
|
1330
|
+
throw error;
|
|
1331
|
+
}
|
|
1209
1332
|
}
|
|
1210
1333
|
const parentSession = parentSessionId ? await getSession(db, workspaceId, parentSessionId) : null;
|
|
1211
1334
|
if (parentSessionId && !parentSession) {
|
|
@@ -1213,10 +1336,35 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1213
1336
|
message: `parent session not found in workspace: ${parentSessionId}`,
|
|
1214
1337
|
});
|
|
1215
1338
|
}
|
|
1339
|
+
const creationInitiator = creationInitiatorForGrant(grant);
|
|
1340
|
+
const parentCallingTurn =
|
|
1341
|
+
parentSession && creationInitiator.actor
|
|
1342
|
+
? await getSessionTurn(db, workspaceId, creationInitiator.actor.turnId)
|
|
1343
|
+
: null;
|
|
1344
|
+
if (
|
|
1345
|
+
creationInitiator.actor &&
|
|
1346
|
+
(!parentCallingTurn || parentCallingTurn.sessionId !== parentSession?.id)
|
|
1347
|
+
) {
|
|
1348
|
+
throw new HTTPException(403, {
|
|
1349
|
+
message: "caller attempt does not belong to the parent session",
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
const xaiProviderAccountAuthoritySnapshot =
|
|
1353
|
+
parentSession && creationInitiator.actor
|
|
1354
|
+
? await getSessionTurnXaiProviderAccountAuthoritySnapshot(
|
|
1355
|
+
db,
|
|
1356
|
+
workspaceId,
|
|
1357
|
+
parentSession.id,
|
|
1358
|
+
creationInitiator.actor.turnId,
|
|
1359
|
+
)
|
|
1360
|
+
: undefined;
|
|
1216
1361
|
const capabilityRuntimeSettings = await settingsWithEnabledCapabilityMcpServers(
|
|
1217
1362
|
db,
|
|
1218
1363
|
workspaceId,
|
|
1219
1364
|
settings,
|
|
1365
|
+
{
|
|
1366
|
+
subjectId: grant.subjectId,
|
|
1367
|
+
},
|
|
1220
1368
|
);
|
|
1221
1369
|
const sessionMcpServers = hasOwnProperty(rawPayload, "mcpServers")
|
|
1222
1370
|
? validateSessionMcpServersForCreate(capabilityRuntimeSettings, grant, payload.mcpServers)
|
|
@@ -1263,7 +1411,10 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1263
1411
|
"child tools may only narrow the parent session tool policy",
|
|
1264
1412
|
);
|
|
1265
1413
|
selectedTools = requestedTools;
|
|
1266
|
-
toolPolicy = {
|
|
1414
|
+
toolPolicy = {
|
|
1415
|
+
mode: "explicit",
|
|
1416
|
+
inheritedFromSessionId: parentSession.id,
|
|
1417
|
+
};
|
|
1267
1418
|
} else {
|
|
1268
1419
|
selectedTools = parentEffective;
|
|
1269
1420
|
toolPolicy = {
|
|
@@ -1296,7 +1447,9 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1296
1447
|
});
|
|
1297
1448
|
await validateGitHubRepositorySelection(db, workspaceId, resources);
|
|
1298
1449
|
if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
1299
|
-
throw new HTTPException(503, {
|
|
1450
|
+
throw new HTTPException(503, {
|
|
1451
|
+
message: "object storage is not configured",
|
|
1452
|
+
});
|
|
1300
1453
|
}
|
|
1301
1454
|
await validateFileResources(db, workspaceId, resources);
|
|
1302
1455
|
// VariableSet attachment requires variable-sets:use on the calling grant
|
|
@@ -1341,25 +1494,70 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1341
1494
|
frozenRigVersionId = rig.activeVersion.id;
|
|
1342
1495
|
}
|
|
1343
1496
|
}
|
|
1344
|
-
|
|
1497
|
+
// CHANNEL FILING. Pure rail organization: a UUID files the session into that
|
|
1498
|
+
// workspace channel, omission/null leaves it unfiled (inbox). Resolved
|
|
1499
|
+
// workspace-scoped so a foreign channel id can never attach; an explicit
|
|
1500
|
+
// unknown channelId is a caller error → 422.
|
|
1501
|
+
let channelId: string | null = null;
|
|
1502
|
+
if (payload.channelId) {
|
|
1503
|
+
const channel = await getChannel(db, workspaceId, payload.channelId);
|
|
1504
|
+
if (!channel) {
|
|
1505
|
+
throw new HTTPException(422, {
|
|
1506
|
+
message: `unknown channelId: ${payload.channelId}`,
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
channelId = channel.id;
|
|
1510
|
+
}
|
|
1511
|
+
// A spawned worker is causally part of the exact turn that created it. Omitted
|
|
1512
|
+
// execution policy fields therefore inherit that calling turn rather than the
|
|
1513
|
+
// deployment defaults. This is especially important for Codex subscription
|
|
1514
|
+
// managers: falling back to the deployment model would silently move a child
|
|
1515
|
+
// onto the OpenGeni-credits billing path. Legacy session-bound grants without
|
|
1516
|
+
// exact attempt claims fall back to the parent session's persisted defaults.
|
|
1517
|
+
const inheritedModel = parentCallingTurn?.model ?? parentSession?.model ?? settings.openaiModel;
|
|
1518
|
+
const model = canonicalConfiguredModel(settings, payload.model ?? inheritedModel);
|
|
1345
1519
|
if (model === null || model === undefined) {
|
|
1346
1520
|
throw new Error("effective session model unexpectedly resolved to null");
|
|
1347
1521
|
}
|
|
1348
|
-
// Session creation persists the EFFECTIVE model —
|
|
1349
|
-
//
|
|
1350
|
-
// that effective value, not just explicit ones (a restricted workspace's
|
|
1351
|
-
// default-model session would otherwise be born blocked).
|
|
1522
|
+
// Session creation persists the EFFECTIVE model — the explicit selection,
|
|
1523
|
+
// inherited calling-turn model, or deployment default — so the policy must
|
|
1524
|
+
// vet that effective value, not just explicit ones (a restricted workspace's
|
|
1525
|
+
// inherited/default-model session would otherwise be born blocked).
|
|
1352
1526
|
await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, model);
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1527
|
+
const inheritedReasoningEffort =
|
|
1528
|
+
parentCallingTurn?.reasoningEffort ??
|
|
1529
|
+
(parentSession
|
|
1530
|
+
? reasoningEffortForSession(parentSession.metadata, settings.openaiReasoningEffort)
|
|
1531
|
+
: settings.openaiReasoningEffort);
|
|
1532
|
+
const inheritedLatencyMode =
|
|
1533
|
+
parentCallingTurn?.latencyMode ??
|
|
1534
|
+
(parentSession ? latencyModeForSession(parentSession.metadata, "standard") : "standard");
|
|
1535
|
+
const reasoningEffort = payload.reasoningEffort ?? inheritedReasoningEffort;
|
|
1536
|
+
const latencyMode = payload.latencyMode ?? inheritedLatencyMode;
|
|
1537
|
+
const inheritedFromParent = parentSession !== null;
|
|
1355
1538
|
const turnExecutionPolicy = resolveTurnExecutionPolicyV1(settings, {
|
|
1356
1539
|
modelId: model,
|
|
1357
1540
|
requestedModelId: payload.model ?? null,
|
|
1358
|
-
modelSource:
|
|
1541
|
+
modelSource:
|
|
1542
|
+
payload.model === undefined
|
|
1543
|
+
? inheritedFromParent
|
|
1544
|
+
? "continuation"
|
|
1545
|
+
: "deployment"
|
|
1546
|
+
: "explicit",
|
|
1359
1547
|
reasoningEffort,
|
|
1360
|
-
reasoningSource:
|
|
1548
|
+
reasoningSource:
|
|
1549
|
+
payload.reasoningEffort === undefined
|
|
1550
|
+
? inheritedFromParent
|
|
1551
|
+
? "continuation"
|
|
1552
|
+
: "deployment"
|
|
1553
|
+
: "explicit",
|
|
1361
1554
|
latencyMode,
|
|
1362
|
-
latencyModeSource:
|
|
1555
|
+
latencyModeSource:
|
|
1556
|
+
payload.latencyMode === undefined
|
|
1557
|
+
? inheritedFromParent
|
|
1558
|
+
? "continuation"
|
|
1559
|
+
: "deployment"
|
|
1560
|
+
: "explicit",
|
|
1363
1561
|
});
|
|
1364
1562
|
// Parent linkage was resolved above, before context validation. A child with
|
|
1365
1563
|
// no explicit permission override inherits the creating session's effective
|
|
@@ -1429,12 +1627,22 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1429
1627
|
// Tool visibility is independent from permission authority. A child that
|
|
1430
1628
|
// omits the field inherits the parent's exact effective selection; a
|
|
1431
1629
|
// top-level omission selects the safe non-connector default catalog.
|
|
1630
|
+
const deploymentFirstPartyMcpToolPolicy = resolveFirstPartyMcpToolPolicy(settings);
|
|
1631
|
+
const disallowedFirstPartyMcpTool = payload.firstPartyMcpTools?.find(
|
|
1632
|
+
(tool) => !deploymentFirstPartyMcpToolPolicy.allowed.includes(tool),
|
|
1633
|
+
);
|
|
1634
|
+
if (disallowedFirstPartyMcpTool) {
|
|
1635
|
+
throw new HTTPException(422, {
|
|
1636
|
+
message: `first-party MCP tool is disabled by deployment policy: ${disallowedFirstPartyMcpTool}`,
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1432
1639
|
const firstPartyMcpTools = resolveFirstPartyMcpToolsForCreate(
|
|
1433
1640
|
payload.firstPartyMcpTools,
|
|
1434
1641
|
parentSession ? parentSession.firstPartyMcpTools : undefined,
|
|
1642
|
+
deploymentFirstPartyMcpToolPolicy,
|
|
1435
1643
|
);
|
|
1436
1644
|
if (payload.goal) {
|
|
1437
|
-
const missingGoalTools = ["goal_update", "goal_complete", "goal_pause"].filter(
|
|
1645
|
+
const missingGoalTools = ["goal_update", "goal_progress", "goal_complete", "goal_pause"].filter(
|
|
1438
1646
|
(name) => !firstPartyMcpTools.includes(name as FirstPartyMcpToolName),
|
|
1439
1647
|
);
|
|
1440
1648
|
if (missingGoalTools.length > 0) {
|
|
@@ -1664,7 +1872,6 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1664
1872
|
model,
|
|
1665
1873
|
});
|
|
1666
1874
|
}
|
|
1667
|
-
const creationInitiator = creationInitiatorForGrant(grant);
|
|
1668
1875
|
let createOutcome: CreateSessionOutcome;
|
|
1669
1876
|
try {
|
|
1670
1877
|
createOutcome = await createAndStartSessionWithOutcome({
|
|
@@ -1706,6 +1913,7 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1706
1913
|
// Frozen rig binding (M3): both null for a rig-less session (today's path).
|
|
1707
1914
|
rigId: frozenRigId,
|
|
1708
1915
|
rigVersionId: frozenRigVersionId,
|
|
1916
|
+
channelId,
|
|
1709
1917
|
goal: payload.goal ?? null,
|
|
1710
1918
|
// Per-session persona instructions (already trimmed/validated by the
|
|
1711
1919
|
// contracts schema). Persisted on the row; composed system-level at turn
|
|
@@ -1717,6 +1925,7 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1717
1925
|
mcpServers: sessionMcpServers.dbServers,
|
|
1718
1926
|
sessionMcpServers: sessionMcpServers.metadata,
|
|
1719
1927
|
personalConnectionDelegations,
|
|
1928
|
+
...(xaiProviderAccountAuthoritySnapshot ? { xaiProviderAccountAuthoritySnapshot } : {}),
|
|
1720
1929
|
parentSessionId,
|
|
1721
1930
|
createIdempotencyKey: payload.idempotencyKey ?? null,
|
|
1722
1931
|
maxNestedAgentDepthOverride: payload.maxNestedAgentDepth ?? null,
|
|
@@ -1727,7 +1936,11 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1727
1936
|
// (after the row exists, before the first turn dispatches). Validation
|
|
1728
1937
|
// (ownership/liveness) lives in swapActiveSandbox; an invalid target 422s.
|
|
1729
1938
|
seedTargetSandbox: payload.targetSandboxId
|
|
1730
|
-
? {
|
|
1939
|
+
? {
|
|
1940
|
+
sandboxId: payload.targetSandboxId,
|
|
1941
|
+
settings,
|
|
1942
|
+
workingDir: payload.workingDir ?? null,
|
|
1943
|
+
}
|
|
1731
1944
|
: null,
|
|
1732
1945
|
consumeNewSessionDraft:
|
|
1733
1946
|
payload.expectedNewSessionDraftRevision !== undefined
|
|
@@ -1810,6 +2023,7 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1810
2023
|
sessionId: string,
|
|
1811
2024
|
input: {
|
|
1812
2025
|
text: string;
|
|
2026
|
+
annotations?: SubmittedTimelineAnnotation[];
|
|
1813
2027
|
turnInstructions?: string | null;
|
|
1814
2028
|
resources?: ResourceRef[];
|
|
1815
2029
|
model?: string | null;
|
|
@@ -1822,8 +2036,14 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1822
2036
|
controlEtag?: string | null;
|
|
1823
2037
|
expectedDraftRevision?: number | null;
|
|
1824
2038
|
},
|
|
1825
|
-
): Promise<{
|
|
2039
|
+
): Promise<{
|
|
2040
|
+
accepted: SessionEvent;
|
|
2041
|
+
turn: SessionTurn;
|
|
2042
|
+
interruptionCount: number;
|
|
2043
|
+
replay: boolean;
|
|
2044
|
+
}> {
|
|
1826
2045
|
const { settings, db, bus, workflowClient, objectStorage } = deps;
|
|
2046
|
+
const delegatedServiceInitiator = serviceInitiatorForGrant(grant);
|
|
1827
2047
|
await requireSessionAuthorization(deps, grant, {
|
|
1828
2048
|
sessionId,
|
|
1829
2049
|
operation: input.delivery === "steer" ? "session.steer" : "session.append",
|
|
@@ -1864,6 +2084,12 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1864
2084
|
latencyModeSource: input.latencyMode == null ? "session" : "explicit",
|
|
1865
2085
|
});
|
|
1866
2086
|
const requestedResources = normalizeResources(input.resources ?? []);
|
|
2087
|
+
const annotations = await validateSubmittedTimelineAnnotations(
|
|
2088
|
+
db,
|
|
2089
|
+
workspaceId,
|
|
2090
|
+
sessionId,
|
|
2091
|
+
input.annotations ?? [],
|
|
2092
|
+
);
|
|
1867
2093
|
await requireLimit(deps, {
|
|
1868
2094
|
accountId: grant.accountId,
|
|
1869
2095
|
workspaceId,
|
|
@@ -1872,7 +2098,9 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1872
2098
|
model: effectiveModel,
|
|
1873
2099
|
});
|
|
1874
2100
|
if (requestedResources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
1875
|
-
throw new HTTPException(503, {
|
|
2101
|
+
throw new HTTPException(503, {
|
|
2102
|
+
message: "object storage is not configured",
|
|
2103
|
+
});
|
|
1876
2104
|
}
|
|
1877
2105
|
await validateFileResources(db, workspaceId, requestedResources);
|
|
1878
2106
|
await validateGitHubRepositorySelection(db, workspaceId, [
|
|
@@ -1885,7 +2113,9 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1885
2113
|
session: existingSession,
|
|
1886
2114
|
updates: input.mcpCredentialUpdates ?? [],
|
|
1887
2115
|
});
|
|
1888
|
-
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings
|
|
2116
|
+
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings, {
|
|
2117
|
+
subjectId: grant.subjectId,
|
|
2118
|
+
});
|
|
1889
2119
|
const personalConnectionDelegations = await freezePersonalConnectionDelegations({
|
|
1890
2120
|
db,
|
|
1891
2121
|
workspaceId,
|
|
@@ -1893,8 +2123,7 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1893
2123
|
tools: existingSession.tools,
|
|
1894
2124
|
source: personalConnectionDelegationSourceForGrant(grant),
|
|
1895
2125
|
});
|
|
1896
|
-
const
|
|
1897
|
-
const { accepted, turn, replay } = await postUserMessageTurn({
|
|
2126
|
+
const { accepted, turn, interruptionCount, replay } = await postUserMessageTurn({
|
|
1898
2127
|
db,
|
|
1899
2128
|
bus,
|
|
1900
2129
|
workflowClient,
|
|
@@ -1903,6 +2132,7 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1903
2132
|
workspaceId,
|
|
1904
2133
|
sessionId,
|
|
1905
2134
|
text: input.text,
|
|
2135
|
+
annotations,
|
|
1906
2136
|
turnInstructions: input.turnInstructions ?? null,
|
|
1907
2137
|
resources: requestedResources,
|
|
1908
2138
|
model: input.model ?? null,
|
|
@@ -1933,24 +2163,10 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1933
2163
|
? { expectedDraftRevision: input.expectedDraftRevision }
|
|
1934
2164
|
: {}),
|
|
1935
2165
|
...(input.clientEventId ? { clientEventId: input.clientEventId } : {}),
|
|
2166
|
+
recordAgentRunUsage: true,
|
|
2167
|
+
...(deps.schedulePromptPostCommit ? { schedulePostCommit: deps.schedulePromptPostCommit } : {}),
|
|
1936
2168
|
});
|
|
1937
|
-
|
|
1938
|
-
accountId: grant.accountId,
|
|
1939
|
-
workspaceId,
|
|
1940
|
-
subjectId: grant.subjectId,
|
|
1941
|
-
eventType: "agent_run.created",
|
|
1942
|
-
quantity: 1,
|
|
1943
|
-
unit: "run",
|
|
1944
|
-
sourceResourceType: "session_turn",
|
|
1945
|
-
sourceResourceId: turn.id,
|
|
1946
|
-
sessionId,
|
|
1947
|
-
turnId: turn.id,
|
|
1948
|
-
initiator: turn.initiator,
|
|
1949
|
-
initiatorContext: turn.initiatorContext,
|
|
1950
|
-
origin: turn.source,
|
|
1951
|
-
idempotencyKey: `agent_run.created:${workspaceId}:${turn.id}`,
|
|
1952
|
-
});
|
|
1953
|
-
return { accepted, turn, replay };
|
|
2169
|
+
return { accepted, turn, interruptionCount, replay };
|
|
1954
2170
|
}
|
|
1955
2171
|
|
|
1956
2172
|
/** Backward-compatible entity-returning path used by existing REST callers. */
|
|
@@ -1960,15 +2176,20 @@ export async function acceptSessionUserMessage(
|
|
|
1960
2176
|
workspaceId: Parameters<typeof acceptSessionUserMessageWithOutcome>[2],
|
|
1961
2177
|
sessionId: Parameters<typeof acceptSessionUserMessageWithOutcome>[3],
|
|
1962
2178
|
input: Parameters<typeof acceptSessionUserMessageWithOutcome>[4],
|
|
1963
|
-
): Promise<{
|
|
1964
|
-
|
|
2179
|
+
): Promise<{
|
|
2180
|
+
accepted: SessionEvent;
|
|
2181
|
+
turn: SessionTurn;
|
|
2182
|
+
interruptionCount: number;
|
|
2183
|
+
replay: boolean;
|
|
2184
|
+
}> {
|
|
2185
|
+
const { accepted, turn, interruptionCount, replay } = await acceptSessionUserMessageWithOutcome(
|
|
1965
2186
|
deps,
|
|
1966
2187
|
grant,
|
|
1967
2188
|
workspaceId,
|
|
1968
2189
|
sessionId,
|
|
1969
2190
|
input,
|
|
1970
2191
|
);
|
|
1971
|
-
return { accepted, turn };
|
|
2192
|
+
return { accepted, turn, interruptionCount, replay };
|
|
1972
2193
|
}
|
|
1973
2194
|
|
|
1974
2195
|
/**
|
|
@@ -2002,7 +2223,12 @@ export async function updateSessionTitle(
|
|
|
2002
2223
|
surface: "core",
|
|
2003
2224
|
});
|
|
2004
2225
|
const workspaceId = grant.workspaceId;
|
|
2005
|
-
const result = await updateSessionTitleRow(db, {
|
|
2226
|
+
const result = await updateSessionTitleRow(db, {
|
|
2227
|
+
workspaceId,
|
|
2228
|
+
sessionId,
|
|
2229
|
+
title,
|
|
2230
|
+
source,
|
|
2231
|
+
});
|
|
2006
2232
|
if (result.updated) {
|
|
2007
2233
|
await appendAndPublishEvents(db, bus, workspaceId, sessionId, [
|
|
2008
2234
|
{
|
|
@@ -2053,7 +2279,9 @@ export async function updateSessionMcpApprovalPolicy(
|
|
|
2053
2279
|
async (_session, context) => {
|
|
2054
2280
|
const result = await context.updateSessionMcpApprovalPolicy(serverId, normalizedPolicy);
|
|
2055
2281
|
if (!result.server) {
|
|
2056
|
-
throw new HTTPException(404, {
|
|
2282
|
+
throw new HTTPException(404, {
|
|
2283
|
+
message: "session MCP server not found",
|
|
2284
|
+
});
|
|
2057
2285
|
}
|
|
2058
2286
|
outcome.server = result.server;
|
|
2059
2287
|
return {
|
|
@@ -2070,6 +2298,7 @@ export async function updateSessionMcpApprovalPolicy(
|
|
|
2070
2298
|
: [],
|
|
2071
2299
|
};
|
|
2072
2300
|
},
|
|
2301
|
+
{ activity: "semantic" },
|
|
2073
2302
|
);
|
|
2074
2303
|
const updatedServer = outcome.server;
|
|
2075
2304
|
if (!updatedServer) {
|
|
@@ -2153,6 +2382,7 @@ export async function updateSessionToolPolicy(
|
|
|
2153
2382
|
deps.db,
|
|
2154
2383
|
grant.workspaceId,
|
|
2155
2384
|
deps.settings,
|
|
2385
|
+
{ subjectId: grant.subjectId },
|
|
2156
2386
|
);
|
|
2157
2387
|
const runtimeSettings = settingsWithSessionMcpServerMetadata(
|
|
2158
2388
|
capabilityRuntimeSettings,
|
|
@@ -2168,7 +2398,9 @@ export async function updateSessionToolPolicy(
|
|
|
2168
2398
|
(tool) => !validatedIds.has(`${tool.kind}:${tool.id}`),
|
|
2169
2399
|
);
|
|
2170
2400
|
if (unknown) {
|
|
2171
|
-
throw new HTTPException(422, {
|
|
2401
|
+
throw new HTTPException(422, {
|
|
2402
|
+
message: `unknown MCP server id: ${unknown.id}`,
|
|
2403
|
+
});
|
|
2172
2404
|
}
|
|
2173
2405
|
return withFirstPartyTools(validatedTools, runtimeSettings);
|
|
2174
2406
|
})()
|
|
@@ -2176,11 +2408,20 @@ export async function updateSessionToolPolicy(
|
|
|
2176
2408
|
const explicitRequestedFirstPartyTools = explicitRequest
|
|
2177
2409
|
? [...explicitRequest.firstPartyMcpTools]
|
|
2178
2410
|
: null;
|
|
2411
|
+
const deploymentFirstPartyMcpToolPolicy = resolveFirstPartyMcpToolPolicy(deps.settings);
|
|
2412
|
+
const disallowedFirstPartyMcpTool = explicitRequestedFirstPartyTools?.find(
|
|
2413
|
+
(tool) => !deploymentFirstPartyMcpToolPolicy.allowed.includes(tool),
|
|
2414
|
+
);
|
|
2415
|
+
if (disallowedFirstPartyMcpTool) {
|
|
2416
|
+
throw new HTTPException(422, {
|
|
2417
|
+
message: `first-party MCP tool is disabled by deployment policy: ${disallowedFirstPartyMcpTool}`,
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2179
2420
|
const workspaceDefaultTools = withFirstPartyTools(
|
|
2180
2421
|
withDefaultEnabledCapabilityMcpTools([], deps.settings, capabilityRuntimeSettings),
|
|
2181
2422
|
runtimeSettings,
|
|
2182
2423
|
);
|
|
2183
|
-
const workspaceDefaultFirstPartyTools = [...
|
|
2424
|
+
const workspaceDefaultFirstPartyTools = [...deploymentFirstPartyMcpToolPolicy.default];
|
|
2184
2425
|
const events = await appendSessionEventsWithLockedSessionUpdate(
|
|
2185
2426
|
deps.db,
|
|
2186
2427
|
grant.workspaceId,
|
|
@@ -2197,7 +2438,9 @@ export async function updateSessionToolPolicy(
|
|
|
2197
2438
|
if (session.parentSessionId) {
|
|
2198
2439
|
const parent = await context.getLockedSession(session.parentSessionId);
|
|
2199
2440
|
if (!parent) {
|
|
2200
|
-
throw new HTTPException(409, {
|
|
2441
|
+
throw new HTTPException(409, {
|
|
2442
|
+
message: "parent session is no longer available",
|
|
2443
|
+
});
|
|
2201
2444
|
}
|
|
2202
2445
|
const parentTracksWorkspaceDefaults = parent.toolPolicy?.mode === "workspace_default";
|
|
2203
2446
|
const parentEffective = withFirstPartyTools(
|
|
@@ -2210,9 +2453,12 @@ export async function updateSessionToolPolicy(
|
|
|
2210
2453
|
: parent.tools,
|
|
2211
2454
|
runtimeSettings,
|
|
2212
2455
|
);
|
|
2456
|
+
const deploymentAllowedFirstPartyMcpTools = new Set(
|
|
2457
|
+
deploymentFirstPartyMcpToolPolicy.allowed,
|
|
2458
|
+
);
|
|
2213
2459
|
const parentFirstPartyMcpTools = [
|
|
2214
|
-
...(parent.firstPartyMcpTools ??
|
|
2215
|
-
];
|
|
2460
|
+
...(parent.firstPartyMcpTools ?? deploymentFirstPartyMcpToolPolicy.default),
|
|
2461
|
+
].filter((tool) => deploymentAllowedFirstPartyMcpTools.has(tool));
|
|
2216
2462
|
if (requestedMode === "workspace_default") {
|
|
2217
2463
|
if (!parentTracksWorkspaceDefaults) {
|
|
2218
2464
|
throw new HTTPException(403, {
|
|
@@ -2266,7 +2512,8 @@ export async function updateSessionToolPolicy(
|
|
|
2266
2512
|
const unchanged =
|
|
2267
2513
|
stableJson({
|
|
2268
2514
|
tools: session.tools,
|
|
2269
|
-
firstPartyMcpTools:
|
|
2515
|
+
firstPartyMcpTools:
|
|
2516
|
+
session.firstPartyMcpTools ?? deploymentFirstPartyMcpToolPolicy.default,
|
|
2270
2517
|
policy: currentPolicy,
|
|
2271
2518
|
}) ===
|
|
2272
2519
|
stableJson({
|
|
@@ -2287,7 +2534,7 @@ export async function updateSessionToolPolicy(
|
|
|
2287
2534
|
before: toolPolicyAuditSnapshot(
|
|
2288
2535
|
session,
|
|
2289
2536
|
session.tools,
|
|
2290
|
-
[...(session.firstPartyMcpTools ??
|
|
2537
|
+
[...(session.firstPartyMcpTools ?? deploymentFirstPartyMcpToolPolicy.default)],
|
|
2291
2538
|
currentPolicy,
|
|
2292
2539
|
),
|
|
2293
2540
|
after: toolPolicyAuditSnapshot(
|
|
@@ -2310,7 +2557,7 @@ export async function updateSessionToolPolicy(
|
|
|
2310
2557
|
},
|
|
2311
2558
|
};
|
|
2312
2559
|
},
|
|
2313
|
-
{ lockParentSession: true },
|
|
2560
|
+
{ activity: "semantic", lockParentSession: true },
|
|
2314
2561
|
);
|
|
2315
2562
|
if (events.length > 0) {
|
|
2316
2563
|
await publishDurableSessionEvents(deps.bus, grant.workspaceId, sessionId, events);
|