@opengeni/core 0.28.0 → 1.0.0

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.
Files changed (224) hide show
  1. package/README.md +1 -1
  2. package/dist/access/external-actor-authority.d.ts +47 -0
  3. package/dist/access/index.d.ts +182 -3
  4. package/dist/access/service-initiator.d.ts +3 -0
  5. package/dist/application/api-integration-servers.d.ts +20 -0
  6. package/dist/application/archived-session-imports.d.ts +6 -0
  7. package/dist/application/composer-submit.d.ts +24 -0
  8. package/dist/application/connect-authority.d.ts +13 -0
  9. package/dist/application/connect-operation.d.ts +31 -0
  10. package/dist/application/external-continuation.d.ts +15 -0
  11. package/dist/application/external-identity-lifecycle.d.ts +20 -0
  12. package/dist/application/external-link-work-admission.d.ts +12 -0
  13. package/dist/application/external-workspace-members.d.ts +34 -0
  14. package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
  15. package/dist/application/new-session-drafts.d.ts +39 -2
  16. package/dist/application/sandbox-recovery.d.ts +58 -0
  17. package/dist/application/session-commands.d.ts +26 -13
  18. package/dist/application/session-mcp-credential-rotation.d.ts +29 -0
  19. package/dist/application/session-tenancy.d.ts +19 -0
  20. package/dist/application/user-resource-grants.d.ts +20 -0
  21. package/dist/billing/limits.d.ts +11 -0
  22. package/dist/canonical-human-identities.d.ts +7 -2
  23. package/dist/canonical-human-identities.js +17 -4
  24. package/dist/canonical-human-identities.js.map +1 -1
  25. package/dist/{chunk-6WKPWE5S.js → chunk-2MKMRGSY.js} +108 -24
  26. package/dist/chunk-2MKMRGSY.js.map +1 -0
  27. package/dist/{chunk-NYPMQ7NO.js → chunk-GVO3N37G.js} +205 -37
  28. package/dist/chunk-GVO3N37G.js.map +1 -0
  29. package/dist/{chunk-JJU6I5XD.js → chunk-LCQXNR62.js} +36 -10
  30. package/dist/chunk-LCQXNR62.js.map +1 -0
  31. package/dist/chunk-MM6N7CTO.js +387 -0
  32. package/dist/chunk-MM6N7CTO.js.map +1 -0
  33. package/dist/chunk-QO5GVFFO.js +235 -0
  34. package/dist/chunk-QO5GVFFO.js.map +1 -0
  35. package/dist/codex-model-availability.d.ts +14 -0
  36. package/dist/default-session-model.d.ts +139 -0
  37. package/dist/dependencies.d.ts +87 -4
  38. package/dist/developer-setup-credential-ceiling.d.ts +10 -0
  39. package/dist/domain/agent-config-resolution.d.ts +49 -0
  40. package/dist/domain/automations.d.ts +58 -0
  41. package/dist/domain/capabilities.d.ts +22 -4
  42. package/dist/domain/company-profile-agent-admin.d.ts +25 -0
  43. package/dist/domain/connection-account-selection-error.d.ts +8 -0
  44. package/dist/domain/connector-tool-permissions.d.ts +36 -0
  45. package/dist/domain/direct-model-provider.d.ts +3 -0
  46. package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
  47. package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
  48. package/dist/domain/editable-artifacts/ports.d.ts +10 -0
  49. package/dist/domain/editable-artifacts/service.d.ts +4 -3
  50. package/dist/domain/environments.d.ts +5 -9
  51. package/dist/domain/external-creation-attribution.d.ts +6 -0
  52. package/dist/domain/file-owner.d.ts +8 -0
  53. package/dist/domain/github-action-policies.d.ts +36 -0
  54. package/dist/domain/github-repository-bindings.d.ts +117 -0
  55. package/dist/domain/github-skill-source.d.ts +5 -0
  56. package/dist/domain/governed-learning-slack-publication.d.ts +45 -0
  57. package/dist/domain/insights-timing.d.ts +16 -0
  58. package/dist/domain/insights-usage.d.ts +27 -0
  59. package/dist/domain/insights.d.ts +28 -1
  60. package/dist/domain/knowledge-files.d.ts +20 -0
  61. package/dist/domain/knowledge-messages.d.ts +39 -0
  62. package/dist/domain/knowledge-messages.test.d.ts +1 -0
  63. package/dist/domain/knowledge-preparation.d.ts +10 -0
  64. package/dist/domain/knowledge-preparation.test.d.ts +1 -0
  65. package/dist/domain/knowledge-search.d.ts +15 -0
  66. package/dist/domain/knowledge-search.test.d.ts +1 -0
  67. package/dist/domain/knowledge.d.ts +9 -0
  68. package/dist/domain/mcp-account-bindings.d.ts +19 -0
  69. package/dist/domain/mcp-account-routes.d.ts +21 -0
  70. package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
  71. package/dist/domain/organization-integration-catalog.d.ts +15 -0
  72. package/dist/domain/organization-membership-lifecycle.d.ts +1 -0
  73. package/dist/domain/personal-connection-delegations.d.ts +87 -4
  74. package/dist/domain/pr-review.d.ts +249 -0
  75. package/dist/domain/resources.d.ts +24 -3
  76. package/dist/domain/scheduled-task-access.d.ts +247 -0
  77. package/dist/domain/scheduled-tasks.d.ts +103 -6
  78. package/dist/domain/session-connection-accounts.d.ts +16 -0
  79. package/dist/domain/session-file-access.d.ts +4 -0
  80. package/dist/domain/session-native-connection-replacement.d.ts +14 -0
  81. package/dist/domain/session-start-timing.d.ts +7 -0
  82. package/dist/domain/session-tool-policy.d.ts +93 -4
  83. package/dist/domain/sessions.d.ts +275 -28
  84. package/dist/domain/skill-imports.d.ts +1 -0
  85. package/dist/domain/skill-search.d.ts +30 -0
  86. package/dist/domain/skills.d.ts +117 -0
  87. package/dist/domain/slack-bot.d.ts +46 -1
  88. package/dist/domain/workspace-members.d.ts +8 -1
  89. package/dist/editable-artifact-live/application.d.ts +12 -3
  90. package/dist/editable-artifact-live/ports.d.ts +0 -1
  91. package/dist/editable-artifact-live/server.d.ts +5 -2
  92. package/dist/editable-artifact-live/ticket.d.ts +2 -2
  93. package/dist/editable-artifact-live/types.d.ts +14 -2
  94. package/dist/editable-artifact-live.js +2 -2
  95. package/dist/editable-artifacts.js +10 -4
  96. package/dist/index.d.ts +49 -3
  97. package/dist/index.js +18826 -6847
  98. package/dist/index.js.map +1 -1
  99. package/dist/managed-auth-session-sets.d.ts +131 -0
  100. package/dist/managed-auth-session-sets.js +51 -0
  101. package/dist/managed-auth-session-sets.js.map +1 -0
  102. package/dist/managed-session.d.ts +98 -11
  103. package/dist/model-catalog.d.ts +117 -0
  104. package/dist/product-usage-metrics.d.ts +30 -0
  105. package/dist/rigs/index.d.ts +22 -5
  106. package/dist/rigs/provider-images.d.ts +1 -1
  107. package/dist/sandbox/fleet.d.ts +59 -10
  108. package/dist/sandbox/routing.d.ts +22 -2
  109. package/dist/sandbox/runtime-settings.d.ts +56 -0
  110. package/dist/session-authorization.d.ts +38 -1
  111. package/dist/site-session-origin.d.ts +9 -0
  112. package/dist/transcription.d.ts +12 -1
  113. package/dist/turn-surface.d.ts +18 -0
  114. package/dist/user-presence.d.ts +32 -0
  115. package/package.json +18 -11
  116. package/src/access/external-actor-authority.ts +94 -0
  117. package/src/access/index.ts +1386 -44
  118. package/src/access/service-initiator.ts +50 -0
  119. package/src/application/api-integration-servers.ts +215 -0
  120. package/src/application/archived-session-imports.ts +161 -0
  121. package/src/application/composer-submit.ts +77 -0
  122. package/src/application/connect-authority.ts +77 -0
  123. package/src/application/connect-operation.ts +80 -0
  124. package/src/application/external-continuation.ts +129 -0
  125. package/src/application/external-identity-lifecycle.ts +48 -0
  126. package/src/application/external-link-work-admission.ts +90 -0
  127. package/src/application/external-workspace-members.ts +232 -0
  128. package/src/application/modal-native-worker-host-transport.ts +263 -0
  129. package/src/application/new-session-drafts.ts +222 -29
  130. package/src/application/sandbox-recovery.ts +78 -0
  131. package/src/application/session-commands.ts +430 -73
  132. package/src/application/session-mcp-credential-rotation.ts +217 -0
  133. package/src/application/session-tenancy.ts +445 -0
  134. package/src/application/user-resource-grants.ts +154 -0
  135. package/src/billing/limits.ts +103 -24
  136. package/src/canonical-human-identities.ts +22 -3
  137. package/src/codex-model-availability.ts +136 -0
  138. package/src/default-session-model.ts +656 -0
  139. package/src/dependencies.ts +97 -5
  140. package/src/developer-setup-credential-ceiling.ts +60 -0
  141. package/src/domain/agent-config-resolution.ts +148 -0
  142. package/src/domain/automations.ts +270 -0
  143. package/src/domain/capabilities.ts +244 -151
  144. package/src/domain/company-profile-agent-admin.ts +56 -0
  145. package/src/domain/connection-account-selection-error.ts +19 -0
  146. package/src/domain/connector-tool-permissions.ts +371 -0
  147. package/src/domain/direct-model-provider.ts +62 -0
  148. package/src/domain/editable-artifacts/agent-application.ts +61 -15
  149. package/src/domain/editable-artifacts/durable-export.ts +100 -5
  150. package/src/domain/editable-artifacts/in-memory.ts +6 -0
  151. package/src/domain/editable-artifacts/ports.ts +11 -0
  152. package/src/domain/editable-artifacts/service.ts +32 -1
  153. package/src/domain/editable-artifacts/snapshot-compaction.ts +9 -5
  154. package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +10 -8
  155. package/src/domain/editable-artifacts/snapshot-verifier.ts +10 -3
  156. package/src/domain/environments.ts +25 -45
  157. package/src/domain/external-creation-attribution.ts +22 -0
  158. package/src/domain/file-owner.ts +67 -0
  159. package/src/domain/github-action-policies.ts +169 -0
  160. package/src/domain/github-repository-bindings.ts +295 -0
  161. package/src/domain/github-skill-source.ts +233 -0
  162. package/src/domain/governed-learning-slack-publication.ts +157 -0
  163. package/src/domain/insights-timing.ts +95 -0
  164. package/src/domain/insights-usage.ts +67 -0
  165. package/src/domain/insights.ts +270 -169
  166. package/src/domain/knowledge-files.ts +46 -0
  167. package/src/domain/knowledge-messages.test.ts +91 -0
  168. package/src/domain/knowledge-messages.ts +106 -0
  169. package/src/domain/knowledge-preparation.test.ts +276 -0
  170. package/src/domain/knowledge-preparation.ts +147 -0
  171. package/src/domain/knowledge-search.test.ts +561 -0
  172. package/src/domain/knowledge-search.ts +256 -0
  173. package/src/domain/knowledge.ts +133 -0
  174. package/src/domain/mcp-account-bindings.ts +171 -0
  175. package/src/domain/mcp-account-routes.ts +121 -0
  176. package/src/domain/native-mcp-connection-admission.ts +14 -0
  177. package/src/domain/organization-integration-catalog.ts +88 -0
  178. package/src/domain/organization-membership-lifecycle.ts +10 -0
  179. package/src/domain/personal-connection-delegations.ts +758 -115
  180. package/src/domain/pr-review.ts +687 -0
  181. package/src/domain/resources.ts +182 -25
  182. package/src/domain/scheduled-task-access.ts +1052 -0
  183. package/src/domain/scheduled-tasks.ts +1952 -215
  184. package/src/domain/session-connection-accounts.ts +67 -0
  185. package/src/domain/session-file-access.ts +47 -0
  186. package/src/domain/session-native-connection-replacement.ts +64 -0
  187. package/src/domain/session-start-timing.ts +55 -0
  188. package/src/domain/session-tool-policy.ts +443 -19
  189. package/src/domain/sessions.ts +3417 -619
  190. package/src/domain/skill-imports.ts +137 -17
  191. package/src/domain/skill-search.ts +221 -0
  192. package/src/domain/skills.ts +104 -0
  193. package/src/domain/slack-bot.ts +128 -3
  194. package/src/domain/workspace-members.ts +34 -2
  195. package/src/editable-artifact-live/application.ts +29 -4
  196. package/src/editable-artifact-live/ports.ts +0 -1
  197. package/src/editable-artifact-live/server.ts +167 -13
  198. package/src/editable-artifact-live/ticket.ts +87 -6
  199. package/src/editable-artifact-live/types.ts +16 -2
  200. package/src/editable-artifact-live/wire.ts +0 -10
  201. package/src/index.ts +69 -4
  202. package/src/managed-auth-session-sets.ts +371 -0
  203. package/src/managed-session.ts +520 -2
  204. package/src/model-catalog.ts +873 -0
  205. package/src/product-usage-metrics.ts +101 -0
  206. package/src/rigs/index.ts +179 -63
  207. package/src/rigs/provider-images.ts +2 -3
  208. package/src/sandbox/fleet.ts +471 -59
  209. package/src/sandbox/routing.ts +160 -33
  210. package/src/sandbox/runtime-settings.ts +281 -0
  211. package/src/session-authorization.ts +262 -78
  212. package/src/site-session-origin.ts +22 -0
  213. package/src/transcription.ts +13 -1
  214. package/src/turn-surface.ts +45 -0
  215. package/src/user-presence.ts +134 -0
  216. package/dist/chunk-6WKPWE5S.js.map +0 -1
  217. package/dist/chunk-IBOEYG6N.js +0 -34
  218. package/dist/chunk-IBOEYG6N.js.map +0 -1
  219. package/dist/chunk-JJU6I5XD.js.map +0 -1
  220. package/dist/chunk-NYPMQ7NO.js.map +0 -1
  221. package/dist/domain/durable-learning-slack-publication.d.ts +0 -72
  222. package/dist/domain/packs.d.ts +0 -55
  223. package/src/domain/durable-learning-slack-publication.ts +0 -202
  224. package/src/domain/packs.ts +0 -556
@@ -1,14 +1,42 @@
1
- import type { Settings } from "@opengeni/config";
2
1
  import {
2
+ allowedFirstPartyMcpToolsForSession,
3
+ codeSearchDeploymentPolicy,
4
+ resolveModelProviderForTurn,
5
+ resolveFirstPartyDelegationSecret,
6
+ type Settings,
7
+ } from "@opengeni/config";
8
+ import {
9
+ AGENT_SKILL_MANAGE_TOOL_NAMES,
10
+ AUTOMATIC_SESSION_TITLE_FALLBACK,
11
+ DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
12
+ FIRST_PARTY_IN_PROCESS_TOOL_NAMES,
13
+ type FirstPartyMcpToolName,
3
14
  SESSION_EFFECTIVE_TOOL_POLICY_ID_LIMIT,
4
15
  SESSION_EFFECTIVE_TOOL_POLICY_ID_MAX_LENGTH,
5
16
  mergeToolRefs,
17
+ projectAgentEffectiveTools,
18
+ resolveAgentToolFamilies,
19
+ resolveAgentMediaToolSurface,
20
+ resolveWorkspaceAgentHumanInputEnabled,
21
+ resolveWorkspaceSessionToolDefaults,
22
+ type AgentFunctionToolName,
23
+ type AgentToolEnvironment,
24
+ type AgentMediaAttachment,
6
25
  type Session,
26
+ type SessionTurn,
7
27
  type SessionEffectiveToolPolicy,
8
28
  type SessionToolPolicy,
9
29
  type ToolRef,
10
30
  } from "@opengeni/contracts";
11
- import type { Database } from "@opengeni/db";
31
+ import { codeSearchEnabledForTurn } from "@opengeni/contracts/code-search";
32
+ import {
33
+ getSandbox,
34
+ listSkillDescriptors,
35
+ requireWorkspace,
36
+ sessionHasToolRouterHistory,
37
+ type Database,
38
+ } from "@opengeni/db";
39
+ import { resolveWorkspaceCatalogSettings } from "../model-catalog";
12
40
  import { settingsWithEnabledCapabilityMcpServers } from "./capabilities";
13
41
 
14
42
  const MANDATORY_SESSION_MCP_SERVER_IDS = ["opengeni"] as const;
@@ -64,8 +92,11 @@ export function resolveSessionToolPolicy(input: SessionToolPolicyInput): Resolve
64
92
  availableIds.has(id),
65
93
  );
66
94
  const mandatoryIdSet = new Set<string>(mandatoryIds);
67
- const selectedRefs = mergeToolRefs([], input.sessionTools);
68
95
  const tracksWorkspaceDefaults = policy.mode === "workspace_default";
96
+ const excludedIds = new Set(tracksWorkspaceDefaults ? policy.excludedMcpServerIds : []);
97
+ const selectedRefs = mergeToolRefs([], input.sessionTools).filter(
98
+ (tool) => !excludedIds.has(tool.id) || mandatoryIdSet.has(tool.id),
99
+ );
69
100
 
70
101
  // Persisted refs may outlive a capability installation, deployment config,
71
102
  // or its credentials. Admission remains strict for newly requested refs, but
@@ -79,7 +110,7 @@ export function resolveSessionToolPolicy(input: SessionToolPolicyInput): Resolve
79
110
  toolRefs = mergeToolRefs(
80
111
  toolRefs,
81
112
  sortedIds(defaultIds)
82
- .filter((id) => availableIds.has(id))
113
+ .filter((id) => availableIds.has(id) && !excludedIds.has(id))
83
114
  .map((id) => ({ kind: "mcp" as const, id, optional: true as const })),
84
115
  );
85
116
  }
@@ -96,7 +127,7 @@ export function resolveSessionToolPolicy(input: SessionToolPolicyInput): Resolve
96
127
  selectedRefs,
97
128
  tracksWorkspaceDefaults
98
129
  ? sortedIds(defaultIds)
99
- .filter((id) => availableIds.has(id))
130
+ .filter((id) => availableIds.has(id) && !excludedIds.has(id))
100
131
  .map((id) => ({ kind: "mcp" as const, id, optional: true as const }))
101
132
  : [],
102
133
  );
@@ -109,15 +140,12 @@ export function resolveSessionToolPolicy(input: SessionToolPolicyInput): Resolve
109
140
  const configuredIds = effectiveIds.filter((id) => availableIds.has(id));
110
141
  const configuredIdSet = new Set(configuredIds);
111
142
  const droppedIds = effectiveIds.filter((id) => !configuredIdSet.has(id));
112
- // Lazy discovery is scoped to the effective MCP allow-list, not only to
113
- // workspace-default capability refs. An explicitly selected connector is
114
- // directly callable from this same materialized list, so excluding it from
115
- // the existing router creates a false "no connected sources" result. The
116
- // mandatory first-party OpenGeni server stays eager; every other configured
117
- // effective server is eligible for bounded schema disclosure.
143
+ // Lazy discovery is scoped to the effective MCP allow-list. Mandatory means
144
+ // selected/fail-loud, not eager: only an exact session ref with eager:true
145
+ // belongs on the first model request's critical path.
118
146
  const deferredIds = sortedIds(
119
147
  toolRefs
120
- .filter((tool) => configuredIdSet.has(tool.id) && !mandatoryIdSet.has(tool.id))
148
+ .filter((tool) => configuredIdSet.has(tool.id) && tool.eager !== true)
121
149
  .map((tool) => tool.id),
122
150
  );
123
151
  const selectedIds = sortedIds(
@@ -164,6 +192,63 @@ export function resolveSessionToolPolicy(input: SessionToolPolicyInput): Resolve
164
192
  };
165
193
  }
166
194
 
195
+ /** Null is ordinary work; an empty list is a frozen scheduled selection. */
196
+ export function scheduledTurnMcpServerIds(turn: Pick<SessionTurn, "metadata">): string[] | null {
197
+ const metadata = turn.metadata;
198
+ const value =
199
+ metadata && typeof metadata === "object" && !Array.isArray(metadata)
200
+ ? metadata.scheduledEffectiveMcpServerIds
201
+ : null;
202
+ return Array.isArray(value) && value.every((id) => typeof id === "string")
203
+ ? sortedIds(value)
204
+ : null;
205
+ }
206
+
207
+ /**
208
+ * Execution's canonical turn selection. Ordinary turns use the durable session
209
+ * policy, not the queue's omitted-tools `[]`. Scheduled turns instead retain
210
+ * their frozen registry and refs, including an explicitly empty selection.
211
+ */
212
+ export function resolveTurnToolPolicy(
213
+ input: Omit<SessionToolPolicyInput, "sessionTools"> & {
214
+ session: Pick<Session, "tools">;
215
+ turn: Pick<SessionTurn, "tools" | "metadata">;
216
+ },
217
+ ): ResolvedSessionToolPolicy {
218
+ const scheduledIds = scheduledTurnMcpServerIds(input.turn);
219
+ const availableIds = new Set(input.availableMcpServerIds);
220
+ return resolveSessionToolPolicy({
221
+ toolPolicy: input.toolPolicy,
222
+ sessionTools: scheduledIds ? input.turn.tools : input.session.tools,
223
+ availableMcpServerIds: scheduledIds
224
+ ? scheduledIds.filter((id) => availableIds.has(id))
225
+ : availableIds,
226
+ defaultMcpServerIds: scheduledIds ?? input.defaultMcpServerIds ?? [],
227
+ });
228
+ }
229
+
230
+ /** Resolve availability and defaults from the same subject-scoped registry read. */
231
+ export async function workspaceSessionToolPolicyContext(
232
+ db: Database,
233
+ workspaceId: string,
234
+ settings: Settings,
235
+ subjectId?: string,
236
+ ): Promise<{ workspaceServerIds: string[]; workspaceDefaultServerIds: string[] }> {
237
+ const [runtimeSettings, workspace] = await Promise.all([
238
+ settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings, {
239
+ ...(subjectId ? { subjectId } : {}),
240
+ }),
241
+ requireWorkspace(db, workspaceId),
242
+ ]);
243
+ return {
244
+ workspaceServerIds: sortedIds(runtimeSettings.mcpServers.map((server) => server.id)),
245
+ workspaceDefaultServerIds: workspaceSessionToolPolicyDefaultServerIdsFor(
246
+ runtimeSettings.mcpServers,
247
+ workspace.settings,
248
+ ),
249
+ };
250
+ }
251
+
167
252
  /** Current full runtime registry IDs, including configured static servers. */
168
253
  export async function workspaceSessionToolPolicyServerIds(
169
254
  db: Database,
@@ -187,7 +272,332 @@ export async function workspaceSessionToolPolicyDefaultServerIds(
187
272
  const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings, {
188
273
  ...(subjectId ? { subjectId } : {}),
189
274
  });
190
- return defaultSessionMcpServerIds(runtimeSettings.mcpServers);
275
+ const workspace = await requireWorkspace(db, workspaceId);
276
+ return workspaceSessionToolPolicyDefaultServerIdsFor(
277
+ runtimeSettings.mcpServers,
278
+ workspace.settings,
279
+ );
280
+ }
281
+
282
+ /**
283
+ * The omitted-tools default for one resolved runtime registry and one
284
+ * workspace settings bag. Pure so a caller that already holds both never has
285
+ * to re-query the capability registry to agree with the worker and the
286
+ * composer on which connectors a `workspace_default` session executes with.
287
+ */
288
+ export function workspaceSessionToolPolicyDefaultServerIdsFor(
289
+ runtimeMcpServers: Iterable<{ id: string }>,
290
+ workspaceSettings: unknown,
291
+ ): string[] {
292
+ const availableDefaults = defaultSessionMcpServerIds(runtimeMcpServers);
293
+ const configured = resolveWorkspaceSessionToolDefaults(workspaceSettings);
294
+ if (!configured?.mcpServerIds) return availableDefaults;
295
+ const available = new Set(availableDefaults);
296
+ return sortedIds([
297
+ ...configured.mcpServerIds.filter((id) => available.has(id)),
298
+ ...(configured.inheritConnectedMcpServers
299
+ ? availableDefaults.filter((id) => !["opengeni", "files", "docs"].includes(id))
300
+ : []),
301
+ ]);
302
+ }
303
+
304
+ /** Metadata only: no MCP listing, provider probes, account selection or credentials. */
305
+ export type SessionEffectiveToolsContext = {
306
+ settings: Settings;
307
+ humanInputEnabled: boolean;
308
+ hasWorkspaceSkills: boolean;
309
+ workspaceSettings?: unknown;
310
+ objectStorageAvailable: boolean;
311
+ activeSandboxBackends?: ReadonlyMap<string, Settings["sandboxBackend"]>;
312
+ /** Exact build options after adapter/credential resolution, keyed by session. No pool-readiness inference. */
313
+ mediaAttachments?: ReadonlyMap<string, AgentMediaAttachment>;
314
+ routerInHistory?: boolean;
315
+ routerHistorySessionIds?: ReadonlySet<string>;
316
+ };
317
+
318
+ /**
319
+ * Resolve one context for a response page, not one catalog per session. Legacy
320
+ * rows require no additional reads and never acquire an effectiveTools field.
321
+ */
322
+ export async function workspaceSessionEffectiveToolsContext(
323
+ deps: { db: Database; settings: Settings; objectStorage?: unknown },
324
+ workspaceId: string,
325
+ subjectId: string,
326
+ sessions: readonly Session[],
327
+ ): Promise<SessionEffectiveToolsContext> {
328
+ const configured = sessions.filter((session) => session.agent != null);
329
+ const baseline: SessionEffectiveToolsContext = {
330
+ settings: deps.settings,
331
+ humanInputEnabled: false,
332
+ hasWorkspaceSkills: false,
333
+ objectStorageAvailable: Boolean(deps.objectStorage),
334
+ };
335
+ if (configured.length === 0) return baseline;
336
+ const [workspace, catalog, descriptors, sandboxes, routerHistory] = await Promise.all([
337
+ requireWorkspace(deps.db, workspaceId),
338
+ resolveWorkspaceCatalogSettings(deps.db, deps.settings, {
339
+ accountId: configured[0]!.accountId,
340
+ workspaceId,
341
+ retainedProductModelIds: configured.map((session) => session.model),
342
+ }),
343
+ configured.some((session) => session.agent?.capabilities.skills === "read")
344
+ ? listSkillDescriptors(deps.db, {
345
+ accountId: configured[0]!.accountId,
346
+ workspaceId,
347
+ subjectId,
348
+ })
349
+ : Promise.resolve([]),
350
+ deps.settings.sandboxSelfhostedEnabled
351
+ ? Promise.all(
352
+ sortedIds(configured.flatMap((session) => session.activeSandboxId ?? [])).map(
353
+ async (id) =>
354
+ [
355
+ id,
356
+ await getSandbox(
357
+ deps.db,
358
+ { accountId: configured[0]!.accountId, workspaceId, subjectId },
359
+ id,
360
+ ),
361
+ ] as const,
362
+ ),
363
+ )
364
+ : Promise.resolve([]),
365
+ Promise.all(
366
+ [...new Map(configured.map((session) => [session.id, session])).values()].map(
367
+ async (session) =>
368
+ [
369
+ session.id,
370
+ await sessionHasToolRouterHistory(deps.db, {
371
+ accountId: session.accountId,
372
+ workspaceId,
373
+ sessionId: session.id,
374
+ }),
375
+ ] as const,
376
+ ),
377
+ ),
378
+ ]);
379
+ return {
380
+ ...baseline,
381
+ settings: catalog.settings,
382
+ workspaceSettings: workspace.settings,
383
+ humanInputEnabled: resolveWorkspaceAgentHumanInputEnabled(workspace.settings),
384
+ hasWorkspaceSkills: descriptors.some((entry) => entry.activationMode === "workspace_managed"),
385
+ activeSandboxBackends: new Map(
386
+ sandboxes.flatMap(([id, sandbox]) =>
387
+ sandbox?.kind === "selfhosted" ? [[id, "selfhosted" as const]] : [],
388
+ ),
389
+ ),
390
+ routerHistorySessionIds: new Set(
391
+ routerHistory.filter(([, present]) => present).map(([id]) => id),
392
+ ),
393
+ };
394
+ }
395
+
396
+ function sessionHasSkills(session: Session, context: SessionEffectiveToolsContext): boolean {
397
+ if (context.hasWorkspaceSkills || session.skills.length > 0) return true;
398
+ // Undefined is the worker's bundled default. Explicit [] means no bundles.
399
+ if (session.bundledSkillIds === undefined) return true;
400
+ const tools = new Set(
401
+ resolveAgentToolFamilies(session.agent).firstPartyTools(
402
+ allowedFirstPartyMcpToolsForSession(context.settings, session.firstPartyMcpTools),
403
+ ),
404
+ );
405
+ return session.bundledSkillIds.some((id) => {
406
+ if (
407
+ [
408
+ "builtin:opengeni-documents",
409
+ "builtin:opengeni-spreadsheets",
410
+ "builtin:opengeni-presentations",
411
+ ].includes(id)
412
+ ) {
413
+ return tools.has("editable_artifact_list") && tools.has("editable_artifact_get");
414
+ }
415
+ if (id === "builtin:opengeni-sites") {
416
+ return tools.has("artifacts_create") && tools.has("artifacts_publish");
417
+ }
418
+ if (id === "builtin:opengeni-video-generation") {
419
+ return context.mediaAttachments?.get(session.id)?.video === true;
420
+ }
421
+ return true;
422
+ });
423
+ }
424
+
425
+ /** The server can prove attachment policy, not successful future provider calls. */
426
+ export function sessionEffectiveToolProjectionInput(
427
+ session: Session & { agent: NonNullable<Session["agent"]> },
428
+ toolRefs: readonly ToolRef[],
429
+ context?: SessionEffectiveToolsContext,
430
+ ) {
431
+ const model = context ? resolveModelProviderForTurn(context.settings, session.model) : undefined;
432
+ const sandboxBackend =
433
+ (session.activeSandboxId
434
+ ? context?.activeSandboxBackends?.get(session.activeSandboxId)
435
+ : undefined) ?? session.sandboxBackend;
436
+ const sandboxAvailable = context !== undefined && sandboxBackend !== "none";
437
+ const supportsImages =
438
+ model?.model.api === "responses" && model.model.capabilities.inputModalities.includes("image");
439
+ const hostedToolNames: AgentFunctionToolName[] = [];
440
+ if (model?.provider.kind === "xai-subscription") {
441
+ if (context?.settings.webSearchEnabled) hostedToolNames.push("web_search", "x_search");
442
+ } else if (model?.model.hostedWebSearch) hostedToolNames.push("web_search");
443
+ const mediaAttachment =
444
+ context?.mediaAttachments?.get(session.id) ??
445
+ (context?.objectStorageAvailable === false
446
+ ? ({ image: null, video: false } as const)
447
+ : undefined);
448
+ const media = resolveAgentMediaToolSurface(session.agent, mediaAttachment);
449
+ hostedToolNames.push(...media.hosted);
450
+ const runtimeToolNames: AgentFunctionToolName[] = context
451
+ ? [
452
+ "request_human_input",
453
+ "list_models",
454
+ "skill_read",
455
+ ...AGENT_SKILL_MANAGE_TOOL_NAMES,
456
+ ...media.runtime,
457
+ ]
458
+ : [];
459
+ const sandboxToolNames: AgentFunctionToolName[] = sandboxAvailable
460
+ ? [
461
+ "exec_command",
462
+ "write_stdin",
463
+ "apply_patch",
464
+ ...(supportsImages ? ["view_image" as const] : []),
465
+ ...(session.resources.some((resource) => resource.kind === "repository")
466
+ ? ["repository_skill_read" as const]
467
+ : []),
468
+ ...(codeSearchEnabledForTurn(
469
+ session.codeSearchEnabled,
470
+ context?.workspaceSettings,
471
+ codeSearchDeploymentPolicy(context!.settings),
472
+ ) && session.sandboxOs !== "windows"
473
+ ? ["code_search" as const]
474
+ : []),
475
+ ]
476
+ : [];
477
+ const needsTitle =
478
+ session.titleSource !== "user" &&
479
+ (!session.title?.trim() || session.title.trim() === AUTOMATIC_SESSION_TITLE_FALLBACK);
480
+ const firstPartyMcpTools =
481
+ context && toolRefs.some((ref) => ref.id === "opengeni")
482
+ ? resolveAgentToolFamilies(session.agent)
483
+ .firstPartyTools(
484
+ allowedFirstPartyMcpToolsForSession(context.settings, session.firstPartyMcpTools),
485
+ )
486
+ .filter((name) => name !== "set_session_title" || !needsTitle)
487
+ : [];
488
+ const interactionNames = new Set<string>(FIRST_PARTY_IN_PROCESS_TOOL_NAMES);
489
+ const firstPartyModelNames = new Map<FirstPartyMcpToolName, string>();
490
+ const permissions: readonly string[] =
491
+ session.firstPartyMcpPermissions ?? DEFAULT_FIRST_PARTY_MCP_PERMISSIONS;
492
+ const permissionAllowed = (permission: string) =>
493
+ permissions.includes(permission as never) || permissions.includes("workspace:admin");
494
+ if (context && resolveFirstPartyDelegationSecret(context.settings)) {
495
+ for (const name of FIRST_PARTY_IN_PROCESS_TOOL_NAMES) {
496
+ const readOnly = [
497
+ "interaction_discover",
498
+ "browser_observe",
499
+ "browser_read",
500
+ "browser_screenshot",
501
+ "browser_clipboard",
502
+ "browser_debug",
503
+ "browser_downloads",
504
+ "computer_targets",
505
+ "computer_observe",
506
+ "computer_clipboard",
507
+ ].includes(name);
508
+ if (
509
+ permissionAllowed(readOnly ? "sessions:read" : "sessions:control") &&
510
+ (name !== "browser_download_save" || permissionAllowed("files:upload"))
511
+ ) {
512
+ firstPartyModelNames.set(name, `interaction__${name}`);
513
+ if (
514
+ !firstPartyMcpTools.includes(name) &&
515
+ resolveAgentToolFamilies(session.agent).allowsFirstPartyTool(name) &&
516
+ session.firstPartyMcpTools.includes(name)
517
+ )
518
+ firstPartyMcpTools.push(name);
519
+ }
520
+ }
521
+ }
522
+ // Interaction names are never registered by the remote first-party server.
523
+ for (let index = firstPartyMcpTools.length - 1; index >= 0; index--) {
524
+ const name = firstPartyMcpTools[index]!;
525
+ if (interactionNames.has(name) && !firstPartyModelNames.has(name))
526
+ firstPartyMcpTools.splice(index, 1);
527
+ }
528
+ const codex = model?.provider.kind === "codex-subscription";
529
+ const retainedRouter =
530
+ context?.routerInHistory === true || context?.routerHistorySessionIds?.has(session.id) === true;
531
+ const progressiveDisclosure =
532
+ retainedRouter ||
533
+ (context
534
+ ? codex
535
+ ? context.settings.codexToolSearchEnabled
536
+ : context.settings.lazyToolSearchEnabled
537
+ : false);
538
+ const upfrontToolNames = new Set<string>([
539
+ ...hostedToolNames,
540
+ ...sandboxToolNames,
541
+ "skill_read",
542
+ "request_human_input",
543
+ "list_models",
544
+ ...(toolRefs.some((ref) => ref.id === "opengeni" && ref.eager === true) ||
545
+ !progressiveDisclosure
546
+ ? firstPartyMcpTools.filter((name) => !interactionNames.has(name) || !progressiveDisclosure)
547
+ : []),
548
+ ...(!progressiveDisclosure ? runtimeToolNames : []),
549
+ ]);
550
+ const environment: AgentToolEnvironment = {
551
+ productServerIds: new Set([
552
+ ...session.mcpServers.map((server) => server.id),
553
+ ...(session.toolPolicy.mode !== "workspace_default"
554
+ ? session.tools.map((tool) => tool.id)
555
+ : []),
556
+ ]),
557
+ hasSkills: context ? sessionHasSkills(session, context) : false,
558
+ webSearch: hostedToolNames.includes("web_search"),
559
+ humanInput: context?.humanInputEnabled ?? false,
560
+ media: media.toolsKnown ? media.hosted.length + media.runtime.length > 0 : undefined,
561
+ routerInHistory:
562
+ context?.routerInHistory === true ||
563
+ context?.routerHistorySessionIds?.has(session.id) === true,
564
+ };
565
+ const families = resolveAgentToolFamilies(session.agent, environment);
566
+ environment.hasDeferredTools =
567
+ toolRefs.some(
568
+ (ref) => ref.id !== "opengeni" && ref.eager !== true && families.allowsMcpServer(ref.id),
569
+ ) ||
570
+ firstPartyMcpTools.some(
571
+ (name) => families.allowsFirstPartyTool(name) && !upfrontToolNames.has(name),
572
+ ) ||
573
+ runtimeToolNames.some(
574
+ (name) => families.allowsFunctionTool(name) && !upfrontToolNames.has(name),
575
+ );
576
+ const provider = model?.provider;
577
+ const native =
578
+ codex ||
579
+ (provider?.api === "responses" &&
580
+ (provider.wireProfile === "azure-openai" ||
581
+ (provider.builtin && provider.id === "openai" && provider.baseUrl === undefined)));
582
+ const routerToolNames: AgentFunctionToolName[] =
583
+ progressiveDisclosure && resolveAgentToolFamilies(session.agent, environment).router
584
+ ? ["tool_search", "tool_list", ...(native ? [] : ["tool_invoke" as const])]
585
+ : [];
586
+ for (const name of routerToolNames) upfrontToolNames.add(name);
587
+ return {
588
+ config: session.agent,
589
+ firstPartyMcpTools,
590
+ firstPartyModelNames,
591
+ mcpServerIds: toolRefs.map((ref) => ref.id),
592
+ productServerIds: environment.productServerIds ?? new Set<string>(),
593
+ environment,
594
+ runtimeToolNames,
595
+ hostedToolNames,
596
+ sandboxToolNames,
597
+ routerToolNames,
598
+ upfrontToolNames,
599
+ mediaAttachment,
600
+ };
191
601
  }
192
602
 
193
603
  /** Add a bounded, secret-safe effective projection to a session response. */
@@ -195,18 +605,32 @@ export function sessionWithEffectiveToolPolicy(
195
605
  session: Session,
196
606
  workspaceServerIds: Iterable<string>,
197
607
  workspaceDefaultServerIds: Iterable<string> = [],
608
+ effectiveToolsContext?: SessionEffectiveToolsContext,
198
609
  ): Session {
199
610
  const availableIds = new Set(workspaceServerIds);
200
611
  for (const server of session.mcpServers) {
201
612
  availableIds.add(server.id);
202
613
  }
614
+ const { effectivePolicy: effectiveToolPolicy, toolRefs } = resolveSessionToolPolicy({
615
+ toolPolicy: session.toolPolicy,
616
+ sessionTools: session.tools,
617
+ availableMcpServerIds: availableIds,
618
+ defaultMcpServerIds: workspaceDefaultServerIds,
619
+ });
203
620
  return {
204
621
  ...session,
205
- effectiveToolPolicy: resolveSessionToolPolicy({
206
- toolPolicy: session.toolPolicy,
207
- sessionTools: session.tools,
208
- availableMcpServerIds: availableIds,
209
- defaultMcpServerIds: workspaceDefaultServerIds,
210
- }).effectivePolicy,
622
+ effectiveToolPolicy,
623
+ // Configured sessions also report what they can use at capability level.
624
+ ...(session.agent
625
+ ? {
626
+ effectiveTools: projectAgentEffectiveTools(
627
+ sessionEffectiveToolProjectionInput(
628
+ { ...session, agent: session.agent },
629
+ toolRefs,
630
+ effectiveToolsContext,
631
+ ),
632
+ ),
633
+ }
634
+ : {}),
211
635
  };
212
636
  }