@opengeni/core 2.7.5-canary.6 → 2.8.0-canary.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.
@@ -80,7 +80,7 @@ export declare const prReviewAutomationAdapter: {
80
80
  eager?: boolean | undefined;
81
81
  optional?: boolean | undefined;
82
82
  }[];
83
- firstPartyMcpTools: ("artifacts_archive" | "artifacts_create" | "artifacts_get_source" | "artifacts_list" | "artifacts_prepare_upload" | "artifacts_publish" | "artifacts_restore" | "artifacts_rollback" | "atlassian_get" | "atlassian_search" | "atlassian_sources_list" | "browser_act" | "browser_auth" | "browser_clipboard" | "browser_debug" | "browser_identity" | "browser_lifecycle" | "browser_observe" | "browser_open" | "browser_publish" | "browser_tabs" | "capability_authorization_request" | "capability_catalog_search" | "command_read" | "command_wait" | "company_profile_confirm" | "company_profile_propose" | "computer_act" | "computer_clipboard" | "computer_lifecycle" | "computer_observe" | "computer_open" | "computer_targets" | "connected_machine_remove" | "editable_artifact_apply" | "editable_artifact_create" | "editable_artifact_export" | "editable_artifact_export_status" | "editable_artifact_get" | "editable_artifact_import" | "editable_artifact_inspect" | "editable_artifact_list" | "environment_list" | "environment_set_variable" | "fiken_bank_accounts_list" | "fiken_companies_list" | "fiken_contact_create" | "fiken_contacts_list" | "fiken_invoice_draft_create" | "fiken_invoice_get" | "fiken_invoices_list" | "fiken_products_list" | "fiken_purchases_list" | "fiken_sales_list" | "github_connect_link" | "github_repositories_list" | "goal_complete" | "goal_pause" | "goal_progress" | "goal_resume" | "goal_set" | "goal_update" | "instruction_policy_propose" | "interaction_discover" | "interaction_request_human" | "knowledge_correct" | "knowledge_propose" | "memory_correct" | "memory_save" | "memory_search" | "preference_propose" | "preference_registry_get" | "preference_registry_summary" | "reddit_accounts_list" | "reddit_mentions_live" | "reddit_post_reply" | "reddit_posts_sync" | "reddit_search_live" | "reddit_thread_fetch" | "remember" | "remember_confirm" | "rig_get" | "rig_list" | "rig_promote" | "rig_propose_change" | "rig_verify" | "run_on" | "sandbox_attach" | "sandbox_file_publish" | "sandbox_provision" | "sandbox_swap" | "sandboxes_list" | "scheduled_task_runs_list" | "scheduled_tasks_create" | "scheduled_tasks_delete" | "scheduled_tasks_get" | "scheduled_tasks_list" | "scheduled_tasks_pause" | "scheduled_tasks_resume" | "scheduled_tasks_trigger" | "scheduled_tasks_update" | "session_create" | "session_events" | "session_get" | "session_human_input_respond" | "session_pause" | "session_resume" | "session_send_message" | "session_steer" | "session_wait" | "sessions_list" | "set_other_session_title" | "set_session_title" | "slack_bot_channel_history" | "slack_bot_delete_message" | "slack_bot_file_content" | "slack_bot_file_info" | "slack_bot_list_channels" | "slack_bot_list_files" | "slack_bot_list_users" | "slack_bot_post_message" | "slack_bot_search" | "slack_bot_thread_replies" | "social_connections_list" | "social_daily_analysis_context" | "social_mentions_live" | "social_post_reply" | "social_posts_recent" | "social_posts_sync" | "social_search_live" | "social_thread_fetch" | "task_note_archive" | "task_note_promote_instruction_policy" | "task_note_promote_knowledge" | "task_note_promote_preference" | "task_note_replace" | "task_note_save" | "task_notes_list" | "variable_set_get_variable" | "variable_set_list" | "variable_set_set_variable" | "wait_for_input" | "work_claim_release" | "work_claim_upsert" | "x_accounts_list" | "x_mentions_live" | "x_post_reply" | "x_posts_sync" | "x_search_live" | "x_thread_fetch")[];
83
+ firstPartyMcpTools: ("artifacts_archive" | "artifacts_create" | "artifacts_get_source" | "artifacts_list" | "artifacts_prepare_upload" | "artifacts_publish" | "artifacts_restore" | "artifacts_rollback" | "atlassian_get" | "atlassian_search" | "atlassian_sources_list" | "browser_act" | "browser_auth" | "browser_clipboard" | "browser_debug" | "browser_identity" | "browser_lifecycle" | "browser_observe" | "browser_open" | "browser_publish" | "browser_tabs" | "capability_authorization_request" | "capability_catalog_search" | "command_read" | "command_wait" | "company_profile_confirm" | "company_profile_propose" | "computer_act" | "computer_clipboard" | "computer_lifecycle" | "computer_observe" | "computer_open" | "computer_targets" | "connected_machine_remove" | "editable_artifact_apply" | "editable_artifact_create" | "editable_artifact_export" | "editable_artifact_export_status" | "editable_artifact_get" | "editable_artifact_import" | "editable_artifact_inspect" | "editable_artifact_list" | "environment_list" | "environment_set_variable" | "fiken_bank_accounts_list" | "fiken_companies_list" | "fiken_contact_create" | "fiken_contacts_list" | "fiken_invoice_draft_create" | "fiken_invoice_get" | "fiken_invoices_list" | "fiken_products_list" | "fiken_purchases_list" | "fiken_sales_list" | "github_connect_link" | "github_repositories_list" | "goal_complete" | "goal_pause" | "goal_progress" | "goal_resume" | "goal_set" | "goal_update" | "instruction_policy_propose" | "interaction_discover" | "interaction_request_human" | "knowledge_correct" | "knowledge_propose" | "memory_correct" | "memory_save" | "memory_search" | "preference_propose" | "preference_registry_get" | "preference_registry_summary" | "project_create" | "project_delete" | "project_get" | "project_list" | "project_reorder" | "project_update" | "reddit_accounts_list" | "reddit_mentions_live" | "reddit_post_reply" | "reddit_posts_sync" | "reddit_search_live" | "reddit_thread_fetch" | "remember" | "remember_confirm" | "rig_get" | "rig_list" | "rig_promote" | "rig_propose_change" | "rig_verify" | "run_on" | "sandbox_attach" | "sandbox_file_publish" | "sandbox_provision" | "sandbox_swap" | "sandboxes_list" | "scheduled_task_runs_list" | "scheduled_tasks_create" | "scheduled_tasks_delete" | "scheduled_tasks_get" | "scheduled_tasks_list" | "scheduled_tasks_pause" | "scheduled_tasks_resume" | "scheduled_tasks_trigger" | "scheduled_tasks_update" | "session_create" | "session_events" | "session_get" | "session_human_input_respond" | "session_pause" | "session_resume" | "session_send_message" | "session_set_project" | "session_steer" | "session_wait" | "sessions_list" | "set_other_session_title" | "set_session_title" | "slack_bot_channel_history" | "slack_bot_delete_message" | "slack_bot_file_content" | "slack_bot_file_info" | "slack_bot_list_channels" | "slack_bot_list_files" | "slack_bot_list_users" | "slack_bot_post_message" | "slack_bot_search" | "slack_bot_thread_replies" | "social_connections_list" | "social_daily_analysis_context" | "social_mentions_live" | "social_post_reply" | "social_posts_recent" | "social_posts_sync" | "social_search_live" | "social_thread_fetch" | "task_note_archive" | "task_note_promote_instruction_policy" | "task_note_promote_knowledge" | "task_note_promote_preference" | "task_note_replace" | "task_note_save" | "task_notes_list" | "variable_set_get_variable" | "variable_set_list" | "variable_set_set_variable" | "wait_for_input" | "work_claim_release" | "work_claim_upsert" | "x_accounts_list" | "x_mentions_live" | "x_post_reply" | "x_posts_sync" | "x_search_live" | "x_thread_fetch")[];
84
84
  firstPartyMcpPermissions: ("account:admin" | "account:read" | "api_keys:manage" | "artifacts:publish" | "artifacts:read" | "billing:manage" | "billing:read" | "capabilities:manage" | "codemode:call" | "connections:read" | "connections:write" | "documents:manage" | "documents:search" | "enrollments:manage" | "enrollments:read" | "environments:manage" | "environments:use" | "files:read" | "files:upload" | "files:write" | "github:manage" | "github:use" | "goals:manage" | "mcp_servers:attach" | "members:manage" | "rigs:manage" | "rigs:use" | "scheduled_tasks:manage" | "scheduled_tasks:run" | "secrets:list" | "secrets:read" | "secrets:write" | "sessions:control" | "sessions:create" | "sessions:read" | "stream:acknowledge" | "stream:control" | "stream:view" | "terminal:attach" | "variable-sets:attach" | "variable-sets:list" | "variable-sets:manage" | "variable-sets:read" | "variable-sets:use" | "variable-sets:write" | "workspace:admin" | "workspace:create" | "workspace:read")[];
85
85
  model: string | null;
86
86
  reasoningEffort: "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh" | null;
@@ -1,5 +1,5 @@
1
1
  import { type Settings } from "@opengeni/config";
2
- import { SessionSpawnDenial, type AccessGrant, type ComposerDraft, type CreateSessionResponse, type GoalSpec, type FirstPartyMcpToolName, type McpPersonalConnectionDelegation, type McpConnectionAuthoritySelection, type Permission, type PersonalResourceAttachmentIntent, type ReasoningEffort, type ResourceRef, type Session, type SessionCommandReceipt, type SessionSkill, type SessionEvent, SessionMcpApprovalPolicy, type SessionMcpCredentialUpdateInput, type SessionMcpServerMetadata, type SubmittedTimelineAnnotation, type TimelineAnnotation, type UpdateSessionMcpApprovalPolicyResponse, type UpdateSessionToolPolicyRequest, type SessionAuthorizationPort, type SessionToolPolicy, type SessionTurn, type SessionPromptRouting, type SessionGoalSnapshot, type ToolRef, type TurnInitiator, type TurnInitiatorContext, type TurnExecutionPolicyV1, type VariableSet, type XaiProviderAccountAuthoritySnapshotV1 } from "@opengeni/contracts";
2
+ import { SessionSpawnDenial, type AccessGrant, type ComposerDraft, type CreateSessionResponse, type GoalSpec, type FirstPartyMcpToolName, type McpPersonalConnectionDelegation, type McpConnectionAuthoritySelection, type Permission, type PersonalResourceAttachmentIntent, type ReasoningEffort, type ResourceRef, type Session, type SessionAgentAccess, type SessionCommandReceipt, type SessionEndUser, type SessionMemoryScope, type SessionSkill, type SessionEvent, SessionMcpApprovalPolicy, type SessionMcpCredentialUpdateInput, type SessionMcpServerMetadata, type SubmittedTimelineAnnotation, type TimelineAnnotation, type UpdateSessionMcpApprovalPolicyResponse, type UpdateSessionToolPolicyRequest, type SessionAuthorizationPort, type SessionToolPolicy, type SessionTurn, type SessionPromptRouting, type SessionGoalSnapshot, type ToolRef, type TurnInitiator, type TurnInitiatorContext, type TurnExecutionPolicyV1, type VariableSet, type XaiProviderAccountAuthoritySnapshotV1 } from "@opengeni/contracts";
3
3
  import { type CreateSessionMcpServerInput, type Database, type UpdateSessionMcpServerCredentialsInput, type SessionCommandActor, type NewSessionDraftSnapshot } from "@opengeni/db";
4
4
  import { type EventBus } from "@opengeni/events";
5
5
  import { type AccessGrantAuthorization } from "../access/index.js";
@@ -9,11 +9,25 @@ export declare class SessionSpawnDeniedError extends Error {
9
9
  readonly denial: SessionSpawnDenial;
10
10
  constructor(denial: SessionSpawnDenial);
11
11
  }
12
+ /**
13
+ * A session's effective first-party selection as a ceiling for narrowing:
14
+ * the stored selection (or the deployment default for a legacy null) under
15
+ * the deployment ceiling, with the resume counterpart of pause authority the
16
+ * runtime already grants to existing sessions.
17
+ */
18
+ export declare function effectiveFirstPartyMcpToolCeiling(stored: readonly FirstPartyMcpToolName[] | null | undefined, policy: {
19
+ default: readonly FirstPartyMcpToolName[];
20
+ allowed: readonly FirstPartyMcpToolName[];
21
+ }): Set<FirstPartyMcpToolName>;
12
22
  /**
13
23
  * Resolve per-session first-party tool visibility without consulting
14
24
  * authorization. Top-level omission snapshots the complete runtime default;
15
25
  * child omission snapshots the parent's exact effective selection. Explicit
16
- * [] is authoritative and must never widen.
26
+ * [] is authoritative and must never widen. An explicit child selection may
27
+ * only narrow the parent's effective selection: a session that was handed a
28
+ * reduced catalog cannot spawn a child that sees more than it does. A
29
+ * top-level explicit selection keeps only the deployment ceiling (checked by
30
+ * the caller) because there is no creator selection to narrow.
17
31
  */
18
32
  export declare function resolveFirstPartyMcpToolsForCreate(requested: FirstPartyMcpToolName[] | undefined, parentStored: FirstPartyMcpToolName[] | null | undefined, policy?: {
19
33
  default: readonly FirstPartyMcpToolName[];
@@ -133,6 +147,9 @@ export declare function createAndStartSessionWithOutcome(input: {
133
147
  policyRole?: string | null;
134
148
  firstPartyMcpPermissions?: Permission[] | null;
135
149
  firstPartyMcpTools: FirstPartyMcpToolName[];
150
+ agentAccess?: SessionAgentAccess;
151
+ endUser?: SessionEndUser | null;
152
+ memoryScope?: SessionMemoryScope;
136
153
  mcpServers?: CreateSessionMcpServerInput[];
137
154
  sessionMcpServers?: SessionMcpServerMetadata[];
138
155
  personalConnectionDelegations?: McpPersonalConnectionDelegation[];
@@ -284,6 +301,32 @@ export declare function resolveSessionCreateVisibility(input: {
284
301
  visibilityProvided: boolean;
285
302
  parentVisibility: "user_private" | "workspace_shared" | null;
286
303
  }): "user_private" | "workspace_shared";
304
+ export type SessionCreateScope = {
305
+ agentAccess: SessionAgentAccess;
306
+ endUser: SessionEndUser | null;
307
+ memoryScope: SessionMemoryScope;
308
+ };
309
+ /**
310
+ * Resolve a new session's agent-access scope, end-user label, and Memory
311
+ * selector (migration 0427). A top-level request takes its own values. An
312
+ * agent-created child inherits every omitted value from its trusted parent
313
+ * and may only NARROW an explicit one: agent access workspace > user >
314
+ * session, memory workspace > user > session > off, and the label must equal
315
+ * the parent's. Widening is a 403 because the parent's declared reach is a
316
+ * security boundary the child's own request cannot cross; a memory `user`
317
+ * selector without a label is a 422 in either position.
318
+ */
319
+ export declare function resolveSessionCreateScope(input: {
320
+ requested: {
321
+ agentAccess: SessionAgentAccess;
322
+ agentAccessProvided: boolean;
323
+ endUser: SessionEndUser | null;
324
+ endUserProvided: boolean;
325
+ memoryScope: SessionMemoryScope;
326
+ memoryScopeProvided: boolean;
327
+ };
328
+ parent: SessionCreateScope | null;
329
+ }): SessionCreateScope;
287
330
  export declare function createSessionForRequestWithOutcome(unresolvedDeps: ApiRouteDeps, grant: AccessGrant, workspaceId: string, rawPayload: unknown, authorization?: AccessGrantAuthorization, agentChildPresentation?: AgentChildSessionCreatePresentation): Promise<CreateSessionRequestOutcome>;
288
331
  /** @internal Fixed public projection; the committed session outcome remains authoritative. */
289
332
  export declare function reportSessionUsageRecordingFailure(_error: unknown): void;
package/dist/index.d.ts CHANGED
@@ -53,3 +53,4 @@ export * from "./application/user-resource-grants.js";
53
53
  export * from "./application/api-integration-servers.js";
54
54
  export * from "./editable-artifact-live.js";
55
55
  export * from "./editable-artifacts.js";
56
+ export { withSiteSessionOrigin } from "./site-session-origin.js";
package/dist/index.js CHANGED
@@ -209,6 +209,7 @@ function filenameForMimeType(mimeType) {
209
209
  }
210
210
 
211
211
  // src/model-catalog.ts
212
+ import { modelAllowedByConnections } from "@opengeni/db";
212
213
  import {
213
214
  applyModelCatalogDocument,
214
215
  configuredGatewayWorkspaceProductModelIds,
@@ -662,7 +663,7 @@ function resolveWorkspaceModelSelection(input) {
662
663
  const policyAllowed = evaluateWorkspaceModelPolicy(input.policy, {
663
664
  providerId: model.providerId,
664
665
  modelId: model.id
665
- }).allowed;
666
+ }).allowed && modelAllowedByConnections(input.connectionModelRestrictions ?? {}, model.id);
666
667
  const credentialReadiness = credentialReadinessFor({
667
668
  model,
668
669
  provider,
@@ -2430,6 +2431,35 @@ var SessionAuthorizationUnavailableError = class extends Error {
2430
2431
  this.name = "SessionAuthorizationUnavailableError";
2431
2432
  }
2432
2433
  };
2434
+ function sameEndUser(left, right) {
2435
+ return left !== null && right !== null && left.source === right.source && left.id === right.id;
2436
+ }
2437
+ function agentAccessPermitsCrossTreeAccess(caller, target) {
2438
+ if (caller.agentAccess === "session" || target.agentAccess === "session") return false;
2439
+ if (caller.agentAccess === "user" || target.agentAccess === "user") {
2440
+ return sameEndUser(caller.endUser, target.endUser);
2441
+ }
2442
+ return true;
2443
+ }
2444
+ function agentAccessListScopeForViewer(viewer, hostScope = null) {
2445
+ if (viewer.agentAccess === "session" && (!hostScope || hostScope.kind === "all")) {
2446
+ return {
2447
+ kind: "scoped",
2448
+ rootSessionIds: [viewer.callerRootSessionId],
2449
+ sessionIds: [],
2450
+ agentAccessViewer: viewer
2451
+ };
2452
+ }
2453
+ if (!hostScope || hostScope.kind === "all") {
2454
+ return { kind: "all", agentAccessViewer: viewer };
2455
+ }
2456
+ return {
2457
+ kind: "scoped",
2458
+ rootSessionIds: [...new Set(hostScope.rootSessionIds)],
2459
+ sessionIds: [...new Set(hostScope.sessionIds)],
2460
+ agentAccessViewer: viewer
2461
+ };
2462
+ }
2433
2463
  function grantHasAgentAttemptAuthority(grant) {
2434
2464
  const hasAgentAttemptClaim = grant.metadata?.["turnId"] !== void 0 || grant.metadata?.["attemptId"] !== void 0 || grant.metadata?.["executionGeneration"] !== void 0;
2435
2465
  return grant.principalKind ? grant.principalKind === "agent_attempt" : hasAgentAttemptClaim;
@@ -2486,6 +2516,15 @@ async function requireSessionAuthorization(deps, grant, input) {
2486
2516
  const allowed = actor.kind === "subject" ? actor.subjectId === slackAccess.owningSubjectId : actor.callerRootSessionId === target.rootSessionId;
2487
2517
  if (!allowed) throw new SessionAuthorizationDeniedError("forbidden");
2488
2518
  }
2519
+ if (actor.kind === "agent_attempt" && target.rootSessionId !== actor.callerRootSessionId) {
2520
+ const callerAccess = resolvedActor.callerAccess;
2521
+ if (!callerAccess || !agentAccessPermitsCrossTreeAccess(callerAccess, {
2522
+ agentAccess: authority.agentAccess,
2523
+ endUser: authority.endUser
2524
+ })) {
2525
+ throw new SessionAuthorizationDeniedError("forbidden");
2526
+ }
2527
+ }
2489
2528
  if (!port) {
2490
2529
  return {
2491
2530
  actor,
@@ -2525,8 +2564,13 @@ async function requireSessionAuthorizationListScope(deps, grant, surface) {
2525
2564
  const port = deps.sessionAuthorization;
2526
2565
  const isAgentAttempt = grantHasAgentAttemptAuthority(grant);
2527
2566
  if (!port && !isAgentAttempt) return null;
2528
- const { actor } = await resolveSessionAuthorizationActor(deps.db, grant);
2529
- if (!port) return null;
2567
+ const { actor, callerAccess } = await resolveSessionAuthorizationActor(deps.db, grant);
2568
+ const viewer = actor.kind === "agent_attempt" && callerAccess ? {
2569
+ callerRootSessionId: actor.callerRootSessionId,
2570
+ agentAccess: callerAccess.agentAccess,
2571
+ endUser: callerAccess.endUser
2572
+ } : null;
2573
+ if (!port) return viewer ? agentAccessListScopeForViewer(viewer) : null;
2530
2574
  let rawScope;
2531
2575
  try {
2532
2576
  rawScope = await port.resolveListScope({
@@ -2542,12 +2586,12 @@ async function requireSessionAuthorizationListScope(deps, grant, surface) {
2542
2586
  if (!parsed.success) {
2543
2587
  throw new SessionAuthorizationUnavailableError({ cause: parsed.error });
2544
2588
  }
2545
- if (parsed.data.kind === "all") return parsed.data;
2546
- return {
2589
+ const hostScope = parsed.data.kind === "all" ? { kind: "all" } : {
2547
2590
  kind: "scoped",
2548
2591
  rootSessionIds: [...new Set(parsed.data.rootSessionIds)],
2549
2592
  sessionIds: [...new Set(parsed.data.sessionIds)]
2550
2593
  };
2594
+ return viewer ? agentAccessListScopeForViewer(viewer, hostScope) : hostScope;
2551
2595
  }
2552
2596
  async function resolveSessionAuthorizationTarget(db, grant, sessionId) {
2553
2597
  const session = await getSession(db, grant.workspaceId, sessionId);
@@ -2572,7 +2616,8 @@ async function resolveSessionAuthorizationActor(db, grant) {
2572
2616
  subjectId: grant.subjectId,
2573
2617
  ...grant.subjectLabel ? { subjectLabel: grant.subjectLabel } : {}
2574
2618
  }),
2575
- callerParentSessionId: null
2619
+ callerParentSessionId: null,
2620
+ callerAccess: null
2576
2621
  };
2577
2622
  }
2578
2623
  if (typeof callerSessionId !== "string" || typeof turnId !== "string" || typeof attemptId !== "string" || typeof executionGeneration !== "number" || !Number.isSafeInteger(executionGeneration) || executionGeneration < 1) {
@@ -2598,7 +2643,8 @@ async function resolveSessionAuthorizationActor(db, grant) {
2598
2643
  initiatorContext: turn.initiatorContext,
2599
2644
  initiatingHumanSubjectId: turn.initiatingHumanSubjectId ?? (turn.initiator.kind === "subject" ? turn.initiator.subjectId : null)
2600
2645
  }),
2601
- callerParentSessionId: callerSession.parentSessionId
2646
+ callerParentSessionId: callerSession.parentSessionId,
2647
+ callerAccess: { agentAccess: callerSession.agentAccess, endUser: callerSession.endUser }
2602
2648
  };
2603
2649
  }
2604
2650
 
@@ -6885,7 +6931,7 @@ function withDefaultEnabledCapabilityMcpTools(tools, settings, runtimeSettings)
6885
6931
  return mergeToolRefs(tools, enabledCapabilityMcpToolRefs(settings, runtimeSettings));
6886
6932
  }
6887
6933
  function withWorkspaceDefaultMcpTools(tools, settings, runtimeSettings, defaults) {
6888
- if (!defaults) {
6934
+ if (!defaults?.mcpServerIds) {
6889
6935
  return withDefaultEnabledCapabilityMcpTools(tools, settings, runtimeSettings);
6890
6936
  }
6891
6937
  return mergeToolRefs(
@@ -8178,7 +8224,7 @@ async function workspaceSessionToolPolicyDefaultServerIds(db, workspaceId, setti
8178
8224
  const availableDefaults = defaultSessionMcpServerIds(runtimeSettings.mcpServers);
8179
8225
  const workspace = await requireWorkspace2(db, workspaceId);
8180
8226
  const configured = resolveWorkspaceSessionToolDefaults(workspace.settings);
8181
- if (!configured) return availableDefaults;
8227
+ if (!configured?.mcpServerIds) return availableDefaults;
8182
8228
  const available = new Set(availableDefaults);
8183
8229
  return sortedIds(configured.mcpServerIds.filter((id) => available.has(id)));
8184
8230
  }
@@ -8199,11 +8245,17 @@ function sessionWithEffectiveToolPolicy(session, workspaceServerIds, workspaceDe
8199
8245
  }
8200
8246
 
8201
8247
  // src/domain/scheduled-tasks.ts
8202
- import { resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy2 } from "@opengeni/config";
8248
+ import {
8249
+ allowedFirstPartyMcpToolsForSession,
8250
+ resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy2
8251
+ } from "@opengeni/config";
8203
8252
  import {
8204
8253
  DEFAULT_FIRST_PARTY_MCP_PERMISSIONS as DEFAULT_FIRST_PARTY_MCP_PERMISSIONS2,
8205
8254
  OPENGENI_SLACK_BOT_SESSION_METADATA_KEY as OPENGENI_SLACK_BOT_SESSION_METADATA_KEY3,
8206
- resolveWorkspaceSessionToolDefaults as resolveWorkspaceSessionToolDefaults3
8255
+ resolveWorkspaceSessionToolDefaults as resolveWorkspaceSessionToolDefaults3,
8256
+ SessionAgentAccess,
8257
+ SessionEndUser,
8258
+ SessionMemoryScope
8207
8259
  } from "@opengeni/contracts";
8208
8260
  import {
8209
8261
  createScheduledTask,
@@ -8233,6 +8285,20 @@ import { isDeepStrictEqual } from "util";
8233
8285
 
8234
8286
  // src/domain/sessions.ts
8235
8287
  import { CODEX_MODEL_ID_PREFIX, isCodexBilledModel } from "@opengeni/codex";
8288
+
8289
+ // src/site-session-origin.ts
8290
+ import { AsyncLocalStorage } from "async_hooks";
8291
+ var origins = new AsyncLocalStorage();
8292
+ function withSiteSessionOrigin(origin, run) {
8293
+ return origins.run(origin, run);
8294
+ }
8295
+ function sessionCreationMetadata(metadata) {
8296
+ const { _opengeniSiteOrigin: _ignored, ...rest } = metadata;
8297
+ const origin = origins.getStore();
8298
+ return origin ? { ...rest, _opengeniSiteOrigin: origin } : rest;
8299
+ }
8300
+
8301
+ // src/domain/sessions.ts
8236
8302
  import {
8237
8303
  canonicalizeConfiguredModelId,
8238
8304
  configuredAllowedModels,
@@ -8872,11 +8938,28 @@ var SessionSpawnDeniedError = class extends Error {
8872
8938
  this.denial = denial;
8873
8939
  }
8874
8940
  };
8941
+ function effectiveFirstPartyMcpToolCeiling(stored, policy) {
8942
+ const allowed = new Set(policy.allowed);
8943
+ const ceiling = new Set([...stored ?? policy.default].filter((tool) => allowed.has(tool)));
8944
+ if (ceiling.has("goal_pause") && allowed.has("goal_resume")) ceiling.add("goal_resume");
8945
+ return ceiling;
8946
+ }
8875
8947
  function resolveFirstPartyMcpToolsForCreate(requested, parentStored, policy = {
8876
8948
  default: DEFAULT_FIRST_PARTY_MCP_TOOLS,
8877
8949
  allowed: FIRST_PARTY_MCP_TOOL_NAMES2
8878
8950
  }) {
8879
- if (requested !== void 0) return [...requested];
8951
+ if (requested !== void 0) {
8952
+ if (parentStored !== void 0) {
8953
+ const parentCeiling = effectiveFirstPartyMcpToolCeiling(parentStored, policy);
8954
+ const widened = requested.find((tool) => !parentCeiling.has(tool));
8955
+ if (widened) {
8956
+ throw new HTTPException12(403, {
8957
+ message: `child first-party MCP tools may only narrow the parent session selection: ${widened}`
8958
+ });
8959
+ }
8960
+ }
8961
+ return [...requested];
8962
+ }
8880
8963
  const allowed = new Set(policy.allowed);
8881
8964
  const inherited = parentStored === void 0 ? policy.default : parentStored ?? policy.default;
8882
8965
  return [...inherited].filter((tool) => allowed.has(tool));
@@ -9299,6 +9382,9 @@ async function createAndStartSessionWithOutcome(input) {
9299
9382
  firstPartyMcpTools: input.firstPartyMcpTools,
9300
9383
  instructions: input.instructions ?? null,
9301
9384
  policyRole: input.policyRole ?? null,
9385
+ ...input.agentAccess ? { agentAccess: input.agentAccess } : {},
9386
+ ...input.endUser !== void 0 ? { endUser: input.endUser } : {},
9387
+ ...input.memoryScope ? { memoryScope: input.memoryScope } : {},
9302
9388
  parentSessionId: input.parentSessionId ?? null,
9303
9389
  createIdempotencyKey: input.createIdempotencyKey,
9304
9390
  selectedInstalledSkillIds: input.selectedInstalledSkillIds ?? [],
@@ -9380,6 +9466,9 @@ async function createAndStartSessionWithOutcome(input) {
9380
9466
  firstPartyMcpTools: input.firstPartyMcpTools,
9381
9467
  instructions: input.instructions ?? null,
9382
9468
  policyRole: input.policyRole ?? null,
9469
+ ...input.agentAccess ? { agentAccess: input.agentAccess } : {},
9470
+ ...input.endUser !== void 0 ? { endUser: input.endUser } : {},
9471
+ ...input.memoryScope ? { memoryScope: input.memoryScope } : {},
9383
9472
  parentSessionId: input.parentSessionId ?? null,
9384
9473
  sandboxGroupId: input.sandboxGroupId ?? null,
9385
9474
  ...input.sandboxOs ? { sandboxOs: input.sandboxOs } : {},
@@ -9665,7 +9754,7 @@ async function postUserMessageTurn(input) {
9665
9754
  const requestedReasoningEffort = input.reasoningEffort ?? null;
9666
9755
  assertConfiguredModel(settings, requestedModel);
9667
9756
  const sessionForModelGate = await requireSession2(db, workspaceId, sessionId);
9668
- const effectiveModelForGate = requestedModel ?? sessionForModelGate.model;
9757
+ const effectiveModelForGate = input.turnExecutionPolicy?.productModelId ?? requestedModel ?? sessionForModelGate.model;
9669
9758
  const freshWorkspaceCustomModel = requestedModel !== null && isWorkspaceCustomModelId(settings, requestedModel) && requestedModel !== sessionForModelGate.model ? requestedModel : null;
9670
9759
  await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, effectiveModelForGate);
9671
9760
  try {
@@ -9797,6 +9886,54 @@ function resolveSessionCreateVisibility(input) {
9797
9886
  }
9798
9887
  return input.requestedVisibility === "private" ? "user_private" : "workspace_shared";
9799
9888
  }
9889
+ var AGENT_ACCESS_WIDTH = {
9890
+ session: 0,
9891
+ user: 1,
9892
+ workspace: 2
9893
+ };
9894
+ var MEMORY_SCOPE_WIDTH = {
9895
+ off: 0,
9896
+ session: 1,
9897
+ user: 2,
9898
+ workspace: 3
9899
+ };
9900
+ function resolveSessionCreateScope(input) {
9901
+ const { requested, parent } = input;
9902
+ let resolved;
9903
+ if (!parent) {
9904
+ resolved = {
9905
+ agentAccess: requested.agentAccess,
9906
+ endUser: requested.endUser,
9907
+ memoryScope: requested.memoryScope
9908
+ };
9909
+ } else {
9910
+ const agentAccess = requested.agentAccessProvided ? requested.agentAccess : parent.agentAccess;
9911
+ if (AGENT_ACCESS_WIDTH[agentAccess] > AGENT_ACCESS_WIDTH[parent.agentAccess]) {
9912
+ throw new HTTPException12(403, {
9913
+ message: "child agent access may only narrow the parent session"
9914
+ });
9915
+ }
9916
+ if (requested.endUserProvided) {
9917
+ const same = requested.endUser !== null && parent.endUser !== null && requested.endUser.source === parent.endUser.source && requested.endUser.id === parent.endUser.id;
9918
+ if (!same) {
9919
+ throw new HTTPException12(403, {
9920
+ message: "child end-user label must equal the parent session label"
9921
+ });
9922
+ }
9923
+ }
9924
+ const memoryScope = requested.memoryScopeProvided ? requested.memoryScope : parent.memoryScope;
9925
+ if (MEMORY_SCOPE_WIDTH[memoryScope] > MEMORY_SCOPE_WIDTH[parent.memoryScope]) {
9926
+ throw new HTTPException12(403, {
9927
+ message: "child memory scope may only narrow the parent session"
9928
+ });
9929
+ }
9930
+ resolved = { agentAccess, endUser: parent.endUser, memoryScope };
9931
+ }
9932
+ if (resolved.memoryScope === "user" && resolved.endUser === null) {
9933
+ throw new HTTPException12(422, { message: 'memoryScope "user" requires an endUser label' });
9934
+ }
9935
+ return resolved;
9936
+ }
9800
9937
  async function resolveWorkspaceModelBoundarySettings(deps, grant, workspaceId, modelIds, retainedProductModelId) {
9801
9938
  const retainedCatalogModel = isCatalogOverlayModel(retainedProductModelId);
9802
9939
  if (deps.catalogSourceSettings) {
@@ -9838,6 +9975,7 @@ async function withSessionCreateUsageRecording(input) {
9838
9975
  }
9839
9976
  async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspaceId, rawPayload, authorization, agentChildPresentation) {
9840
9977
  const payload = CreateSessionRequest.parse(rawPayload);
9978
+ payload.metadata = sessionCreationMetadata(payload.metadata);
9841
9979
  if (hasReservedOpenGeniSlackBotSessionMetadata(payload.metadata)) {
9842
9980
  throw new HTTPException12(422, {
9843
9981
  message: `${OPENGENI_SLACK_BOT_SESSION_METADATA_KEY2} is reserved for scheduler routing`
@@ -9899,6 +10037,21 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
9899
10037
  message: error instanceof Error ? error.message : "invalid child visibility"
9900
10038
  });
9901
10039
  }
10040
+ const sessionScope = resolveSessionCreateScope({
10041
+ requested: {
10042
+ agentAccess: payload.agentAccess,
10043
+ agentAccessProvided: hasOwnProperty(rawPayload, "agentAccess"),
10044
+ endUser: payload.endUser ?? null,
10045
+ endUserProvided: hasOwnProperty(rawPayload, "endUser"),
10046
+ memoryScope: payload.memoryScope,
10047
+ memoryScopeProvided: hasOwnProperty(rawPayload, "memoryScope")
10048
+ },
10049
+ parent: parentAuthority ? {
10050
+ agentAccess: parentAuthority.agentAccess,
10051
+ endUser: parentAuthority.endUser,
10052
+ memoryScope: parentAuthority.memoryScope
10053
+ } : null
10054
+ });
9902
10055
  const creationInitiator = creationInitiatorForGrant(grant);
9903
10056
  const parentCallingTurn = parentSession && creationInitiator.actor ? await getSessionTurnForAttempt2(
9904
10057
  db,
@@ -10276,7 +10429,7 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10276
10429
  message: `first-party MCP tool is disabled by deployment policy: ${disallowedFirstPartyMcpTool}`
10277
10430
  });
10278
10431
  }
10279
- const workspaceFirstPartyDefaults = workspaceSessionToolDefaults?.firstPartyMcpTools.filter(
10432
+ const workspaceFirstPartyDefaults = workspaceSessionToolDefaults?.firstPartyMcpTools?.filter(
10280
10433
  (tool) => deploymentFirstPartyMcpToolPolicy.allowed.includes(tool)
10281
10434
  );
10282
10435
  const firstPartyMcpTools = resolveFirstPartyMcpToolsForCreate(
@@ -10502,6 +10655,9 @@ async function createSessionForRequestWithOutcome(unresolvedDeps, grant, workspa
10502
10655
  // time. Not surfaced as an event.
10503
10656
  instructions: payload.instructions ?? null,
10504
10657
  policyRole: payload.policyRole ?? null,
10658
+ agentAccess: sessionScope.agentAccess,
10659
+ endUser: sessionScope.endUser,
10660
+ memoryScope: sessionScope.memoryScope,
10505
10661
  firstPartyMcpPermissions,
10506
10662
  firstPartyMcpTools,
10507
10663
  mcpServers: sessionMcpServers.dbServers,
@@ -10995,6 +11151,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
10995
11151
  surface: "core"
10996
11152
  });
10997
11153
  requirePermission(grant, "sessions:control");
11154
+ const agentAttemptCaller = grantHasAgentAttemptAuthority(grant);
10998
11155
  const existingSession = await requireSession2(deps.db, grant.workspaceId, sessionId);
10999
11156
  const workspace = await requireWorkspace3(deps.db, grant.workspaceId);
11000
11157
  const workspaceSessionToolDefaults = resolveWorkspaceSessionToolDefaults2(workspace.settings);
@@ -11043,7 +11200,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
11043
11200
  runtimeSettings
11044
11201
  );
11045
11202
  const workspaceDefaultFirstPartyTools = [
11046
- ...workspaceSessionToolDefaults?.firstPartyMcpTools.filter(
11203
+ ...workspaceSessionToolDefaults?.firstPartyMcpTools?.filter(
11047
11204
  (tool) => deploymentFirstPartyMcpToolPolicy.allowed.includes(tool)
11048
11205
  ) ?? deploymentFirstPartyMcpToolPolicy.default
11049
11206
  ];
@@ -11120,6 +11277,35 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
11120
11277
  nextTools = requestedMode === "workspace_default" ? workspaceDefaultTools : explicitRequestedTools;
11121
11278
  nextFirstPartyMcpTools = requestedMode === "workspace_default" ? workspaceDefaultFirstPartyTools : explicitRequestedFirstPartyTools;
11122
11279
  nextPolicy = { mode: requestedMode, inheritedFromSessionId: null };
11280
+ if (agentAttemptCaller) {
11281
+ const sessionTracksWorkspaceDefaults = session.toolPolicy?.mode === "workspace_default";
11282
+ const currentEffectiveTools = withFirstPartyTools(
11283
+ sessionTracksWorkspaceDefaults ? withWorkspaceDefaultMcpTools(
11284
+ availableToolRefs(session.tools, runtimeSettings),
11285
+ deps.settings,
11286
+ runtimeSettings,
11287
+ workspaceSessionToolDefaults
11288
+ ) : session.tools,
11289
+ runtimeSettings
11290
+ );
11291
+ assertToolRefsSubset(
11292
+ nextTools,
11293
+ currentEffectiveTools,
11294
+ "an agent may only narrow its session tool policy"
11295
+ );
11296
+ const currentFirstPartyCeiling = effectiveFirstPartyMcpToolCeiling(
11297
+ session.firstPartyMcpTools,
11298
+ deploymentFirstPartyMcpToolPolicy
11299
+ );
11300
+ const widenedFirstPartyTool = nextFirstPartyMcpTools.find(
11301
+ (tool) => !currentFirstPartyCeiling.has(tool)
11302
+ );
11303
+ if (widenedFirstPartyTool) {
11304
+ throw new HTTPException12(403, {
11305
+ message: `an agent may only narrow its session OpenGeni tools: ${widenedFirstPartyTool}`
11306
+ });
11307
+ }
11308
+ }
11123
11309
  }
11124
11310
  const currentPolicy = session.toolPolicy;
11125
11311
  const unchanged = stableJson4({
@@ -11312,6 +11498,12 @@ async function createValidatedScheduledTask(input) {
11312
11498
  ...scheduledConnectionSurfaceEligibility(runtimeSettings, target)
11313
11499
  });
11314
11500
  const creationInitiator = creationInitiatorForGrant(input.grant);
11501
+ const creatorPolicy = creationInitiator.actor ? await frozenScheduledTaskCreatorPolicy({
11502
+ db: input.db,
11503
+ settings: input.settings,
11504
+ grant: input.grant,
11505
+ sessionId: creationInitiator.actor.sessionId
11506
+ }) : null;
11315
11507
  const xaiProviderAccountAuthoritySnapshot = creationInitiator.actor ? await getSessionTurnXaiProviderAccountAuthoritySnapshot2(
11316
11508
  input.db,
11317
11509
  input.grant.workspaceId,
@@ -11345,6 +11537,7 @@ async function createValidatedScheduledTask(input) {
11345
11537
  createdByActor: creationInitiator.actor ?? null,
11346
11538
  personalConnectionDelegations,
11347
11539
  xaiProviderAccountAuthoritySnapshot,
11540
+ creatorPolicy,
11348
11541
  targetSessionId: target?.id ?? null,
11349
11542
  variableSetId: input.payload.variableSetId ?? null,
11350
11543
  rigId: input.payload.rigId ?? null,
@@ -11353,6 +11546,37 @@ async function createValidatedScheduledTask(input) {
11353
11546
  })
11354
11547
  );
11355
11548
  }
11549
+ async function frozenScheduledTaskCreatorPolicy(input) {
11550
+ const session = await getSession3(input.db, input.grant.workspaceId, input.sessionId);
11551
+ if (!session) {
11552
+ throw new HTTPException13(403, {
11553
+ message: "the calling agent session is not available in this workspace"
11554
+ });
11555
+ }
11556
+ const firstPartyMcpTools = allowedFirstPartyMcpToolsForSession(
11557
+ input.settings,
11558
+ session.firstPartyMcpTools
11559
+ );
11560
+ const firstPartyMcpPermissions = (session.firstPartyMcpPermissions ?? [...DEFAULT_FIRST_PARTY_MCP_PERMISSIONS2]).filter((permission) => hasPermission(input.grant.permissions, permission));
11561
+ if (firstPartyMcpPermissions.length === 0) {
11562
+ throw new HTTPException13(403, {
11563
+ message: "the calling agent session holds no first-party MCP permission it could delegate to scheduled runs"
11564
+ });
11565
+ }
11566
+ const projection = session;
11567
+ const agentAccess = SessionAgentAccess.safeParse(projection["agentAccess"]);
11568
+ const endUser = SessionEndUser.safeParse(projection["endUser"]);
11569
+ const memoryScope = SessionMemoryScope.safeParse(projection["memoryScope"]);
11570
+ return {
11571
+ firstPartyMcpTools,
11572
+ firstPartyMcpPermissions,
11573
+ sessionPolicy: {
11574
+ agentAccess: agentAccess.success ? agentAccess.data : null,
11575
+ endUser: endUser.success ? { source: endUser.data.source, id: endUser.data.id } : null,
11576
+ memoryScope: memoryScope.success ? memoryScope.data : null
11577
+ }
11578
+ };
11579
+ }
11356
11580
  function nestedPostgresMessage(error) {
11357
11581
  const queue = [error];
11358
11582
  const seen = /* @__PURE__ */ new Set();
@@ -16426,6 +16650,8 @@ export {
16426
16650
  accessGrantAuthorizationFromContext,
16427
16651
  accountScopedApiKeyWorkspaceAuthority,
16428
16652
  activateRigVersionForApi,
16653
+ agentAccessListScopeForViewer,
16654
+ agentAccessPermitsCrossTreeAccess,
16429
16655
  apiIntegrationsMatchingDelegations,
16430
16656
  appendRigSetupCommand,
16431
16657
  applyCapabilityEnablement,
@@ -16544,6 +16770,7 @@ export {
16544
16770
  editableArtifactStateHash,
16545
16771
  editableArtifactStorePortFromPostgres,
16546
16772
  editableArtifactTransactionId,
16773
+ effectiveFirstPartyMcpToolCeiling,
16547
16774
  enableCapability,
16548
16775
  enabledCapabilityMcpToolRefs,
16549
16776
  encodeEditableArtifactBrokerHint,
@@ -16574,6 +16801,7 @@ export {
16574
16801
  governedLearningEvidenceIsSlackDerived,
16575
16802
  governedLearningSlackImportance,
16576
16803
  governedLearningSlackProjection,
16804
+ grantHasAgentAttemptAuthority,
16577
16805
  hasLiteralPermission,
16578
16806
  hasPermission,
16579
16807
  hasReservedFikenMetadata,
@@ -16715,6 +16943,7 @@ export {
16715
16943
  resolveMemberSubjectId,
16716
16944
  resolveRigProviderImageForRun,
16717
16945
  resolveRigProviderImageSelection,
16946
+ resolveSessionCreateScope,
16718
16947
  resolveSessionCreateVisibility,
16719
16948
  resolveSessionSandboxRuntime,
16720
16949
  resolveSessionToolPolicy,
@@ -16807,6 +17036,7 @@ export {
16807
17036
  withFrozenPersonalConnectionDelegations,
16808
17037
  withResolvedSessionAuthorization,
16809
17038
  withScheduledTaskAuthorityWriteErrors,
17039
+ withSiteSessionOrigin,
16810
17040
  withWorkspaceDefaultMcpTools,
16811
17041
  workflowIdForSession,
16812
17042
  workspaceCustomModelReference,