@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/core",
3
- "version": "0.12.5",
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.10",
39
- "@opengeni/contracts": "^0.21.0",
40
- "@opengeni/db": "^0.13.4",
41
- "@opengeni/documents": "^0.2.41",
42
- "@opengeni/events": "^0.3.32",
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.0",
45
- "@opengeni/storage": "^0.2.34",
44
+ "@opengeni/runtime": "^0.14.1",
45
+ "@opengeni/storage": "^0.2.35",
46
46
  "hono": "^4.12.18"
47
47
  },
48
48
  "engines": {
@@ -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 } : {}),
@@ -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?: string;
57
- triggerWorkflowId?: string;
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(
@@ -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. It hosts the
1146
- // session's own metadata tool (set_session_title) + goal tools, and — only
1147
- // when the grant carries the permission — the orchestration/variableSet/
1148
- // github tools. Capability is gated per-tool by permission, never by whether
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,