@opengeni/core 0.21.10 → 0.24.1
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/chunk-6WKPWE5S.js +2380 -0
- package/dist/chunk-6WKPWE5S.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 +26 -6
- package/dist/domain/capabilities.d.ts +28 -3
- package/dist/domain/company-profile-durable-learning-adapter.d.ts +39 -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/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 +14 -6
- 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 +9 -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 +35 -26
- package/dist/index.js +2792 -609
- package/dist/index.js.map +1 -1
- package/dist/managed-session.d.ts +1 -1
- package/dist/rigs/index.d.ts +1 -1
- package/dist/rigs/provider-images.d.ts +31 -0
- package/dist/sandbox/routing.d.ts +1 -0
- package/dist/session-authorization.d.ts +1 -1
- package/dist/workflow-wake-contract.d.ts +7 -0
- package/package.json +18 -10
- package/src/application/new-session-drafts.ts +2 -0
- package/src/application/session-commands.ts +188 -107
- package/src/dependencies.ts +30 -2
- package/src/domain/capabilities.ts +604 -220
- package/src/domain/company-profile-durable-learning-adapter.ts +221 -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/insights.ts +136 -33
- 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 +155 -29
- package/src/domain/session-tool-policy.ts +8 -2
- package/src/domain/sessions.ts +274 -131
- 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 +69 -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 +13 -0
- package/src/rigs/index.ts +26 -19
- package/src/rigs/provider-images.ts +110 -0
- package/src/sandbox/routing.ts +6 -3
- package/src/session-authorization.ts +3 -0
- package/src/workflow-wake-contract.ts +8 -0
package/src/domain/sessions.ts
CHANGED
|
@@ -35,6 +35,8 @@ import {
|
|
|
35
35
|
type SessionMcpCredentialUpdateInput,
|
|
36
36
|
type SessionMcpServerInput,
|
|
37
37
|
type SessionMcpServerMetadata,
|
|
38
|
+
type SubmittedTimelineAnnotation,
|
|
39
|
+
type TimelineAnnotation,
|
|
38
40
|
type UpdateSessionMcpApprovalPolicyResponse,
|
|
39
41
|
type UpdateSessionToolPolicyRequest,
|
|
40
42
|
type SessionAuthorizationPort,
|
|
@@ -59,7 +61,6 @@ import {
|
|
|
59
61
|
getSession,
|
|
60
62
|
SessionIdConflictError,
|
|
61
63
|
getSessionSpawnDenialByIdempotencyKey,
|
|
62
|
-
getSessionEvent,
|
|
63
64
|
getWorkspaceControlEvent,
|
|
64
65
|
getSessionLineage,
|
|
65
66
|
getSessionTurn,
|
|
@@ -71,12 +72,13 @@ import {
|
|
|
71
72
|
submitHumanPromptInTransaction,
|
|
72
73
|
appendSessionEventsWithLockedSessionUpdate,
|
|
73
74
|
updateSessionTitle as updateSessionTitleRow,
|
|
74
|
-
|
|
75
|
+
withWorkspaceSubjectSessionActivityRls,
|
|
75
76
|
type CreateSessionMcpServerInput,
|
|
76
77
|
type Database,
|
|
77
78
|
type UpdateSessionMcpServerCredentialsInput,
|
|
78
79
|
QueueCommandConflictError,
|
|
79
80
|
AgentCommandAuthorityError,
|
|
81
|
+
runIdempotentPersistenceTransaction,
|
|
80
82
|
SessionSpawnDeniedDbError,
|
|
81
83
|
SessionControlConflictError,
|
|
82
84
|
SessionToolPolicyVersionConflictError,
|
|
@@ -99,6 +101,7 @@ import type {
|
|
|
99
101
|
import { requireSessionAuthorization } from "../session-authorization";
|
|
100
102
|
import { swapActiveSandbox, type FleetContext } from "../sandbox/fleet";
|
|
101
103
|
import { settingsWithEnabledCapabilityMcpServers } from "./capabilities";
|
|
104
|
+
import { validateSubmittedTimelineAnnotations } from "./timeline-annotations";
|
|
102
105
|
import { requireVariableSetEncryption, validateVariableSetAttachment } from "./environments";
|
|
103
106
|
import {
|
|
104
107
|
freezePersonalConnectionDelegations,
|
|
@@ -241,7 +244,9 @@ export function creationInitiatorForGrant(grant: AccessGrant): FrozenCreationIni
|
|
|
241
244
|
!Number.isSafeInteger(callerExecutionGeneration) ||
|
|
242
245
|
callerExecutionGeneration < 1
|
|
243
246
|
) {
|
|
244
|
-
throw new HTTPException(403, {
|
|
247
|
+
throw new HTTPException(403, {
|
|
248
|
+
message: "caller attempt claims are incomplete",
|
|
249
|
+
});
|
|
245
250
|
}
|
|
246
251
|
const actor = {
|
|
247
252
|
type: "agent_attempt",
|
|
@@ -284,11 +289,15 @@ function normalizedSessionMcpCredentialHeaders(
|
|
|
284
289
|
const seen = new Set<string>();
|
|
285
290
|
for (const [name, value] of entries) {
|
|
286
291
|
if (!sessionMcpCredentialHeaderName.test(name)) {
|
|
287
|
-
throw new HTTPException(422, {
|
|
292
|
+
throw new HTTPException(422, {
|
|
293
|
+
message: `invalid credential header name: ${name}`,
|
|
294
|
+
});
|
|
288
295
|
}
|
|
289
296
|
const lower = name.toLowerCase();
|
|
290
297
|
if (seen.has(lower)) {
|
|
291
|
-
throw new HTTPException(422, {
|
|
298
|
+
throw new HTTPException(422, {
|
|
299
|
+
message: `duplicate credential header name: ${name}`,
|
|
300
|
+
});
|
|
292
301
|
}
|
|
293
302
|
seen.add(lower);
|
|
294
303
|
if (value.length === 0 || value.length > maxSessionMcpCredentialHeaderValueLength) {
|
|
@@ -388,11 +397,15 @@ function validateSessionMcpServersForCreate(
|
|
|
388
397
|
const metadata: SessionMcpServerMetadata[] = [];
|
|
389
398
|
for (const server of servers) {
|
|
390
399
|
if (seenIds.has(server.id)) {
|
|
391
|
-
throw new HTTPException(422, {
|
|
400
|
+
throw new HTTPException(422, {
|
|
401
|
+
message: `duplicate session MCP server id: ${server.id}`,
|
|
402
|
+
});
|
|
392
403
|
}
|
|
393
404
|
seenIds.add(server.id);
|
|
394
405
|
if (reservedSessionMcpServerIds.has(server.id) || existingIds.has(server.id)) {
|
|
395
|
-
throw new HTTPException(422, {
|
|
406
|
+
throw new HTTPException(422, {
|
|
407
|
+
message: `MCP server id already exists: ${server.id}`,
|
|
408
|
+
});
|
|
396
409
|
}
|
|
397
410
|
const headers = normalizedSessionMcpCredentialHeaders(server.headers);
|
|
398
411
|
const headersEncrypted = Object.fromEntries(
|
|
@@ -490,7 +503,9 @@ function validateSessionMcpCredentialUpdates(input: {
|
|
|
490
503
|
}
|
|
491
504
|
seenIds.add(update.id);
|
|
492
505
|
if (!knownIds.has(update.id)) {
|
|
493
|
-
throw new HTTPException(422, {
|
|
506
|
+
throw new HTTPException(422, {
|
|
507
|
+
message: `unknown session MCP server id: ${update.id}`,
|
|
508
|
+
});
|
|
494
509
|
}
|
|
495
510
|
const headers = normalizedSessionMcpCredentialHeaders(update.headers);
|
|
496
511
|
return {
|
|
@@ -602,10 +617,17 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
602
617
|
// target fails the create (422) — never a silent fall-back to the default box.
|
|
603
618
|
// `workingDir` (optional) is the path/cwd base the chosen machine runs under,
|
|
604
619
|
// seeded alongside the pointer through the epoch-fenced CAS.
|
|
605
|
-
seedTargetSandbox?: {
|
|
620
|
+
seedTargetSandbox?: {
|
|
621
|
+
sandboxId: string;
|
|
622
|
+
settings: Settings;
|
|
623
|
+
workingDir?: string | null;
|
|
624
|
+
} | null;
|
|
606
625
|
// Exact actor-private pre-session draft represented by this create. The
|
|
607
626
|
// initializer consumes it only after the first durable runnable unit commits.
|
|
608
|
-
consumeNewSessionDraft?: {
|
|
627
|
+
consumeNewSessionDraft?: {
|
|
628
|
+
subjectId: string;
|
|
629
|
+
expectedRevision: number;
|
|
630
|
+
} | null;
|
|
609
631
|
// A child may lower its inherited nested-agent depth limit freely; increases
|
|
610
632
|
// are authorized by the caller's workspace:admin grant and checked again by
|
|
611
633
|
// the database admission transaction.
|
|
@@ -767,7 +789,10 @@ async function finishStartSession(
|
|
|
767
789
|
settings: Settings;
|
|
768
790
|
workingDir?: string | null;
|
|
769
791
|
} | null;
|
|
770
|
-
consumeNewSessionDraft?: {
|
|
792
|
+
consumeNewSessionDraft?: {
|
|
793
|
+
subjectId: string;
|
|
794
|
+
expectedRevision: number;
|
|
795
|
+
} | null;
|
|
771
796
|
},
|
|
772
797
|
session: Session,
|
|
773
798
|
): Promise<{ session: CreateSessionResponse; changed: boolean }> {
|
|
@@ -790,7 +815,11 @@ async function finishStartSession(
|
|
|
790
815
|
sessionGroupId: session.sandboxGroupId,
|
|
791
816
|
};
|
|
792
817
|
const seeded = await swapActiveSandbox(
|
|
793
|
-
{
|
|
818
|
+
{
|
|
819
|
+
db: input.db,
|
|
820
|
+
settings: input.seedTargetSandbox.settings,
|
|
821
|
+
bus: input.bus,
|
|
822
|
+
},
|
|
794
823
|
ctx,
|
|
795
824
|
input.seedTargetSandbox.sandboxId,
|
|
796
825
|
// The working dir is committed in the SAME epoch-fenced CAS that seeds the
|
|
@@ -813,7 +842,10 @@ async function finishStartSession(
|
|
|
813
842
|
createdEventPayload: {
|
|
814
843
|
toolPolicy: input.toolPolicy,
|
|
815
844
|
...(input.variableSet
|
|
816
|
-
? {
|
|
845
|
+
? {
|
|
846
|
+
variableSetId: input.variableSet.id,
|
|
847
|
+
variableSetName: input.variableSet.name,
|
|
848
|
+
}
|
|
817
849
|
: {}),
|
|
818
850
|
...(input.sessionMcpServers?.length ? { mcpServers: input.sessionMcpServers } : {}),
|
|
819
851
|
},
|
|
@@ -863,10 +895,10 @@ export function workflowIdForSession(sessionId: string): string {
|
|
|
863
895
|
* it cannot be resolved to a provider at run time, so we fail the request at
|
|
864
896
|
* the API edge with 422 rather than enqueuing a turn the worker can't honor.
|
|
865
897
|
*
|
|
866
|
-
* `model` is the
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
*
|
|
898
|
+
* `model` is the effective value selected by the caller's boundary. Top-level
|
|
899
|
+
* omission defaults to `settings.openaiModel`; child-session omission inherits
|
|
900
|
+
* the worker-signed calling turn before reaching this helper. Centralized here
|
|
901
|
+
* so every model-carrying choke point
|
|
870
902
|
* (create-session, user-message/turn-accept, queued-turn update, and
|
|
871
903
|
* scheduled-task agentConfig — a scheduled task is a session the worker runs
|
|
872
904
|
* later) and the MCP surfaces that share them validate identically and cannot
|
|
@@ -1023,6 +1055,7 @@ export async function postUserMessageTurn(input: {
|
|
|
1023
1055
|
workspaceId: string;
|
|
1024
1056
|
sessionId: string;
|
|
1025
1057
|
text: string;
|
|
1058
|
+
annotations?: TimelineAnnotation[];
|
|
1026
1059
|
turnInstructions?: string | null;
|
|
1027
1060
|
resources: ResourceRef[];
|
|
1028
1061
|
model?: string | null;
|
|
@@ -1040,7 +1073,14 @@ export async function postUserMessageTurn(input: {
|
|
|
1040
1073
|
expectedDraftRevision?: number | null;
|
|
1041
1074
|
reasoningEffortFallback?: Settings["openaiReasoningEffort"];
|
|
1042
1075
|
turnExecutionPolicy: TurnExecutionPolicyV1;
|
|
1043
|
-
|
|
1076
|
+
recordAgentRunUsage?: boolean;
|
|
1077
|
+
schedulePostCommit?: (task: () => Promise<void>) => void;
|
|
1078
|
+
}): Promise<{
|
|
1079
|
+
accepted: SessionEvent;
|
|
1080
|
+
turn: SessionTurn;
|
|
1081
|
+
interruptionCount: number;
|
|
1082
|
+
replay: boolean;
|
|
1083
|
+
}> {
|
|
1044
1084
|
const { db, bus, workflowClient, settings, accountId, workspaceId, sessionId } = input;
|
|
1045
1085
|
const requestedModel = canonicalConfiguredModel(settings, input.model ?? null) ?? null;
|
|
1046
1086
|
const requestedReasoningEffort = input.reasoningEffort ?? null;
|
|
@@ -1061,35 +1101,50 @@ export async function postUserMessageTurn(input: {
|
|
|
1061
1101
|
const operationKey = input.clientEventId ?? crypto.randomUUID();
|
|
1062
1102
|
let result;
|
|
1063
1103
|
try {
|
|
1064
|
-
result = await
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1104
|
+
result = await runIdempotentPersistenceTransaction(
|
|
1105
|
+
{
|
|
1106
|
+
stage: "session.prompt.submit",
|
|
1107
|
+
eventTypes: ["user.message", "turn.queued", "session.status.changed"],
|
|
1108
|
+
maxAttempts: 3,
|
|
1109
|
+
},
|
|
1110
|
+
async () =>
|
|
1111
|
+
await withWorkspaceSubjectSessionActivityRls(
|
|
1112
|
+
db,
|
|
1068
1113
|
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
|
-
|
|
1114
|
+
input.actor ?? accountId,
|
|
1115
|
+
(scoped) =>
|
|
1116
|
+
submitHumanPromptInTransaction(scoped, {
|
|
1117
|
+
accountId,
|
|
1118
|
+
workspaceId,
|
|
1119
|
+
sessionId,
|
|
1120
|
+
subjectId: input.actor ?? accountId,
|
|
1121
|
+
...(input.actorLabel ? { subjectLabel: input.actorLabel } : {}),
|
|
1122
|
+
actor: input.commandActor ?? {
|
|
1123
|
+
type: "human",
|
|
1124
|
+
subjectId: input.actor ?? accountId,
|
|
1125
|
+
},
|
|
1126
|
+
operationKey,
|
|
1127
|
+
delivery: input.delivery ?? "send",
|
|
1128
|
+
controlEtag: input.controlEtag ?? null,
|
|
1129
|
+
expectedDraftRevision: input.expectedDraftRevision ?? null,
|
|
1130
|
+
text: input.text,
|
|
1131
|
+
annotations: input.annotations ?? [],
|
|
1132
|
+
turnInstructions: input.turnInstructions ?? null,
|
|
1133
|
+
resources: input.resources,
|
|
1134
|
+
model: requestedModel,
|
|
1135
|
+
reasoningEffort: requestedReasoningEffort,
|
|
1136
|
+
latencyMode: input.latencyMode ?? null,
|
|
1137
|
+
reasoningEffortFallback:
|
|
1138
|
+
input.reasoningEffortFallback ?? settings.openaiReasoningEffort,
|
|
1139
|
+
turnExecutionPolicy: input.turnExecutionPolicy,
|
|
1140
|
+
source: input.origin === "operator" ? "api" : "user",
|
|
1141
|
+
...(input.recordAgentRunUsage !== undefined
|
|
1142
|
+
? { recordAgentRunUsage: input.recordAgentRunUsage }
|
|
1143
|
+
: {}),
|
|
1144
|
+
personalConnectionDelegations: input.personalConnectionDelegations ?? [],
|
|
1145
|
+
mcpCredentialUpdates: input.mcpCredentialUpdates ?? [],
|
|
1146
|
+
}),
|
|
1147
|
+
),
|
|
1093
1148
|
);
|
|
1094
1149
|
} catch (error) {
|
|
1095
1150
|
if (
|
|
@@ -1106,54 +1161,68 @@ export async function postUserMessageTurn(input: {
|
|
|
1106
1161
|
}
|
|
1107
1162
|
throw error;
|
|
1108
1163
|
}
|
|
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
|
-
);
|
|
1164
|
+
const postCommitTask = async () => {
|
|
1165
|
+
try {
|
|
1166
|
+
await publishDurableSessionEvents(bus, workspaceId, sessionId, result.events);
|
|
1167
|
+
if (result.workspaceControlEventId) {
|
|
1168
|
+
const controlEvent = await getWorkspaceControlEvent(
|
|
1169
|
+
db,
|
|
1170
|
+
workspaceId,
|
|
1171
|
+
result.workspaceControlEventId,
|
|
1172
|
+
);
|
|
1173
|
+
if (!controlEvent) {
|
|
1174
|
+
throw new Error(
|
|
1175
|
+
`Committed workspace control event disappeared: ${result.workspaceControlEventId}`,
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
await publishDurableWorkspaceControlEvent(bus, workspaceId, controlEvent);
|
|
1179
|
+
}
|
|
1180
|
+
} catch {
|
|
1181
|
+
console.warn("[sessions] prompt event fanout failed; durable rows remain replayable", {
|
|
1182
|
+
errorClass: "PromptEventFanoutOperationError",
|
|
1183
|
+
errorCode: "session_prompt_event_fanout_failed",
|
|
1184
|
+
origin: "core",
|
|
1185
|
+
});
|
|
1135
1186
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1187
|
+
try {
|
|
1188
|
+
await workflowClient.wakeSessionWorkflow({
|
|
1189
|
+
accountId,
|
|
1190
|
+
workspaceId,
|
|
1191
|
+
sessionId,
|
|
1192
|
+
workflowId: result.turn.temporalWorkflowId,
|
|
1193
|
+
wakeRevision: result.wakeRevision,
|
|
1194
|
+
...((input.delivery ?? "send") === "steer" || result.interruptionCount > 0
|
|
1195
|
+
? { interruptionRequested: true }
|
|
1196
|
+
: {}),
|
|
1197
|
+
});
|
|
1198
|
+
} catch {
|
|
1199
|
+
console.warn("[sessions] workflow wake failed; durable outbox will retry", {
|
|
1200
|
+
errorClass: "WorkflowWakeOperationError",
|
|
1201
|
+
errorCode: "session_workflow_wake_failed",
|
|
1202
|
+
origin: "core",
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
const schedulePostCommit =
|
|
1207
|
+
input.schedulePostCommit ??
|
|
1208
|
+
((task: () => Promise<void>) => {
|
|
1209
|
+
void task();
|
|
1148
1210
|
});
|
|
1211
|
+
try {
|
|
1212
|
+
schedulePostCommit(postCommitTask);
|
|
1149
1213
|
} catch {
|
|
1150
|
-
console.warn("[sessions]
|
|
1151
|
-
errorClass: "
|
|
1152
|
-
errorCode: "
|
|
1214
|
+
console.warn("[sessions] prompt post-commit scheduling failed; durable recovery remains", {
|
|
1215
|
+
errorClass: "PromptPostCommitScheduleError",
|
|
1216
|
+
errorCode: "session_prompt_post_commit_schedule_failed",
|
|
1153
1217
|
origin: "core",
|
|
1154
1218
|
});
|
|
1155
1219
|
}
|
|
1156
|
-
return {
|
|
1220
|
+
return {
|
|
1221
|
+
accepted: result.accepted,
|
|
1222
|
+
turn: result.turn,
|
|
1223
|
+
interruptionCount: result.interruptionCount,
|
|
1224
|
+
replay: result.replay,
|
|
1225
|
+
};
|
|
1157
1226
|
}
|
|
1158
1227
|
|
|
1159
1228
|
/**
|
|
@@ -1213,10 +1282,24 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1213
1282
|
message: `parent session not found in workspace: ${parentSessionId}`,
|
|
1214
1283
|
});
|
|
1215
1284
|
}
|
|
1285
|
+
const creationInitiator = creationInitiatorForGrant(grant);
|
|
1286
|
+
const parentCallingTurn =
|
|
1287
|
+
parentSession && creationInitiator.actor
|
|
1288
|
+
? await getSessionTurn(db, workspaceId, creationInitiator.actor.turnId)
|
|
1289
|
+
: null;
|
|
1290
|
+
if (
|
|
1291
|
+
creationInitiator.actor &&
|
|
1292
|
+
(!parentCallingTurn || parentCallingTurn.sessionId !== parentSession?.id)
|
|
1293
|
+
) {
|
|
1294
|
+
throw new HTTPException(403, {
|
|
1295
|
+
message: "caller attempt does not belong to the parent session",
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1216
1298
|
const capabilityRuntimeSettings = await settingsWithEnabledCapabilityMcpServers(
|
|
1217
1299
|
db,
|
|
1218
1300
|
workspaceId,
|
|
1219
1301
|
settings,
|
|
1302
|
+
{ subjectId: grant.subjectId },
|
|
1220
1303
|
);
|
|
1221
1304
|
const sessionMcpServers = hasOwnProperty(rawPayload, "mcpServers")
|
|
1222
1305
|
? validateSessionMcpServersForCreate(capabilityRuntimeSettings, grant, payload.mcpServers)
|
|
@@ -1263,7 +1346,10 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1263
1346
|
"child tools may only narrow the parent session tool policy",
|
|
1264
1347
|
);
|
|
1265
1348
|
selectedTools = requestedTools;
|
|
1266
|
-
toolPolicy = {
|
|
1349
|
+
toolPolicy = {
|
|
1350
|
+
mode: "explicit",
|
|
1351
|
+
inheritedFromSessionId: parentSession.id,
|
|
1352
|
+
};
|
|
1267
1353
|
} else {
|
|
1268
1354
|
selectedTools = parentEffective;
|
|
1269
1355
|
toolPolicy = {
|
|
@@ -1296,7 +1382,9 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1296
1382
|
});
|
|
1297
1383
|
await validateGitHubRepositorySelection(db, workspaceId, resources);
|
|
1298
1384
|
if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
1299
|
-
throw new HTTPException(503, {
|
|
1385
|
+
throw new HTTPException(503, {
|
|
1386
|
+
message: "object storage is not configured",
|
|
1387
|
+
});
|
|
1300
1388
|
}
|
|
1301
1389
|
await validateFileResources(db, workspaceId, resources);
|
|
1302
1390
|
// VariableSet attachment requires variable-sets:use on the calling grant
|
|
@@ -1341,25 +1429,56 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1341
1429
|
frozenRigVersionId = rig.activeVersion.id;
|
|
1342
1430
|
}
|
|
1343
1431
|
}
|
|
1344
|
-
|
|
1432
|
+
// A spawned worker is causally part of the exact turn that created it. Omitted
|
|
1433
|
+
// execution policy fields therefore inherit that calling turn rather than the
|
|
1434
|
+
// deployment defaults. This is especially important for Codex subscription
|
|
1435
|
+
// managers: falling back to the deployment model would silently move a child
|
|
1436
|
+
// onto the OpenGeni-credits billing path. Legacy session-bound grants without
|
|
1437
|
+
// exact attempt claims fall back to the parent session's persisted defaults.
|
|
1438
|
+
const inheritedModel = parentCallingTurn?.model ?? parentSession?.model ?? settings.openaiModel;
|
|
1439
|
+
const model = canonicalConfiguredModel(settings, payload.model ?? inheritedModel);
|
|
1345
1440
|
if (model === null || model === undefined) {
|
|
1346
1441
|
throw new Error("effective session model unexpectedly resolved to null");
|
|
1347
1442
|
}
|
|
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).
|
|
1443
|
+
// Session creation persists the EFFECTIVE model — the explicit selection,
|
|
1444
|
+
// inherited calling-turn model, or deployment default — so the policy must
|
|
1445
|
+
// vet that effective value, not just explicit ones (a restricted workspace's
|
|
1446
|
+
// inherited/default-model session would otherwise be born blocked).
|
|
1352
1447
|
await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, model);
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1448
|
+
const inheritedReasoningEffort =
|
|
1449
|
+
parentCallingTurn?.reasoningEffort ??
|
|
1450
|
+
(parentSession
|
|
1451
|
+
? reasoningEffortForSession(parentSession.metadata, settings.openaiReasoningEffort)
|
|
1452
|
+
: settings.openaiReasoningEffort);
|
|
1453
|
+
const inheritedLatencyMode =
|
|
1454
|
+
parentCallingTurn?.latencyMode ??
|
|
1455
|
+
(parentSession ? latencyModeForSession(parentSession.metadata, "standard") : "standard");
|
|
1456
|
+
const reasoningEffort = payload.reasoningEffort ?? inheritedReasoningEffort;
|
|
1457
|
+
const latencyMode = payload.latencyMode ?? inheritedLatencyMode;
|
|
1458
|
+
const inheritedFromParent = parentSession !== null;
|
|
1355
1459
|
const turnExecutionPolicy = resolveTurnExecutionPolicyV1(settings, {
|
|
1356
1460
|
modelId: model,
|
|
1357
1461
|
requestedModelId: payload.model ?? null,
|
|
1358
|
-
modelSource:
|
|
1462
|
+
modelSource:
|
|
1463
|
+
payload.model === undefined
|
|
1464
|
+
? inheritedFromParent
|
|
1465
|
+
? "continuation"
|
|
1466
|
+
: "deployment"
|
|
1467
|
+
: "explicit",
|
|
1359
1468
|
reasoningEffort,
|
|
1360
|
-
reasoningSource:
|
|
1469
|
+
reasoningSource:
|
|
1470
|
+
payload.reasoningEffort === undefined
|
|
1471
|
+
? inheritedFromParent
|
|
1472
|
+
? "continuation"
|
|
1473
|
+
: "deployment"
|
|
1474
|
+
: "explicit",
|
|
1361
1475
|
latencyMode,
|
|
1362
|
-
latencyModeSource:
|
|
1476
|
+
latencyModeSource:
|
|
1477
|
+
payload.latencyMode === undefined
|
|
1478
|
+
? inheritedFromParent
|
|
1479
|
+
? "continuation"
|
|
1480
|
+
: "deployment"
|
|
1481
|
+
: "explicit",
|
|
1363
1482
|
});
|
|
1364
1483
|
// Parent linkage was resolved above, before context validation. A child with
|
|
1365
1484
|
// no explicit permission override inherits the creating session's effective
|
|
@@ -1664,7 +1783,6 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1664
1783
|
model,
|
|
1665
1784
|
});
|
|
1666
1785
|
}
|
|
1667
|
-
const creationInitiator = creationInitiatorForGrant(grant);
|
|
1668
1786
|
let createOutcome: CreateSessionOutcome;
|
|
1669
1787
|
try {
|
|
1670
1788
|
createOutcome = await createAndStartSessionWithOutcome({
|
|
@@ -1727,7 +1845,11 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1727
1845
|
// (after the row exists, before the first turn dispatches). Validation
|
|
1728
1846
|
// (ownership/liveness) lives in swapActiveSandbox; an invalid target 422s.
|
|
1729
1847
|
seedTargetSandbox: payload.targetSandboxId
|
|
1730
|
-
? {
|
|
1848
|
+
? {
|
|
1849
|
+
sandboxId: payload.targetSandboxId,
|
|
1850
|
+
settings,
|
|
1851
|
+
workingDir: payload.workingDir ?? null,
|
|
1852
|
+
}
|
|
1731
1853
|
: null,
|
|
1732
1854
|
consumeNewSessionDraft:
|
|
1733
1855
|
payload.expectedNewSessionDraftRevision !== undefined
|
|
@@ -1810,6 +1932,7 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1810
1932
|
sessionId: string,
|
|
1811
1933
|
input: {
|
|
1812
1934
|
text: string;
|
|
1935
|
+
annotations?: SubmittedTimelineAnnotation[];
|
|
1813
1936
|
turnInstructions?: string | null;
|
|
1814
1937
|
resources?: ResourceRef[];
|
|
1815
1938
|
model?: string | null;
|
|
@@ -1822,7 +1945,12 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1822
1945
|
controlEtag?: string | null;
|
|
1823
1946
|
expectedDraftRevision?: number | null;
|
|
1824
1947
|
},
|
|
1825
|
-
): Promise<{
|
|
1948
|
+
): Promise<{
|
|
1949
|
+
accepted: SessionEvent;
|
|
1950
|
+
turn: SessionTurn;
|
|
1951
|
+
interruptionCount: number;
|
|
1952
|
+
replay: boolean;
|
|
1953
|
+
}> {
|
|
1826
1954
|
const { settings, db, bus, workflowClient, objectStorage } = deps;
|
|
1827
1955
|
await requireSessionAuthorization(deps, grant, {
|
|
1828
1956
|
sessionId,
|
|
@@ -1864,6 +1992,12 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1864
1992
|
latencyModeSource: input.latencyMode == null ? "session" : "explicit",
|
|
1865
1993
|
});
|
|
1866
1994
|
const requestedResources = normalizeResources(input.resources ?? []);
|
|
1995
|
+
const annotations = await validateSubmittedTimelineAnnotations(
|
|
1996
|
+
db,
|
|
1997
|
+
workspaceId,
|
|
1998
|
+
sessionId,
|
|
1999
|
+
input.annotations ?? [],
|
|
2000
|
+
);
|
|
1867
2001
|
await requireLimit(deps, {
|
|
1868
2002
|
accountId: grant.accountId,
|
|
1869
2003
|
workspaceId,
|
|
@@ -1872,7 +2006,9 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1872
2006
|
model: effectiveModel,
|
|
1873
2007
|
});
|
|
1874
2008
|
if (requestedResources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
1875
|
-
throw new HTTPException(503, {
|
|
2009
|
+
throw new HTTPException(503, {
|
|
2010
|
+
message: "object storage is not configured",
|
|
2011
|
+
});
|
|
1876
2012
|
}
|
|
1877
2013
|
await validateFileResources(db, workspaceId, requestedResources);
|
|
1878
2014
|
await validateGitHubRepositorySelection(db, workspaceId, [
|
|
@@ -1885,7 +2021,9 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1885
2021
|
session: existingSession,
|
|
1886
2022
|
updates: input.mcpCredentialUpdates ?? [],
|
|
1887
2023
|
});
|
|
1888
|
-
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings
|
|
2024
|
+
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings, {
|
|
2025
|
+
subjectId: grant.subjectId,
|
|
2026
|
+
});
|
|
1889
2027
|
const personalConnectionDelegations = await freezePersonalConnectionDelegations({
|
|
1890
2028
|
db,
|
|
1891
2029
|
workspaceId,
|
|
@@ -1894,7 +2032,7 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1894
2032
|
source: personalConnectionDelegationSourceForGrant(grant),
|
|
1895
2033
|
});
|
|
1896
2034
|
const delegatedServiceInitiator = serviceInitiatorForGrant(grant);
|
|
1897
|
-
const { accepted, turn, replay } = await postUserMessageTurn({
|
|
2035
|
+
const { accepted, turn, interruptionCount, replay } = await postUserMessageTurn({
|
|
1898
2036
|
db,
|
|
1899
2037
|
bus,
|
|
1900
2038
|
workflowClient,
|
|
@@ -1903,6 +2041,7 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1903
2041
|
workspaceId,
|
|
1904
2042
|
sessionId,
|
|
1905
2043
|
text: input.text,
|
|
2044
|
+
annotations,
|
|
1906
2045
|
turnInstructions: input.turnInstructions ?? null,
|
|
1907
2046
|
resources: requestedResources,
|
|
1908
2047
|
model: input.model ?? null,
|
|
@@ -1933,24 +2072,10 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1933
2072
|
? { expectedDraftRevision: input.expectedDraftRevision }
|
|
1934
2073
|
: {}),
|
|
1935
2074
|
...(input.clientEventId ? { clientEventId: input.clientEventId } : {}),
|
|
2075
|
+
recordAgentRunUsage: true,
|
|
2076
|
+
...(deps.schedulePromptPostCommit ? { schedulePostCommit: deps.schedulePromptPostCommit } : {}),
|
|
1936
2077
|
});
|
|
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 };
|
|
2078
|
+
return { accepted, turn, interruptionCount, replay };
|
|
1954
2079
|
}
|
|
1955
2080
|
|
|
1956
2081
|
/** Backward-compatible entity-returning path used by existing REST callers. */
|
|
@@ -1960,15 +2085,20 @@ export async function acceptSessionUserMessage(
|
|
|
1960
2085
|
workspaceId: Parameters<typeof acceptSessionUserMessageWithOutcome>[2],
|
|
1961
2086
|
sessionId: Parameters<typeof acceptSessionUserMessageWithOutcome>[3],
|
|
1962
2087
|
input: Parameters<typeof acceptSessionUserMessageWithOutcome>[4],
|
|
1963
|
-
): Promise<{
|
|
1964
|
-
|
|
2088
|
+
): Promise<{
|
|
2089
|
+
accepted: SessionEvent;
|
|
2090
|
+
turn: SessionTurn;
|
|
2091
|
+
interruptionCount: number;
|
|
2092
|
+
replay: boolean;
|
|
2093
|
+
}> {
|
|
2094
|
+
const { accepted, turn, interruptionCount, replay } = await acceptSessionUserMessageWithOutcome(
|
|
1965
2095
|
deps,
|
|
1966
2096
|
grant,
|
|
1967
2097
|
workspaceId,
|
|
1968
2098
|
sessionId,
|
|
1969
2099
|
input,
|
|
1970
2100
|
);
|
|
1971
|
-
return { accepted, turn };
|
|
2101
|
+
return { accepted, turn, interruptionCount, replay };
|
|
1972
2102
|
}
|
|
1973
2103
|
|
|
1974
2104
|
/**
|
|
@@ -2002,7 +2132,12 @@ export async function updateSessionTitle(
|
|
|
2002
2132
|
surface: "core",
|
|
2003
2133
|
});
|
|
2004
2134
|
const workspaceId = grant.workspaceId;
|
|
2005
|
-
const result = await updateSessionTitleRow(db, {
|
|
2135
|
+
const result = await updateSessionTitleRow(db, {
|
|
2136
|
+
workspaceId,
|
|
2137
|
+
sessionId,
|
|
2138
|
+
title,
|
|
2139
|
+
source,
|
|
2140
|
+
});
|
|
2006
2141
|
if (result.updated) {
|
|
2007
2142
|
await appendAndPublishEvents(db, bus, workspaceId, sessionId, [
|
|
2008
2143
|
{
|
|
@@ -2053,7 +2188,9 @@ export async function updateSessionMcpApprovalPolicy(
|
|
|
2053
2188
|
async (_session, context) => {
|
|
2054
2189
|
const result = await context.updateSessionMcpApprovalPolicy(serverId, normalizedPolicy);
|
|
2055
2190
|
if (!result.server) {
|
|
2056
|
-
throw new HTTPException(404, {
|
|
2191
|
+
throw new HTTPException(404, {
|
|
2192
|
+
message: "session MCP server not found",
|
|
2193
|
+
});
|
|
2057
2194
|
}
|
|
2058
2195
|
outcome.server = result.server;
|
|
2059
2196
|
return {
|
|
@@ -2070,6 +2207,7 @@ export async function updateSessionMcpApprovalPolicy(
|
|
|
2070
2207
|
: [],
|
|
2071
2208
|
};
|
|
2072
2209
|
},
|
|
2210
|
+
{ activity: "semantic" },
|
|
2073
2211
|
);
|
|
2074
2212
|
const updatedServer = outcome.server;
|
|
2075
2213
|
if (!updatedServer) {
|
|
@@ -2153,6 +2291,7 @@ export async function updateSessionToolPolicy(
|
|
|
2153
2291
|
deps.db,
|
|
2154
2292
|
grant.workspaceId,
|
|
2155
2293
|
deps.settings,
|
|
2294
|
+
{ subjectId: grant.subjectId },
|
|
2156
2295
|
);
|
|
2157
2296
|
const runtimeSettings = settingsWithSessionMcpServerMetadata(
|
|
2158
2297
|
capabilityRuntimeSettings,
|
|
@@ -2168,7 +2307,9 @@ export async function updateSessionToolPolicy(
|
|
|
2168
2307
|
(tool) => !validatedIds.has(`${tool.kind}:${tool.id}`),
|
|
2169
2308
|
);
|
|
2170
2309
|
if (unknown) {
|
|
2171
|
-
throw new HTTPException(422, {
|
|
2310
|
+
throw new HTTPException(422, {
|
|
2311
|
+
message: `unknown MCP server id: ${unknown.id}`,
|
|
2312
|
+
});
|
|
2172
2313
|
}
|
|
2173
2314
|
return withFirstPartyTools(validatedTools, runtimeSettings);
|
|
2174
2315
|
})()
|
|
@@ -2197,7 +2338,9 @@ export async function updateSessionToolPolicy(
|
|
|
2197
2338
|
if (session.parentSessionId) {
|
|
2198
2339
|
const parent = await context.getLockedSession(session.parentSessionId);
|
|
2199
2340
|
if (!parent) {
|
|
2200
|
-
throw new HTTPException(409, {
|
|
2341
|
+
throw new HTTPException(409, {
|
|
2342
|
+
message: "parent session is no longer available",
|
|
2343
|
+
});
|
|
2201
2344
|
}
|
|
2202
2345
|
const parentTracksWorkspaceDefaults = parent.toolPolicy?.mode === "workspace_default";
|
|
2203
2346
|
const parentEffective = withFirstPartyTools(
|
|
@@ -2310,7 +2453,7 @@ export async function updateSessionToolPolicy(
|
|
|
2310
2453
|
},
|
|
2311
2454
|
};
|
|
2312
2455
|
},
|
|
2313
|
-
{ lockParentSession: true },
|
|
2456
|
+
{ activity: "semantic", lockParentSession: true },
|
|
2314
2457
|
);
|
|
2315
2458
|
if (events.length > 0) {
|
|
2316
2459
|
await publishDurableSessionEvents(deps.bus, grant.workspaceId, sessionId, events);
|