@opengeni/core 0.24.1 → 0.28.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/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-IBOEYG6N.js +34 -0
- package/dist/chunk-IBOEYG6N.js.map +1 -0
- package/dist/dependencies.d.ts +5 -1
- package/dist/domain/conversation-integrations.d.ts +225 -0
- package/dist/domain/fiken.d.ts +35 -0
- package/dist/domain/sessions.d.ts +11 -3
- package/dist/domain/video-generation-capabilities.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1667 -664
- package/dist/index.js.map +1 -1
- package/dist/managed-session.d.ts +5 -1
- package/dist/sandbox/fleet.d.ts +8 -5
- package/dist/session-authorization.d.ts +3 -1
- package/dist/transcription.d.ts +5 -0
- package/package.json +14 -10
- package/src/access/index.ts +14 -2
- package/src/canonical-human-identities.ts +27 -0
- package/src/dependencies.ts +5 -1
- package/src/domain/capabilities.ts +220 -283
- package/src/domain/conversation-integrations.ts +1238 -0
- package/src/domain/fiken.ts +88 -0
- package/src/domain/insights.ts +27 -17
- package/src/domain/scheduled-tasks.ts +16 -0
- package/src/domain/sessions.ts +122 -18
- package/src/domain/video-generation-capabilities.ts +37 -2
- package/src/index.ts +2 -0
- package/src/managed-session.ts +19 -2
- package/src/sandbox/fleet.ts +72 -66
- package/src/session-authorization.ts +187 -55
- package/src/transcription.ts +5 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FIKEN_CREDENTIAL_LABEL,
|
|
3
|
+
FIKEN_CREDENTIAL_ROLE,
|
|
4
|
+
FIKEN_PROVIDER_DOMAIN,
|
|
5
|
+
FikenConnectionMetadata,
|
|
6
|
+
type ConnectionMetadata,
|
|
7
|
+
type FikenCompanySummary,
|
|
8
|
+
type FikenConnectionMetadata as FikenMetadata,
|
|
9
|
+
} from "@opengeni/contracts";
|
|
10
|
+
|
|
11
|
+
export function fikenConnectionMetadata(metadata: Record<string, unknown>): FikenMetadata | null {
|
|
12
|
+
const parsed = FikenConnectionMetadata.safeParse(metadata);
|
|
13
|
+
return parsed.success ? parsed.data : null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A workspace-shared Fiken connection created through one of the verified
|
|
18
|
+
* install paths: the pasted personal API token (`api_key`) or the Fiken OAuth
|
|
19
|
+
* app flow (`oauth2`). Both are deliberately workspace-owned; personal
|
|
20
|
+
* ("Connect only for me") ownership needs the delegation-snapshot lane and is
|
|
21
|
+
* not yet wired for the first-party fiken tools.
|
|
22
|
+
*/
|
|
23
|
+
export function isFikenConnection(connection: ConnectionMetadata): boolean {
|
|
24
|
+
return (
|
|
25
|
+
connection.subjectId === null &&
|
|
26
|
+
connection.providerDomain === FIKEN_PROVIDER_DOMAIN &&
|
|
27
|
+
(connection.kind === "api_key" || connection.kind === "oauth2") &&
|
|
28
|
+
fikenConnectionMetadata(connection.metadata)?.credentialRole === FIKEN_CREDENTIAL_ROLE
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The Fiken credential role is reserved for the verified install routes.
|
|
34
|
+
* Generic connection create/update must reject it so a caller cannot forge a
|
|
35
|
+
* "verified" Fiken row (with attacker-chosen companies metadata) that the
|
|
36
|
+
* first-party fiken tools would then bind to. Mirrors the Slack bot guard.
|
|
37
|
+
*/
|
|
38
|
+
export function hasReservedFikenMetadata(
|
|
39
|
+
metadata: Record<string, unknown> | null | undefined,
|
|
40
|
+
): boolean {
|
|
41
|
+
return (
|
|
42
|
+
metadata?.credentialRole === FIKEN_CREDENTIAL_ROLE ||
|
|
43
|
+
metadata?.credentialLabel === FIKEN_CREDENTIAL_LABEL
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* One default-company rule for both verified lanes: an explicit request wins
|
|
49
|
+
* when the credential can access it, then a still-accessible previous default
|
|
50
|
+
* survives reconnect, then a single-company credential auto-selects. Returns
|
|
51
|
+
* null (caller decides whether that is an error) when a requested slug is not
|
|
52
|
+
* among the verified companies.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveFikenDefaultCompanySlug(input: {
|
|
55
|
+
requested: string | null;
|
|
56
|
+
previous: string | null;
|
|
57
|
+
companies: readonly FikenCompanySummary[];
|
|
58
|
+
}): string | null {
|
|
59
|
+
const accessible = (slug: string | null): slug is string =>
|
|
60
|
+
slug !== null && input.companies.some((company) => company.slug === slug);
|
|
61
|
+
if (input.requested !== null) {
|
|
62
|
+
return accessible(input.requested) ? input.requested : null;
|
|
63
|
+
}
|
|
64
|
+
if (accessible(input.previous)) {
|
|
65
|
+
return input.previous;
|
|
66
|
+
}
|
|
67
|
+
return input.companies.length === 1 ? input.companies[0]!.slug : null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The row a capability tile or tool default should bind to when several Fiken
|
|
72
|
+
* connections exist: usable status first, then newest update, then immutable
|
|
73
|
+
* UUID descending as the stable tie-breaker.
|
|
74
|
+
*/
|
|
75
|
+
export function preferredFikenConnection<
|
|
76
|
+
T extends Pick<ConnectionMetadata, "status" | "updatedAt" | "id">,
|
|
77
|
+
>(connections: readonly T[]): T | null {
|
|
78
|
+
const statusRank = (status: ConnectionMetadata["status"]): number =>
|
|
79
|
+
status === "active" ? 0 : status === "needs_reauth" ? 1 : 2;
|
|
80
|
+
return (
|
|
81
|
+
[...connections].sort(
|
|
82
|
+
(left, right) =>
|
|
83
|
+
statusRank(left.status) - statusRank(right.status) ||
|
|
84
|
+
right.updatedAt.localeCompare(left.updatedAt) ||
|
|
85
|
+
right.id.localeCompare(left.id),
|
|
86
|
+
)[0] ?? null
|
|
87
|
+
);
|
|
88
|
+
}
|
package/src/domain/insights.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
aggregateModelCallFacts,
|
|
9
9
|
aggregateModelCallFactsByDay,
|
|
10
|
+
aggregateModelCallFactsByHour,
|
|
10
11
|
aggregateRootSessionDrivers,
|
|
11
12
|
aggregateScheduleFacts,
|
|
12
13
|
aggregateSessionDepth,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
countScheduledTaskFires,
|
|
16
17
|
countSessionsAttachedToGroups,
|
|
17
18
|
enumerateUtcDays,
|
|
19
|
+
enumerateUtcHours,
|
|
18
20
|
listFloorSessions,
|
|
19
21
|
listLiveWarmLeases,
|
|
20
22
|
listModelCallFacets,
|
|
@@ -23,6 +25,7 @@ import {
|
|
|
23
25
|
requireWorkspace,
|
|
24
26
|
sumUsageQuantity,
|
|
25
27
|
sumUsageQuantityByDay,
|
|
28
|
+
sumUsageQuantityByHour,
|
|
26
29
|
sumUsageQuantityInRange,
|
|
27
30
|
type Database,
|
|
28
31
|
} from "@opengeni/db";
|
|
@@ -82,29 +85,29 @@ function resolveRangeWindow(
|
|
|
82
85
|
since = startOfUtcDay(now);
|
|
83
86
|
rangeLabel = "Today (UTC)";
|
|
84
87
|
priorLabel = "Prior equal window";
|
|
85
|
-
seriesLabel = "Credit $
|
|
86
|
-
cacheSeriesLabel = "Cache hit %";
|
|
88
|
+
seriesLabel = "Credit $ / UTC hour";
|
|
89
|
+
cacheSeriesLabel = "Cache hit % / UTC hour";
|
|
87
90
|
break;
|
|
88
91
|
case "week":
|
|
89
92
|
since = new Date(startOfUtcDay(now).getTime() - 6 * 24 * 60 * 60 * 1000);
|
|
90
93
|
rangeLabel = "Last 7 days (UTC)";
|
|
91
94
|
priorLabel = "Prior 7 days";
|
|
92
|
-
seriesLabel = "Credit $ / day";
|
|
93
|
-
cacheSeriesLabel = "Cache hit % / day";
|
|
95
|
+
seriesLabel = "Credit $ / UTC day";
|
|
96
|
+
cacheSeriesLabel = "Cache hit % / UTC day";
|
|
94
97
|
break;
|
|
95
98
|
case "month":
|
|
96
99
|
since = startOfUtcMonth(now);
|
|
97
100
|
rangeLabel = "This month (UTC)";
|
|
98
101
|
priorLabel = "Prior equal window";
|
|
99
|
-
seriesLabel = "Credit $ / day";
|
|
100
|
-
cacheSeriesLabel = "Cache hit % / day";
|
|
102
|
+
seriesLabel = "Credit $ / UTC day";
|
|
103
|
+
cacheSeriesLabel = "Cache hit % / UTC day";
|
|
101
104
|
break;
|
|
102
105
|
case "ytd":
|
|
103
106
|
since = startOfUtcYear(now);
|
|
104
107
|
rangeLabel = "Year to date (UTC)";
|
|
105
108
|
priorLabel = "Prior equal window";
|
|
106
|
-
seriesLabel = "Credit $ / day";
|
|
107
|
-
cacheSeriesLabel = "Cache hit % / day";
|
|
109
|
+
seriesLabel = "Credit $ / UTC day";
|
|
110
|
+
cacheSeriesLabel = "Cache hit % / UTC day";
|
|
108
111
|
break;
|
|
109
112
|
default: {
|
|
110
113
|
const _exhaustive: never = range;
|
|
@@ -182,6 +185,10 @@ export async function getWorkspaceInsights(
|
|
|
182
185
|
const model = input.model?.trim() || null;
|
|
183
186
|
const modelFilterActive = Boolean(provider || model);
|
|
184
187
|
const filter = { provider, model };
|
|
188
|
+
const aggregateFactsForSeries =
|
|
189
|
+
input.range === "today" ? aggregateModelCallFactsByHour : aggregateModelCallFactsByDay;
|
|
190
|
+
const sumUsageForSeries =
|
|
191
|
+
input.range === "today" ? sumUsageQuantityByHour : sumUsageQuantityByDay;
|
|
185
192
|
|
|
186
193
|
const [
|
|
187
194
|
workspaceCreditMicros,
|
|
@@ -242,19 +249,19 @@ export async function getWorkspaceInsights(
|
|
|
242
249
|
until: window.priorUntil,
|
|
243
250
|
...filter,
|
|
244
251
|
}),
|
|
245
|
-
|
|
252
|
+
aggregateFactsForSeries(db, {
|
|
246
253
|
workspaceId: input.workspaceId,
|
|
247
254
|
since: window.since,
|
|
248
255
|
until: window.until,
|
|
249
256
|
...filter,
|
|
250
257
|
}),
|
|
251
|
-
|
|
258
|
+
sumUsageForSeries(db, {
|
|
252
259
|
workspaceId: input.workspaceId,
|
|
253
260
|
eventType: "sandbox.warm_seconds",
|
|
254
261
|
since: window.since,
|
|
255
262
|
until: window.until,
|
|
256
263
|
}),
|
|
257
|
-
|
|
264
|
+
sumUsageForSeries(db, {
|
|
258
265
|
workspaceId: input.workspaceId,
|
|
259
266
|
eventType: "model.cost",
|
|
260
267
|
since: window.since,
|
|
@@ -382,9 +389,12 @@ export async function getWorkspaceInsights(
|
|
|
382
389
|
const priorCacheInputTokens = priorModelRows.reduce((sum, row) => sum + row.cacheInputTokens, 0);
|
|
383
390
|
const priorCalls = priorModelRows.reduce((sum, row) => sum + row.calls, 0);
|
|
384
391
|
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
392
|
+
const buckets =
|
|
393
|
+
input.range === "today"
|
|
394
|
+
? enumerateUtcHours(window.since, window.until)
|
|
395
|
+
: enumerateUtcDays(window.since, window.until);
|
|
396
|
+
const series = buckets.map((bucket) => {
|
|
397
|
+
const facts = factDays.get(bucket) ?? {
|
|
388
398
|
costMicros: 0,
|
|
389
399
|
estimatedProviderCostMicros: 0,
|
|
390
400
|
estimatedProviderCostKnownCalls: 0,
|
|
@@ -401,13 +411,13 @@ export async function getWorkspaceInsights(
|
|
|
401
411
|
};
|
|
402
412
|
const modelCostMicros = modelFilterActive
|
|
403
413
|
? facts.costMicros
|
|
404
|
-
: (costDays.get(
|
|
414
|
+
: (costDays.get(bucket) ?? facts.costMicros);
|
|
405
415
|
return {
|
|
406
|
-
label:
|
|
416
|
+
label: input.range === "today" ? bucket.slice(11) : bucket.slice(5),
|
|
407
417
|
modelCostUsd: microsToUsd(modelCostMicros),
|
|
408
418
|
estimatedProviderUsd: microsToUsd(facts.estimatedProviderCostMicros),
|
|
409
419
|
estimatedProviderCostKnownCalls: facts.estimatedProviderCostKnownCalls,
|
|
410
|
-
warmSeconds: warmDays.get(
|
|
420
|
+
warmSeconds: warmDays.get(bucket) ?? 0,
|
|
411
421
|
inputTokens: facts.inputTokens,
|
|
412
422
|
outputTokens: facts.outputTokens,
|
|
413
423
|
cachedTokens: facts.cachedTokens,
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
SessionAuthorizationSurface,
|
|
11
11
|
CreateScheduledTaskRequest as CreateScheduledTaskPayload,
|
|
12
12
|
UpdateScheduledTaskRequest as UpdateScheduledTaskPayload,
|
|
13
|
+
XaiProviderAccountAuthoritySnapshotV1,
|
|
13
14
|
} from "@opengeni/contracts";
|
|
14
15
|
import { OPENGENI_SLACK_BOT_SESSION_METADATA_KEY } from "@opengeni/contracts";
|
|
15
16
|
import {
|
|
@@ -21,10 +22,12 @@ import {
|
|
|
21
22
|
getRig,
|
|
22
23
|
getScheduledTask,
|
|
23
24
|
getScheduledTaskPersonalConnectionDelegations,
|
|
25
|
+
getSessionTurnXaiProviderAccountAuthoritySnapshot,
|
|
24
26
|
getSession,
|
|
25
27
|
requireWorkspace,
|
|
26
28
|
scopedKnowledgeScopeKey,
|
|
27
29
|
updateScheduledTask,
|
|
30
|
+
resolveXaiProviderAccountAuthoritySnapshotForAcceptance,
|
|
28
31
|
type Database,
|
|
29
32
|
type UpdateScheduledTaskInput,
|
|
30
33
|
} from "@opengeni/db";
|
|
@@ -161,6 +164,18 @@ export async function createValidatedScheduledTask(input: {
|
|
|
161
164
|
source: personalConnectionDelegationSourceForGrant(input.grant),
|
|
162
165
|
});
|
|
163
166
|
const creationInitiator = creationInitiatorForGrant(input.grant);
|
|
167
|
+
const xaiProviderAccountAuthoritySnapshot: XaiProviderAccountAuthoritySnapshotV1 =
|
|
168
|
+
creationInitiator.actor
|
|
169
|
+
? await getSessionTurnXaiProviderAccountAuthoritySnapshot(
|
|
170
|
+
input.db,
|
|
171
|
+
input.grant.workspaceId,
|
|
172
|
+
creationInitiator.actor.sessionId,
|
|
173
|
+
creationInitiator.actor.turnId,
|
|
174
|
+
)
|
|
175
|
+
: await resolveXaiProviderAccountAuthoritySnapshotForAcceptance(input.db, {
|
|
176
|
+
workspaceId: input.grant.workspaceId,
|
|
177
|
+
subjectId: input.grant.subjectId,
|
|
178
|
+
});
|
|
164
179
|
return await createScheduledTask(input.db, {
|
|
165
180
|
id,
|
|
166
181
|
accountId: input.grant.accountId,
|
|
@@ -177,6 +192,7 @@ export async function createValidatedScheduledTask(input: {
|
|
|
177
192
|
...(creationInitiator.context ? { createdByContext: creationInitiator.context } : {}),
|
|
178
193
|
createdByActor: creationInitiator.actor ?? null,
|
|
179
194
|
personalConnectionDelegations,
|
|
195
|
+
xaiProviderAccountAuthoritySnapshot,
|
|
180
196
|
targetSessionId: target?.id ?? null,
|
|
181
197
|
variableSetId: input.payload.variableSetId ?? null,
|
|
182
198
|
rigId: input.payload.rigId ?? null,
|
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 {
|
|
@@ -46,6 +48,7 @@ import {
|
|
|
46
48
|
type TurnInitiator,
|
|
47
49
|
type TurnInitiatorContext,
|
|
48
50
|
type TurnExecutionPolicyV1,
|
|
51
|
+
type XaiProviderAccountAuthoritySnapshotV1,
|
|
49
52
|
} from "@opengeni/contracts";
|
|
50
53
|
import {
|
|
51
54
|
createSession,
|
|
@@ -53,6 +56,7 @@ import {
|
|
|
53
56
|
encryptVariableSetValue,
|
|
54
57
|
getAnySessionInGroup,
|
|
55
58
|
getEnrollment,
|
|
59
|
+
getChannel,
|
|
56
60
|
getRig,
|
|
57
61
|
getWorkspaceDefaultRigId,
|
|
58
62
|
listDistinctVariableSetIdsInGroup,
|
|
@@ -64,6 +68,7 @@ import {
|
|
|
64
68
|
getWorkspaceControlEvent,
|
|
65
69
|
getSessionLineage,
|
|
66
70
|
getSessionTurn,
|
|
71
|
+
getSessionTurnXaiProviderAccountAuthoritySnapshot,
|
|
67
72
|
getWorkspaceModelPolicy,
|
|
68
73
|
initializeSessionStartAtomically,
|
|
69
74
|
listSessionTurns,
|
|
@@ -98,7 +103,10 @@ import type {
|
|
|
98
103
|
ApiRouteDeps,
|
|
99
104
|
SessionWorkflowClient,
|
|
100
105
|
} from "../dependencies";
|
|
101
|
-
import {
|
|
106
|
+
import {
|
|
107
|
+
requireSessionAuthorization,
|
|
108
|
+
SessionAuthorizationDeniedError,
|
|
109
|
+
} from "../session-authorization";
|
|
102
110
|
import { swapActiveSandbox, type FleetContext } from "../sandbox/fleet";
|
|
103
111
|
import { settingsWithEnabledCapabilityMcpServers } from "./capabilities";
|
|
104
112
|
import { validateSubmittedTimelineAnnotations } from "./timeline-annotations";
|
|
@@ -148,10 +156,18 @@ export class SessionSpawnDeniedError extends Error {
|
|
|
148
156
|
export function resolveFirstPartyMcpToolsForCreate(
|
|
149
157
|
requested: FirstPartyMcpToolName[] | undefined,
|
|
150
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
|
+
},
|
|
151
166
|
): FirstPartyMcpToolName[] {
|
|
152
167
|
if (requested !== undefined) return [...requested];
|
|
153
|
-
|
|
154
|
-
|
|
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));
|
|
155
171
|
}
|
|
156
172
|
|
|
157
173
|
function sessionSpawnDeniedMessage(denial: SessionSpawnDenial): string {
|
|
@@ -540,7 +556,10 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
540
556
|
requestedSessionId?: string;
|
|
541
557
|
db: Database;
|
|
542
558
|
bus: EventBus;
|
|
543
|
-
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>;
|
|
544
563
|
accountId: string;
|
|
545
564
|
workspaceId: string;
|
|
546
565
|
initialMessage: string;
|
|
@@ -572,6 +591,9 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
572
591
|
// rig promote never moves an existing session's version.
|
|
573
592
|
rigId?: string | null;
|
|
574
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;
|
|
575
597
|
goal?: GoalSpec | null;
|
|
576
598
|
// Per-session agent persona/system instructions (org-visible metadata, not a
|
|
577
599
|
// secret). Persisted on the session row and composed system-level AFTER the
|
|
@@ -591,6 +613,7 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
591
613
|
mcpServers?: CreateSessionMcpServerInput[];
|
|
592
614
|
sessionMcpServers?: SessionMcpServerMetadata[];
|
|
593
615
|
personalConnectionDelegations?: McpPersonalConnectionDelegation[];
|
|
616
|
+
xaiProviderAccountAuthoritySnapshot?: XaiProviderAccountAuthoritySnapshotV1;
|
|
594
617
|
// The manager session spawning this worker (a worker-signed sessionId claim
|
|
595
618
|
// on the creating grant); null for direct API creates and scheduled runs.
|
|
596
619
|
// When set, the worker's terminal-for-now transitions wake this parent.
|
|
@@ -665,6 +688,7 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
665
688
|
variableSetId: input.variableSet?.id ?? null,
|
|
666
689
|
rigId: input.rigId ?? null,
|
|
667
690
|
rigVersionId: input.rigVersionId ?? null,
|
|
691
|
+
channelId: input.channelId ?? null,
|
|
668
692
|
firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
|
|
669
693
|
firstPartyMcpTools: input.firstPartyMcpTools,
|
|
670
694
|
instructions: input.instructions ?? null,
|
|
@@ -675,9 +699,15 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
675
699
|
...(input.sandboxOs ? { sandboxOs: input.sandboxOs } : {}),
|
|
676
700
|
mcpServers: input.mcpServers ?? [],
|
|
677
701
|
personalConnectionDelegations: input.personalConnectionDelegations ?? [],
|
|
702
|
+
...(input.xaiProviderAccountAuthoritySnapshot
|
|
703
|
+
? {
|
|
704
|
+
initialXaiProviderAccountAuthoritySnapshot: input.xaiProviderAccountAuthoritySnapshot,
|
|
705
|
+
}
|
|
706
|
+
: {}),
|
|
678
707
|
maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
|
|
679
708
|
allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
|
|
680
709
|
subjectId: input.subjectId ?? null,
|
|
710
|
+
...(input.beforeCreateCommit ? { beforeCreateCommit: input.beforeCreateCommit } : {}),
|
|
681
711
|
});
|
|
682
712
|
if (keyedResult.denied) {
|
|
683
713
|
throw new SessionSpawnDeniedError(SessionSpawnDenial.parse(keyedResult.denial));
|
|
@@ -724,6 +754,7 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
724
754
|
variableSetId: input.variableSet?.id ?? null,
|
|
725
755
|
rigId: input.rigId ?? null,
|
|
726
756
|
rigVersionId: input.rigVersionId ?? null,
|
|
757
|
+
channelId: input.channelId ?? null,
|
|
727
758
|
firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
|
|
728
759
|
firstPartyMcpTools: input.firstPartyMcpTools,
|
|
729
760
|
instructions: input.instructions ?? null,
|
|
@@ -733,9 +764,15 @@ export async function createAndStartSessionWithOutcome(input: {
|
|
|
733
764
|
...(input.sandboxOs ? { sandboxOs: input.sandboxOs } : {}),
|
|
734
765
|
mcpServers: input.mcpServers ?? [],
|
|
735
766
|
personalConnectionDelegations: input.personalConnectionDelegations ?? [],
|
|
767
|
+
...(input.xaiProviderAccountAuthoritySnapshot
|
|
768
|
+
? {
|
|
769
|
+
initialXaiProviderAccountAuthoritySnapshot: input.xaiProviderAccountAuthoritySnapshot,
|
|
770
|
+
}
|
|
771
|
+
: {}),
|
|
736
772
|
maxNestedAgentDepthOverride: input.maxNestedAgentDepthOverride ?? null,
|
|
737
773
|
allowNestedAgentDepthIncrease: input.allowNestedAgentDepthIncrease ?? false,
|
|
738
774
|
subjectId: input.subjectId ?? null,
|
|
775
|
+
...(input.beforeCreateCommit ? { beforeCreateCommit: input.beforeCreateCommit } : {}),
|
|
739
776
|
});
|
|
740
777
|
} catch (error) {
|
|
741
778
|
if (error instanceof SessionSpawnDeniedDbError) {
|
|
@@ -769,7 +806,7 @@ async function finishStartSession(
|
|
|
769
806
|
input: {
|
|
770
807
|
db: Database;
|
|
771
808
|
bus: EventBus;
|
|
772
|
-
workflowClient: SessionWorkflowClient
|
|
809
|
+
workflowClient: Pick<SessionWorkflowClient, "wakeSessionWorkflow">;
|
|
773
810
|
initialMessage: string;
|
|
774
811
|
deferInitialTurn?: boolean;
|
|
775
812
|
turnInstructions?: string | null;
|
|
@@ -924,6 +961,16 @@ export function canonicalConfiguredModel(
|
|
|
924
961
|
if (settings.codexSubscriptionEnabled && canonicalModel.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
925
962
|
return canonicalModel;
|
|
926
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
|
+
}
|
|
927
974
|
if (canonicalModel.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
|
|
928
975
|
return canonicalModel;
|
|
929
976
|
}
|
|
@@ -1270,11 +1317,18 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1270
1317
|
? (grant.metadata["sessionId"] as string)
|
|
1271
1318
|
: null;
|
|
1272
1319
|
if (parentSessionId) {
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
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
|
+
}
|
|
1278
1332
|
}
|
|
1279
1333
|
const parentSession = parentSessionId ? await getSession(db, workspaceId, parentSessionId) : null;
|
|
1280
1334
|
if (parentSessionId && !parentSession) {
|
|
@@ -1295,11 +1349,22 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1295
1349
|
message: "caller attempt does not belong to the parent session",
|
|
1296
1350
|
});
|
|
1297
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;
|
|
1298
1361
|
const capabilityRuntimeSettings = await settingsWithEnabledCapabilityMcpServers(
|
|
1299
1362
|
db,
|
|
1300
1363
|
workspaceId,
|
|
1301
1364
|
settings,
|
|
1302
|
-
{
|
|
1365
|
+
{
|
|
1366
|
+
subjectId: grant.subjectId,
|
|
1367
|
+
},
|
|
1303
1368
|
);
|
|
1304
1369
|
const sessionMcpServers = hasOwnProperty(rawPayload, "mcpServers")
|
|
1305
1370
|
? validateSessionMcpServersForCreate(capabilityRuntimeSettings, grant, payload.mcpServers)
|
|
@@ -1429,6 +1494,20 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1429
1494
|
frozenRigVersionId = rig.activeVersion.id;
|
|
1430
1495
|
}
|
|
1431
1496
|
}
|
|
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
|
+
}
|
|
1432
1511
|
// A spawned worker is causally part of the exact turn that created it. Omitted
|
|
1433
1512
|
// execution policy fields therefore inherit that calling turn rather than the
|
|
1434
1513
|
// deployment defaults. This is especially important for Codex subscription
|
|
@@ -1548,12 +1627,22 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1548
1627
|
// Tool visibility is independent from permission authority. A child that
|
|
1549
1628
|
// omits the field inherits the parent's exact effective selection; a
|
|
1550
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
|
+
}
|
|
1551
1639
|
const firstPartyMcpTools = resolveFirstPartyMcpToolsForCreate(
|
|
1552
1640
|
payload.firstPartyMcpTools,
|
|
1553
1641
|
parentSession ? parentSession.firstPartyMcpTools : undefined,
|
|
1642
|
+
deploymentFirstPartyMcpToolPolicy,
|
|
1554
1643
|
);
|
|
1555
1644
|
if (payload.goal) {
|
|
1556
|
-
const missingGoalTools = ["goal_update", "goal_complete", "goal_pause"].filter(
|
|
1645
|
+
const missingGoalTools = ["goal_update", "goal_progress", "goal_complete", "goal_pause"].filter(
|
|
1557
1646
|
(name) => !firstPartyMcpTools.includes(name as FirstPartyMcpToolName),
|
|
1558
1647
|
);
|
|
1559
1648
|
if (missingGoalTools.length > 0) {
|
|
@@ -1824,6 +1913,7 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1824
1913
|
// Frozen rig binding (M3): both null for a rig-less session (today's path).
|
|
1825
1914
|
rigId: frozenRigId,
|
|
1826
1915
|
rigVersionId: frozenRigVersionId,
|
|
1916
|
+
channelId,
|
|
1827
1917
|
goal: payload.goal ?? null,
|
|
1828
1918
|
// Per-session persona instructions (already trimmed/validated by the
|
|
1829
1919
|
// contracts schema). Persisted on the row; composed system-level at turn
|
|
@@ -1835,6 +1925,7 @@ export async function createSessionForRequestWithOutcome(
|
|
|
1835
1925
|
mcpServers: sessionMcpServers.dbServers,
|
|
1836
1926
|
sessionMcpServers: sessionMcpServers.metadata,
|
|
1837
1927
|
personalConnectionDelegations,
|
|
1928
|
+
...(xaiProviderAccountAuthoritySnapshot ? { xaiProviderAccountAuthoritySnapshot } : {}),
|
|
1838
1929
|
parentSessionId,
|
|
1839
1930
|
createIdempotencyKey: payload.idempotencyKey ?? null,
|
|
1840
1931
|
maxNestedAgentDepthOverride: payload.maxNestedAgentDepth ?? null,
|
|
@@ -1952,6 +2043,7 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
1952
2043
|
replay: boolean;
|
|
1953
2044
|
}> {
|
|
1954
2045
|
const { settings, db, bus, workflowClient, objectStorage } = deps;
|
|
2046
|
+
const delegatedServiceInitiator = serviceInitiatorForGrant(grant);
|
|
1955
2047
|
await requireSessionAuthorization(deps, grant, {
|
|
1956
2048
|
sessionId,
|
|
1957
2049
|
operation: input.delivery === "steer" ? "session.steer" : "session.append",
|
|
@@ -2031,7 +2123,6 @@ export async function acceptSessionUserMessageWithOutcome(
|
|
|
2031
2123
|
tools: existingSession.tools,
|
|
2032
2124
|
source: personalConnectionDelegationSourceForGrant(grant),
|
|
2033
2125
|
});
|
|
2034
|
-
const delegatedServiceInitiator = serviceInitiatorForGrant(grant);
|
|
2035
2126
|
const { accepted, turn, interruptionCount, replay } = await postUserMessageTurn({
|
|
2036
2127
|
db,
|
|
2037
2128
|
bus,
|
|
@@ -2317,11 +2408,20 @@ export async function updateSessionToolPolicy(
|
|
|
2317
2408
|
const explicitRequestedFirstPartyTools = explicitRequest
|
|
2318
2409
|
? [...explicitRequest.firstPartyMcpTools]
|
|
2319
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
|
+
}
|
|
2320
2420
|
const workspaceDefaultTools = withFirstPartyTools(
|
|
2321
2421
|
withDefaultEnabledCapabilityMcpTools([], deps.settings, capabilityRuntimeSettings),
|
|
2322
2422
|
runtimeSettings,
|
|
2323
2423
|
);
|
|
2324
|
-
const workspaceDefaultFirstPartyTools = [...
|
|
2424
|
+
const workspaceDefaultFirstPartyTools = [...deploymentFirstPartyMcpToolPolicy.default];
|
|
2325
2425
|
const events = await appendSessionEventsWithLockedSessionUpdate(
|
|
2326
2426
|
deps.db,
|
|
2327
2427
|
grant.workspaceId,
|
|
@@ -2353,9 +2453,12 @@ export async function updateSessionToolPolicy(
|
|
|
2353
2453
|
: parent.tools,
|
|
2354
2454
|
runtimeSettings,
|
|
2355
2455
|
);
|
|
2456
|
+
const deploymentAllowedFirstPartyMcpTools = new Set(
|
|
2457
|
+
deploymentFirstPartyMcpToolPolicy.allowed,
|
|
2458
|
+
);
|
|
2356
2459
|
const parentFirstPartyMcpTools = [
|
|
2357
|
-
...(parent.firstPartyMcpTools ??
|
|
2358
|
-
];
|
|
2460
|
+
...(parent.firstPartyMcpTools ?? deploymentFirstPartyMcpToolPolicy.default),
|
|
2461
|
+
].filter((tool) => deploymentAllowedFirstPartyMcpTools.has(tool));
|
|
2359
2462
|
if (requestedMode === "workspace_default") {
|
|
2360
2463
|
if (!parentTracksWorkspaceDefaults) {
|
|
2361
2464
|
throw new HTTPException(403, {
|
|
@@ -2409,7 +2512,8 @@ export async function updateSessionToolPolicy(
|
|
|
2409
2512
|
const unchanged =
|
|
2410
2513
|
stableJson({
|
|
2411
2514
|
tools: session.tools,
|
|
2412
|
-
firstPartyMcpTools:
|
|
2515
|
+
firstPartyMcpTools:
|
|
2516
|
+
session.firstPartyMcpTools ?? deploymentFirstPartyMcpToolPolicy.default,
|
|
2413
2517
|
policy: currentPolicy,
|
|
2414
2518
|
}) ===
|
|
2415
2519
|
stableJson({
|
|
@@ -2430,7 +2534,7 @@ export async function updateSessionToolPolicy(
|
|
|
2430
2534
|
before: toolPolicyAuditSnapshot(
|
|
2431
2535
|
session,
|
|
2432
2536
|
session.tools,
|
|
2433
|
-
[...(session.firstPartyMcpTools ??
|
|
2537
|
+
[...(session.firstPartyMcpTools ?? deploymentFirstPartyMcpToolPolicy.default)],
|
|
2434
2538
|
currentPolicy,
|
|
2435
2539
|
),
|
|
2436
2540
|
after: toolPolicyAuditSnapshot(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
GROK_IMAGINE_VIDEO_1_5_MODEL_ID,
|
|
2
3
|
SEEDANCE_2_5_MODEL_ID,
|
|
3
4
|
VideoGenerationCapabilities,
|
|
4
5
|
VideoGenerationPolicy,
|
|
@@ -23,11 +24,41 @@ export const VIDEO_GENERATION_MODEL_CATALOG: readonly VideoGenerationModelCapabi
|
|
|
23
24
|
]),
|
|
24
25
|
resolutions: Object.freeze(["480p", "720p"]),
|
|
25
26
|
aspectRatios: Object.freeze(["16:9", "4:3", "1:1", "3:4", "9:16", "21:9", "adaptive"]),
|
|
26
|
-
duration: Object.freeze({
|
|
27
|
+
duration: Object.freeze({
|
|
28
|
+
minSeconds: 4,
|
|
29
|
+
maxSeconds: 30,
|
|
30
|
+
stepSeconds: 1,
|
|
31
|
+
}),
|
|
32
|
+
supportsAudio: true,
|
|
33
|
+
}) as VideoGenerationModelCapability,
|
|
34
|
+
Object.freeze({
|
|
35
|
+
modelId: GROK_IMAGINE_VIDEO_1_5_MODEL_ID,
|
|
36
|
+
label: "Grok Imagine Video 1.5",
|
|
37
|
+
providerLabel: "Connected SuperGrok",
|
|
38
|
+
sourceModes: Object.freeze(["text", "first_frame", "image_reference"]),
|
|
39
|
+
resolutions: Object.freeze(["480p", "720p"]),
|
|
40
|
+
aspectRatios: Object.freeze(["16:9", "4:3", "1:1", "3:4", "9:16"]),
|
|
41
|
+
duration: Object.freeze({
|
|
42
|
+
minSeconds: 4,
|
|
43
|
+
maxSeconds: 15,
|
|
44
|
+
stepSeconds: 1,
|
|
45
|
+
}),
|
|
46
|
+
// xAI emits audio but exposes no separate audio wire option.
|
|
27
47
|
supportsAudio: true,
|
|
28
48
|
}) as VideoGenerationModelCapability,
|
|
29
49
|
]);
|
|
30
50
|
|
|
51
|
+
export function videoGenerationModelSupportsFundingSource(
|
|
52
|
+
modelId: string,
|
|
53
|
+
fundingSource: VideoGenerationPolicyType["fundingSource"],
|
|
54
|
+
): boolean {
|
|
55
|
+
return modelId === GROK_IMAGINE_VIDEO_1_5_MODEL_ID
|
|
56
|
+
? fundingSource === "supergrok_subscription"
|
|
57
|
+
: modelId === SEEDANCE_2_5_MODEL_ID
|
|
58
|
+
? fundingSource === "opengeni_credits" || fundingSource === "workspace_gateway"
|
|
59
|
+
: false;
|
|
60
|
+
}
|
|
61
|
+
|
|
31
62
|
export function defaultVideoGenerationPolicy(): VideoGenerationPolicyType {
|
|
32
63
|
return VideoGenerationPolicy.parse({
|
|
33
64
|
schemaVersion: 1,
|
|
@@ -44,7 +75,11 @@ export function videoGenerationCapabilitiesForPolicy(input: {
|
|
|
44
75
|
}): VideoGenerationCapabilities {
|
|
45
76
|
const policy = VideoGenerationPolicy.parse(input.policy);
|
|
46
77
|
const enabled = new Set(policy.enabledModelIds);
|
|
47
|
-
const models = VIDEO_GENERATION_MODEL_CATALOG.filter(
|
|
78
|
+
const models = VIDEO_GENERATION_MODEL_CATALOG.filter(
|
|
79
|
+
(model) =>
|
|
80
|
+
enabled.has(model.modelId) &&
|
|
81
|
+
videoGenerationModelSupportsFundingSource(model.modelId, policy.fundingSource),
|
|
82
|
+
);
|
|
48
83
|
if (models.length === 0 || policy.defaultModelId === null) {
|
|
49
84
|
throw new Error("Video generation is disabled for this workspace");
|
|
50
85
|
}
|
package/src/index.ts
CHANGED
|
@@ -71,6 +71,8 @@ export * from "./domain/durable-learning-slack-publication";
|
|
|
71
71
|
export * from "./domain/slack-publication-secret-safety";
|
|
72
72
|
export * from "./domain/company-profile-durable-learning-adapter";
|
|
73
73
|
export * from "./domain/slack-bot";
|
|
74
|
+
export * from "./domain/conversation-integrations";
|
|
75
|
+
export * from "./domain/fiken";
|
|
74
76
|
export * from "./domain/workspace-members";
|
|
75
77
|
export * from "./domain/video-generation";
|
|
76
78
|
export * from "./domain/video-generation-capabilities";
|