@opengeni/core 0.12.5 → 0.12.7
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/index.d.ts +20 -4
- package/dist/index.js +53 -1
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/access/index.ts +3 -0
- package/src/dependencies.ts +4 -2
- package/src/domain/capabilities.ts +36 -1
- package/src/domain/sessions.ts +45 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/core",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.7",
|
|
4
4
|
"description": "OpenGeni framework-agnostic core: the domain, access, and billing layers (neutral access, off-HTTP V2 surface). Behavior-preserving extraction from apps/api — keeps Hono's HTTPException for error throwing (typed-errors cleanup deferred).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
37
|
"@opengeni/codex": "^0.2.7",
|
|
38
|
-
"@opengeni/config": "^0.7.
|
|
39
|
-
"@opengeni/contracts": "^0.
|
|
40
|
-
"@opengeni/db": "^0.
|
|
41
|
-
"@opengeni/documents": "^0.2.
|
|
42
|
-
"@opengeni/events": "^0.3.
|
|
38
|
+
"@opengeni/config": "^0.7.11",
|
|
39
|
+
"@opengeni/contracts": "^0.22.0",
|
|
40
|
+
"@opengeni/db": "^0.14.0",
|
|
41
|
+
"@opengeni/documents": "^0.2.42",
|
|
42
|
+
"@opengeni/events": "^0.3.33",
|
|
43
43
|
"@opengeni/observability": "^0.3.0",
|
|
44
|
-
"@opengeni/runtime": "^0.14.
|
|
45
|
-
"@opengeni/storage": "^0.2.
|
|
44
|
+
"@opengeni/runtime": "^0.14.1",
|
|
45
|
+
"@opengeni/storage": "^0.2.35",
|
|
46
46
|
"hono": "^4.12.18"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
package/src/access/index.ts
CHANGED
|
@@ -236,6 +236,9 @@ async function delegatedAccessContext(
|
|
|
236
236
|
metadata: {
|
|
237
237
|
delegated: true,
|
|
238
238
|
...(payload.sessionId ? { sessionId: payload.sessionId } : {}),
|
|
239
|
+
...(payload.firstPartyMcpTools !== undefined
|
|
240
|
+
? { firstPartyMcpTools: payload.firstPartyMcpTools }
|
|
241
|
+
: {}),
|
|
239
242
|
// Caller identity: the turn that minted this token. Tools classify the
|
|
240
243
|
// CALLER from this instead of re-reading the live active pointer.
|
|
241
244
|
...(payload.turnId ? { turnId: payload.turnId } : {}),
|
package/src/dependencies.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
GitHubAppApiPort,
|
|
6
6
|
ScheduledTask,
|
|
7
7
|
SessionAuthorizationPort,
|
|
8
|
+
TurnInitiator,
|
|
8
9
|
} from "@opengeni/contracts";
|
|
9
10
|
import type { Database } from "@opengeni/db";
|
|
10
11
|
import type { DocumentServices } from "@opengeni/documents";
|
|
@@ -53,8 +54,9 @@ export type SessionWorkflowClient = {
|
|
|
53
54
|
deleteScheduledTaskSchedule: (input: { temporalScheduleId: string }) => Promise<void>;
|
|
54
55
|
triggerScheduledTask: (input: {
|
|
55
56
|
task: ScheduledTask;
|
|
56
|
-
agentRunUsageIdempotencyKey
|
|
57
|
-
triggerWorkflowId
|
|
57
|
+
agentRunUsageIdempotencyKey: string;
|
|
58
|
+
triggerWorkflowId: string;
|
|
59
|
+
initiator: TurnInitiator;
|
|
58
60
|
}) => Promise<void>;
|
|
59
61
|
startRigVerification: (input: {
|
|
60
62
|
workspaceId: string;
|
|
@@ -12,6 +12,12 @@ import {
|
|
|
12
12
|
type EnableCapabilityRequest,
|
|
13
13
|
type McpServerConnectionRef,
|
|
14
14
|
} from "@opengeni/contracts";
|
|
15
|
+
import {
|
|
16
|
+
CODEX_APPS_MCP_SERVER_ID,
|
|
17
|
+
CODEX_APPS_MCP_SERVER_NAME,
|
|
18
|
+
CODEX_APPS_MCP_URL,
|
|
19
|
+
CODEX_APPS_STARTUP_TIMEOUT_MS,
|
|
20
|
+
} from "@opengeni/codex";
|
|
15
21
|
import {
|
|
16
22
|
decryptVariableSetValue,
|
|
17
23
|
decryptedCapabilityHeaders,
|
|
@@ -711,7 +717,36 @@ export async function settingsWithEnabledCapabilityMcpServers(
|
|
|
711
717
|
settings: Settings,
|
|
712
718
|
): Promise<Settings> {
|
|
713
719
|
const enabled = await listEnabledMcpCapabilityServers(db, workspaceId);
|
|
714
|
-
return settingsWithMcpCapabilityServers(settings, enabled);
|
|
720
|
+
return settingsWithCodexAppsMcpServer(settingsWithMcpCapabilityServers(settings, enabled));
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Register Codex Apps as an optional runtime MCP when the deployment enables
|
|
725
|
+
* it. Registration only makes the server selectable; the session tool policy
|
|
726
|
+
* decides whether the model sees it, and Codex credential resolution
|
|
727
|
+
* independently decides whether calls can authenticate.
|
|
728
|
+
*/
|
|
729
|
+
export function settingsWithCodexAppsMcpServer(settings: Settings): Settings {
|
|
730
|
+
if (
|
|
731
|
+
!settings.codexConnectedAppsEnabled ||
|
|
732
|
+
settings.mcpServers.some((server) => server.id === CODEX_APPS_MCP_SERVER_ID)
|
|
733
|
+
) {
|
|
734
|
+
return settings;
|
|
735
|
+
}
|
|
736
|
+
return {
|
|
737
|
+
...settings,
|
|
738
|
+
mcpServers: [
|
|
739
|
+
...settings.mcpServers,
|
|
740
|
+
{
|
|
741
|
+
id: CODEX_APPS_MCP_SERVER_ID,
|
|
742
|
+
name: CODEX_APPS_MCP_SERVER_NAME,
|
|
743
|
+
url: CODEX_APPS_MCP_URL,
|
|
744
|
+
timeoutMs: CODEX_APPS_STARTUP_TIMEOUT_MS,
|
|
745
|
+
// Availability is credential-specific, so discover on every run.
|
|
746
|
+
cacheToolsList: false,
|
|
747
|
+
},
|
|
748
|
+
],
|
|
749
|
+
};
|
|
715
750
|
}
|
|
716
751
|
|
|
717
752
|
export function settingsWithMcpCapabilityServers(
|
package/src/domain/sessions.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
CreateSessionRequest,
|
|
11
11
|
DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
|
|
12
|
+
DEFAULT_FIRST_PARTY_MCP_TOOLS,
|
|
12
13
|
OPENGENI_SLACK_BOT_SESSION_METADATA_KEY,
|
|
13
14
|
SessionSpawnDenial,
|
|
14
15
|
ServiceTurnInitiator,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
type AccessGrant,
|
|
20
21
|
type CreateSessionResponse,
|
|
21
22
|
type GoalSpec,
|
|
23
|
+
type FirstPartyMcpToolName,
|
|
22
24
|
type Permission,
|
|
23
25
|
type ReasoningEffort,
|
|
24
26
|
type ResourceRef,
|
|
@@ -127,6 +129,21 @@ export class SessionSpawnDeniedError extends Error {
|
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Resolve per-session first-party tool visibility without consulting
|
|
134
|
+
* authorization. Top-level omission uses the minimal runtime default (stored
|
|
135
|
+
* as null); child omission snapshots the parent's exact effective selection.
|
|
136
|
+
* Explicit [] is authoritative and must never widen.
|
|
137
|
+
*/
|
|
138
|
+
export function resolveFirstPartyMcpToolsForCreate(
|
|
139
|
+
requested: FirstPartyMcpToolName[] | undefined,
|
|
140
|
+
parentStored: FirstPartyMcpToolName[] | null | undefined,
|
|
141
|
+
): FirstPartyMcpToolName[] | null {
|
|
142
|
+
if (requested !== undefined) return [...requested];
|
|
143
|
+
if (parentStored === undefined) return null;
|
|
144
|
+
return [...(parentStored ?? DEFAULT_FIRST_PARTY_MCP_TOOLS)];
|
|
145
|
+
}
|
|
146
|
+
|
|
130
147
|
function sessionSpawnDeniedMessage(denial: SessionSpawnDenial): string {
|
|
131
148
|
if (denial.code === "nested_agent_depth_override_forbidden") {
|
|
132
149
|
return `requested nested-agent depth limit ${denial.requestedMaxNestedAgentDepthOverride ?? "unknown"} exceeds inherited limit ${denial.effectiveMaxNestedAgentDepth}; workspace:admin is required to increase it`;
|
|
@@ -522,6 +539,9 @@ export async function createAndStartSession(input: {
|
|
|
522
539
|
instructions?: string | null;
|
|
523
540
|
// Validated against the creating grant before this is called.
|
|
524
541
|
firstPartyMcpPermissions?: Permission[] | null;
|
|
542
|
+
// Model-visible first-party tool names. Authorization remains controlled by
|
|
543
|
+
// firstPartyMcpPermissions and the target resource checks.
|
|
544
|
+
firstPartyMcpTools?: FirstPartyMcpToolName[] | null;
|
|
525
545
|
// Encrypted DB rows plus matching safe metadata for create-time per-session
|
|
526
546
|
// MCP servers. Metadata is the only shape emitted in events/responses.
|
|
527
547
|
mcpServers?: CreateSessionMcpServerInput[];
|
|
@@ -593,6 +613,7 @@ export async function createAndStartSession(input: {
|
|
|
593
613
|
rigId: input.rigId ?? null,
|
|
594
614
|
rigVersionId: input.rigVersionId ?? null,
|
|
595
615
|
firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
|
|
616
|
+
firstPartyMcpTools: input.firstPartyMcpTools ?? null,
|
|
596
617
|
instructions: input.instructions ?? null,
|
|
597
618
|
parentSessionId: input.parentSessionId ?? null,
|
|
598
619
|
createIdempotencyKey: input.createIdempotencyKey,
|
|
@@ -637,6 +658,7 @@ export async function createAndStartSession(input: {
|
|
|
637
658
|
rigId: input.rigId ?? null,
|
|
638
659
|
rigVersionId: input.rigVersionId ?? null,
|
|
639
660
|
firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
|
|
661
|
+
firstPartyMcpTools: input.firstPartyMcpTools ?? null,
|
|
640
662
|
instructions: input.instructions ?? null,
|
|
641
663
|
parentSessionId: input.parentSessionId ?? null,
|
|
642
664
|
sandboxGroupId: input.sandboxGroupId ?? null,
|
|
@@ -1142,12 +1164,10 @@ export async function createSessionForRequest(
|
|
|
1142
1164
|
);
|
|
1143
1165
|
toolPolicy = { mode: "workspace_default", inheritedFromSessionId: null };
|
|
1144
1166
|
}
|
|
1145
|
-
// The first-party MCP server is attached to EVERY session.
|
|
1146
|
-
//
|
|
1147
|
-
//
|
|
1148
|
-
//
|
|
1149
|
-
// the server is attached, so a bare chat still gets titling while the
|
|
1150
|
-
// dangerous tools stay off by default.
|
|
1167
|
+
// The first-party MCP server is attached to EVERY session. Registration is
|
|
1168
|
+
// independently intersected with the exact model-visible selection and the
|
|
1169
|
+
// tool's permission/target authorization predicate, so attachment alone
|
|
1170
|
+
// exposes nothing.
|
|
1151
1171
|
const tools = withFirstPartyTools(selectedTools, runtimeSettings);
|
|
1152
1172
|
await validateGitHubRepositorySelection(db, workspaceId, resources);
|
|
1153
1173
|
if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
@@ -1276,6 +1296,24 @@ export async function createSessionForRequest(
|
|
|
1276
1296
|
"goal-bearing sessions require goals:manage in the resulting first-party MCP permission set",
|
|
1277
1297
|
});
|
|
1278
1298
|
}
|
|
1299
|
+
// Tool visibility is independent from permission authority. A child that
|
|
1300
|
+
// omits the field inherits the parent's exact effective selection; a
|
|
1301
|
+
// top-level omission resolves to the fixed minimal default at execution.
|
|
1302
|
+
const firstPartyMcpTools = resolveFirstPartyMcpToolsForCreate(
|
|
1303
|
+
payload.firstPartyMcpTools,
|
|
1304
|
+
parentSession ? parentSession.firstPartyMcpTools : undefined,
|
|
1305
|
+
);
|
|
1306
|
+
if (payload.goal) {
|
|
1307
|
+
const effectiveTools = firstPartyMcpTools ?? [...DEFAULT_FIRST_PARTY_MCP_TOOLS];
|
|
1308
|
+
const missingGoalTools = ["goal_update", "goal_complete", "goal_pause"].filter(
|
|
1309
|
+
(name) => !effectiveTools.includes(name as FirstPartyMcpToolName),
|
|
1310
|
+
);
|
|
1311
|
+
if (missingGoalTools.length > 0) {
|
|
1312
|
+
throw new HTTPException(422, {
|
|
1313
|
+
message: `goal-bearing sessions require first-party MCP tools: ${missingGoalTools.join(", ")}`,
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1279
1317
|
// Parent linkage: a worker is linked to its manager ONLY from the
|
|
1280
1318
|
// worker-signed sessionId claim on the creating grant — the manager
|
|
1281
1319
|
// session's own id, signed into the delegated token by the worker and never
|
|
@@ -1541,6 +1579,7 @@ export async function createSessionForRequest(
|
|
|
1541
1579
|
// time. Not surfaced as an event.
|
|
1542
1580
|
instructions: payload.instructions ?? null,
|
|
1543
1581
|
firstPartyMcpPermissions,
|
|
1582
|
+
firstPartyMcpTools,
|
|
1544
1583
|
mcpServers: sessionMcpServers.dbServers,
|
|
1545
1584
|
sessionMcpServers: sessionMcpServers.metadata,
|
|
1546
1585
|
parentSessionId,
|