@opengeni/core 3.0.2-canary.38894290271001 → 3.1.2

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": "3.0.2-canary.38894290271001",
3
+ "version": "3.1.2",
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": {
@@ -50,18 +50,18 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@modelcontextprotocol/sdk": "^1.29.0",
53
- "@opengeni/capabilities": "3.0.2-canary.38894290271001",
54
- "@opengeni/codex": "3.0.2-canary.38894290271001",
55
- "@opengeni/config": "3.0.2-canary.38894290271001",
56
- "@opengeni/contracts": "3.0.2-canary.38894290271001",
57
- "@opengeni/db": "3.0.2-canary.38894290271001",
58
- "@opengeni/documents": "3.0.2-canary.38894290271001",
59
- "@opengeni/events": "3.0.2-canary.38894290271001",
60
- "@opengeni/network": "3.0.2-canary.38894290271001",
61
- "@opengeni/observability": "3.0.2-canary.38894290271001",
62
- "@opengeni/runtime": "3.0.2-canary.38894290271001",
63
- "@opengeni/storage": "3.0.2-canary.38894290271001",
64
- "@opengeni/vaults": "3.0.2-canary.38894290271001",
53
+ "@opengeni/capabilities": "3.1.2",
54
+ "@opengeni/codex": "3.1.2",
55
+ "@opengeni/config": "3.1.2",
56
+ "@opengeni/contracts": "3.1.2",
57
+ "@opengeni/db": "3.1.2",
58
+ "@opengeni/documents": "3.1.2",
59
+ "@opengeni/events": "3.1.2",
60
+ "@opengeni/network": "3.1.2",
61
+ "@opengeni/observability": "3.1.2",
62
+ "@opengeni/runtime": "3.1.2",
63
+ "@opengeni/storage": "3.1.2",
64
+ "@opengeni/vaults": "3.1.2",
65
65
  "hono": "^4.12.18",
66
66
  "zod": "^4.2.1"
67
67
  },
@@ -33,7 +33,11 @@ import {
33
33
  } from "../domain/resources";
34
34
  import { hasVerifiedOwningUserAuthorization, type AccessGrantAuthorization } from "../access";
35
35
  import { assertConfiguredModel, assertWorkspaceModelPolicyAllows } from "../domain/sessions";
36
- import { resolveDefaultSessionModel } from "../default-session-model";
36
+ import {
37
+ resolveCallerWorkspaceModelSelections,
38
+ resolveDefaultSessionModel,
39
+ workspaceCatalogListsModel,
40
+ } from "../default-session-model";
37
41
  import { canonicalizeConfiguredModelId, type Settings } from "@opengeni/config";
38
42
 
39
43
  type NewSessionDraftDependencies = Pick<AppDependencies, "settings" | "db" | "objectStorage">;
@@ -98,6 +102,32 @@ async function actorDefaultModel(
98
102
  });
99
103
  }
100
104
 
105
+ /**
106
+ * False only when this workspace's catalog demonstrably no longer lists the
107
+ * model. A catalog read failure is not proof of removal, so it keeps the
108
+ * saved choice.
109
+ */
110
+ async function workspaceCatalogMayHaveModel(
111
+ deps: Pick<NewSessionDraftDependencies, "db" | "settings">,
112
+ grant: AccessGrant,
113
+ workspaceId: string,
114
+ modelId: string,
115
+ ): Promise<boolean> {
116
+ try {
117
+ const selections = await resolveCallerWorkspaceModelSelections(deps.db, deps.settings, {
118
+ accountId: grant.accountId,
119
+ workspaceId,
120
+ subjectId: grant.subjectId,
121
+ });
122
+ return workspaceCatalogListsModel(
123
+ selections,
124
+ canonicalizeConfiguredModelId(deps.settings, modelId),
125
+ );
126
+ } catch {
127
+ return true;
128
+ }
129
+ }
130
+
101
131
  type NewSessionDraftReadOptions = {
102
132
  /**
103
133
  * Project a draft that follows the default onto today's resolved default.
@@ -120,7 +150,18 @@ async function hydrateNewSessionDraft(
120
150
  // A draft that follows the default is projected onto today's default, so a
121
151
  // later subscription connect or credit purchase replaces an untouched free
122
152
  // model. The stored row is unchanged until the person saves again.
123
- const modelProvided = draftModelProvided(deps.settings, stored);
153
+ let modelProvided = draftModelProvided(deps.settings, stored);
154
+ if (
155
+ modelProvided &&
156
+ readOptions.projectDefaultModel !== false &&
157
+ !(await workspaceCatalogMayHaveModel(deps, grant, workspaceId, stored.model))
158
+ ) {
159
+ // The saved choice left this workspace's catalog (a removed provider or a
160
+ // retired model). A new chat must not start on a model that cannot run, so
161
+ // the draft follows today's default until the person picks again. A model
162
+ // that is listed but temporarily unavailable keeps the person's choice.
163
+ modelProvided = false;
164
+ }
124
165
  let mapped: NewSessionDraftValue = { ...stored, modelProvided };
125
166
  if (!modelProvided && readOptions.projectDefaultModel !== false) {
126
167
  const resolved = await actorDefaultModel(deps, grant, workspaceId);
@@ -575,6 +575,17 @@ export async function resolveCallerWorkspaceModelSelections(
575
575
  );
576
576
  }
577
577
 
578
+ /**
579
+ * Catalog membership only: true while the workspace still lists the model,
580
+ * selectable or not. Retired and removed models are not listed.
581
+ */
582
+ export function workspaceCatalogListsModel(
583
+ selections: readonly WorkspaceModelSelection[],
584
+ modelId: string,
585
+ ): boolean {
586
+ return findSelection(selections, modelId) !== undefined;
587
+ }
588
+
578
589
  /** Transient selectability for existing consumers; not fresh-create admission. */
579
590
  export function selectableWorkspaceModel(
580
591
  selections: readonly WorkspaceModelSelection[],
@@ -22,6 +22,7 @@ import type {
22
22
  SessionMcpToolPermissionsResponse,
23
23
  UpdateSessionMcpToolPermissionsRequest,
24
24
  } from "@opengeni/contracts";
25
+ import { mcpToolRecommendsApproval } from "@opengeni/contracts";
25
26
  import {
26
27
  buildConnectionTokenResolver,
27
28
  getConnectionMetadata,
@@ -76,6 +77,7 @@ type ListedTool = {
76
77
  title?: string | undefined;
77
78
  }
78
79
  | undefined;
80
+ _meta?: Record<string, unknown> | undefined;
79
81
  };
80
82
 
81
83
  export function connectorToolGroup(tool: ListedTool): "read" | "write" | "other" {
@@ -422,7 +424,8 @@ export async function getConnectorToolPermissions(
422
424
  defaultDecision:
423
425
  target.server.requireApproval === true ||
424
426
  (Array.isArray(target.server.requireApproval) &&
425
- target.server.requireApproval.includes(tool.name))
427
+ target.server.requireApproval.includes(tool.name)) ||
428
+ mcpToolRecommendsApproval(tool)
426
429
  ? "ask"
427
430
  : "allow",
428
431
  }),