@opengeni/core 0.12.7 → 0.12.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { Settings } from '@opengeni/config';
2
2
  import { ScheduledTask, TurnInitiator, Document, GitHubAppApiPort, ConnectionCredentialsPort, SessionAuthorizationPort, Permission, AccessContext, AccessGrant, SessionAuthorizationActor, SessionAuthorizationTarget, SessionAuthorizationOperation, SessionAuthorizationSurface, SessionAuthorizationListScope, LimitAction, LimitDecision, TurnInitiatorContext, SessionTurnSource, CapabilityCatalogItem, CapabilityInstallation, CapabilityCatalogResponse, CreateCapabilityCatalogItemRequest, EnableCapabilityRequest, VariableSet, Rig, RigVersion, CreateRigRequest, RigDefinitionEditPayload, RigChange, ProposeRigChangeRequest, UpdateRigRequest, CapabilityPack, SocialConnection, ScheduledTaskAgentConfig, ToolRef, ResourceRef, SessionEffectiveToolPolicy, SessionToolPolicy, Session, CreateScheduledTaskRequest, UpdateScheduledTaskRequest, SessionSpawnDenial, ReasoningEffort, SessionMcpCredentialUpdateInput, SessionEvent, SessionTurn, SessionSkill, TurnExecutionPolicyV1, GoalSpec, FirstPartyMcpToolName, SessionMcpServerMetadata, CreateSessionResponse, SessionMcpApprovalPolicy, UpdateSessionMcpApprovalPolicyResponse, UpdateSessionToolPolicyRequest, ConnectionMetadata, OpenGeniSlackBotConnectionMetadata, WorkspaceMember, NewSessionDraft, SessionControlRequest, SessionControlResponse, WorkspaceInferenceControlRequest, WorkspaceInferenceControlResponse, DeleteSessionQueueItemRequest, SessionQueueMutationResponse, EditSessionQueueItemRequest, ComposerDraft, MoveSessionQueueItemRequest, SaveComposerDraftRequest, SteerSessionQueueItemRequest } from '@opengeni/contracts';
3
3
  export { mergeToolRefs, stableJson } from '@opengeni/contracts';
4
4
  import * as _opengeni_db from '@opengeni/db';
5
- import { Database, SandboxRecord, EnabledMcpCapabilityServer, UpdateScheduledTaskInput, SessionCommandActor, CreateSessionMcpServerInput, UpdateSessionMcpServerCredentialsInput, ConnectionMetadataWithVerification } from '@opengeni/db';
5
+ import { Database, SandboxRecord, EnabledMcpCapabilityServer, UpdateScheduledTaskInput, SessionCommandActor, CreateSessionMcpServerInput, UpdateSessionMcpServerCredentialsInput, ConnectionMetadataWithVerification, SessionCommandReceiptRow } from '@opengeni/db';
6
6
  import { DocumentServices } from '@opengeni/documents';
7
7
  import { EventBus } from '@opengeni/events';
8
8
  import { Observability } from '@opengeni/observability';
@@ -699,11 +699,8 @@ type ResolvedSessionToolPolicy = {
699
699
  effectivePolicy: SessionEffectiveToolPolicy;
700
700
  };
701
701
  type SessionToolPolicyInput = {
702
- toolPolicy?: SessionToolPolicy | null;
702
+ toolPolicy: SessionToolPolicy;
703
703
  sessionTools: ToolRef[];
704
- turnTools?: ToolRef[];
705
- /** Undefined preserves the legacy merge path for pre-provenance callers. */
706
- turnToolsProvided?: boolean;
707
704
  availableMcpServerIds: Iterable<string>;
708
705
  /** Current omitted-tools defaults, intentionally narrower than all servers. */
709
706
  defaultMcpServerIds?: Iterable<string>;
@@ -809,7 +806,7 @@ declare class SessionSpawnDeniedError extends Error {
809
806
  * as null); child omission snapshots the parent's exact effective selection.
810
807
  * Explicit [] is authoritative and must never widen.
811
808
  */
812
- declare function resolveFirstPartyMcpToolsForCreate(requested: FirstPartyMcpToolName[] | undefined, parentStored: FirstPartyMcpToolName[] | null | undefined): FirstPartyMcpToolName[] | null;
809
+ declare function resolveFirstPartyMcpToolsForCreate(requested: FirstPartyMcpToolName[] | undefined, parentStored: FirstPartyMcpToolName[] | null | undefined): FirstPartyMcpToolName[];
813
810
  declare function sessionSpawnDenialEnvelope(error: SessionSpawnDeniedError): {
814
811
  readonly error: {
815
812
  readonly code: "nested_agent_depth_exceeded" | "nested_agent_depth_override_forbidden";
@@ -848,7 +845,7 @@ declare function createAndStartSession(input: {
848
845
  resources: ResourceRef[];
849
846
  skills?: SessionSkill[];
850
847
  tools: ToolRef[];
851
- toolPolicy?: SessionToolPolicy;
848
+ toolPolicy: SessionToolPolicy;
852
849
  clientEventId?: string;
853
850
  model: string;
854
851
  reasoningEffort: Settings["openaiReasoningEffort"];
@@ -869,7 +866,7 @@ declare function createAndStartSession(input: {
869
866
  goal?: GoalSpec | null;
870
867
  instructions?: string | null;
871
868
  firstPartyMcpPermissions?: Permission[] | null;
872
- firstPartyMcpTools?: FirstPartyMcpToolName[] | null;
869
+ firstPartyMcpTools: FirstPartyMcpToolName[];
873
870
  mcpServers?: CreateSessionMcpServerInput[];
874
871
  sessionMcpServers?: SessionMcpServerMetadata[];
875
872
  parentSessionId?: string | null;
@@ -940,8 +937,6 @@ declare function postUserMessageTurn(input: {
940
937
  text: string;
941
938
  turnInstructions?: string | null;
942
939
  resources: ResourceRef[];
943
- tools: ToolRef[];
944
- toolsProvided: boolean;
945
940
  model?: string | null;
946
941
  reasoningEffort?: Settings["openaiReasoningEffort"] | null;
947
942
  clientEventId?: string;
@@ -974,15 +969,12 @@ declare function createSessionForRequest(deps: ApiRouteDeps, grant: AccessGrant,
974
969
  * `POST /sessions/:id/events` and the first-party MCP `session_send_message`
975
970
  * tool: resource/tool validation, usage limits, the locked append + turn
976
971
  * enqueue, and usage recording. `toolsProvided: false` durably preserves an
977
- * absent `tools` key so execution inherits the session policy; an explicit
978
- * empty array is a deliberate per-turn narrowing.
972
+ * Tool selection is durable session state and never rides a follow-up prompt.
979
973
  */
980
974
  declare function acceptSessionUserMessage(deps: AcceptSessionUserMessageDependencies, grant: AccessGrant, workspaceId: string, sessionId: string, input: {
981
975
  text: string;
982
976
  turnInstructions?: string | null;
983
977
  resources?: ResourceRef[];
984
- tools?: ToolRef[];
985
- toolsProvided: boolean;
986
978
  model?: string | null;
987
979
  reasoningEffort?: ReasoningEffort | null;
988
980
  clientEventId?: string;
@@ -1107,6 +1099,8 @@ type HumanSessionCommandContext = {
1107
1099
  workspaceId: string;
1108
1100
  sessionId: string;
1109
1101
  subjectId: string;
1102
+ /** See AgentSessionCommandContext.authorizationSurface. */
1103
+ authorizationSurface?: SessionAuthorizationSurface;
1110
1104
  };
1111
1105
  type AgentSessionCommandContext = {
1112
1106
  accountId: string;
@@ -1116,6 +1110,13 @@ type AgentSessionCommandContext = {
1116
1110
  callerTurnId: string;
1117
1111
  callerAttemptId: string;
1118
1112
  callerExecutionGeneration: number;
1113
+ /**
1114
+ * The trusted adapter surface that owns this command's one authorization
1115
+ * decision. Direct core callers omit it and retain the canonical `core`
1116
+ * surface; adapters that delegate the complete command set it explicitly so
1117
+ * they do not authorize once at the edge and then repeat the host call here.
1118
+ */
1119
+ authorizationSurface?: SessionAuthorizationSurface;
1119
1120
  };
1120
1121
  type SessionAuthorizationCommandDeps = {
1121
1122
  db: Database;
@@ -1151,7 +1152,16 @@ declare function controlAgentSessionWorkstream(deps: {
1151
1152
  action: "pause" | "resume";
1152
1153
  idempotencyKey: string;
1153
1154
  reason?: string | null;
1154
- }): Promise<_opengeni_db.SessionControlMutationResult>;
1155
+ }): Promise<{
1156
+ authorization: ResolvedSessionAuthorization | null;
1157
+ receipt: SessionCommandReceiptRow;
1158
+ control: _opengeni_db.EffectiveSessionControl;
1159
+ sessionControlEventId: string;
1160
+ workspaceControlEventId: string;
1161
+ interruptionCount: number;
1162
+ wakeCount: number;
1163
+ replay: boolean;
1164
+ }>;
1155
1165
  declare function moveHumanQueuePrompt(deps: {
1156
1166
  db: Database;
1157
1167
  bus: EventBus;
package/dist/index.js CHANGED
@@ -3251,15 +3251,15 @@ function projectIds(ids) {
3251
3251
  };
3252
3252
  }
3253
3253
  function resolveSessionToolPolicy(input) {
3254
- const policy = input.toolPolicy ?? { mode: "legacy", inheritedFromSessionId: null };
3254
+ const policy = input.toolPolicy;
3255
3255
  const availableIds = new Set(input.availableMcpServerIds);
3256
3256
  const defaultIds = new Set(input.defaultMcpServerIds ?? []);
3257
3257
  const mandatoryIds = MANDATORY_SESSION_MCP_SERVER_IDS.filter(
3258
3258
  (id) => availableIds.has(id)
3259
3259
  );
3260
3260
  const mandatoryIdSet = new Set(mandatoryIds);
3261
- const selectedRefs = input.turnToolsProvided === true ? mergeToolRefs2([], input.turnTools ?? []) : input.turnToolsProvided === false ? mergeToolRefs2([], input.sessionTools) : mergeToolRefs2(input.sessionTools, input.turnTools ?? []);
3262
- const tracksWorkspaceDefaults = policy.mode === "workspace_default" && input.turnToolsProvided !== true;
3261
+ const selectedRefs = mergeToolRefs2([], input.sessionTools);
3262
+ const tracksWorkspaceDefaults = policy.mode === "workspace_default";
3263
3263
  let toolRefs = selectedRefs.filter((tool) => tool.optional !== true || availableIds.has(tool.id));
3264
3264
  if (tracksWorkspaceDefaults) {
3265
3265
  toolRefs = mergeToolRefs2(
@@ -3345,7 +3345,7 @@ function sessionWithEffectiveToolPolicy(session, workspaceServerIds, workspaceDe
3345
3345
  return {
3346
3346
  ...session,
3347
3347
  effectiveToolPolicy: resolveSessionToolPolicy({
3348
- ...session.toolPolicy ? { toolPolicy: session.toolPolicy } : {},
3348
+ toolPolicy: session.toolPolicy,
3349
3349
  sessionTools: session.tools,
3350
3350
  availableMcpServerIds: availableIds,
3351
3351
  defaultMcpServerIds: workspaceDefaultServerIds
@@ -3378,6 +3378,7 @@ import {
3378
3378
  CreateSessionRequest,
3379
3379
  DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
3380
3380
  DEFAULT_FIRST_PARTY_MCP_TOOLS,
3381
+ FIRST_PARTY_MCP_TOOL_NAMES,
3381
3382
  OPENGENI_SLACK_BOT_SESSION_METADATA_KEY as OPENGENI_SLACK_BOT_SESSION_METADATA_KEY2,
3382
3383
  SessionSpawnDenial,
3383
3384
  ServiceTurnInitiator,
@@ -3502,7 +3503,7 @@ var SessionSpawnDeniedError = class extends Error {
3502
3503
  };
3503
3504
  function resolveFirstPartyMcpToolsForCreate(requested, parentStored) {
3504
3505
  if (requested !== void 0) return [...requested];
3505
- if (parentStored === void 0) return null;
3506
+ if (parentStored === void 0) return [...DEFAULT_FIRST_PARTY_MCP_TOOLS];
3506
3507
  return [...parentStored ?? DEFAULT_FIRST_PARTY_MCP_TOOLS];
3507
3508
  }
3508
3509
  function sessionSpawnDeniedMessage(denial) {
@@ -3798,7 +3799,7 @@ async function createAndStartSession(input) {
3798
3799
  resources: input.resources,
3799
3800
  skills: input.skills ?? [],
3800
3801
  tools: input.tools,
3801
- ...input.toolPolicy ? { toolPolicy: input.toolPolicy } : {},
3802
+ toolPolicy: input.toolPolicy,
3802
3803
  metadata: sessionMetadata,
3803
3804
  ...input.createdBy ? { createdBy: input.createdBy } : {},
3804
3805
  ...input.createdByContext ? { createdByContext: input.createdByContext } : {},
@@ -3809,7 +3810,7 @@ async function createAndStartSession(input) {
3809
3810
  rigId: input.rigId ?? null,
3810
3811
  rigVersionId: input.rigVersionId ?? null,
3811
3812
  firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
3812
- firstPartyMcpTools: input.firstPartyMcpTools ?? null,
3813
+ firstPartyMcpTools: input.firstPartyMcpTools,
3813
3814
  instructions: input.instructions ?? null,
3814
3815
  parentSessionId: input.parentSessionId ?? null,
3815
3816
  createIdempotencyKey: input.createIdempotencyKey,
@@ -3843,7 +3844,7 @@ async function createAndStartSession(input) {
3843
3844
  resources: input.resources,
3844
3845
  skills: input.skills ?? [],
3845
3846
  tools: input.tools,
3846
- ...input.toolPolicy ? { toolPolicy: input.toolPolicy } : {},
3847
+ toolPolicy: input.toolPolicy,
3847
3848
  metadata: sessionMetadata,
3848
3849
  ...input.createdBy ? { createdBy: input.createdBy } : {},
3849
3850
  ...input.createdByContext ? { createdByContext: input.createdByContext } : {},
@@ -3854,7 +3855,7 @@ async function createAndStartSession(input) {
3854
3855
  rigId: input.rigId ?? null,
3855
3856
  rigVersionId: input.rigVersionId ?? null,
3856
3857
  firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
3857
- firstPartyMcpTools: input.firstPartyMcpTools ?? null,
3858
+ firstPartyMcpTools: input.firstPartyMcpTools,
3858
3859
  instructions: input.instructions ?? null,
3859
3860
  parentSessionId: input.parentSessionId ?? null,
3860
3861
  sandboxGroupId: input.sandboxGroupId ?? null,
@@ -3908,7 +3909,7 @@ async function finishStartSession(input, session) {
3908
3909
  reasoningEffortFallback: input.reasoningEffort,
3909
3910
  turnExecutionPolicy: input.turnExecutionPolicy,
3910
3911
  createdEventPayload: {
3911
- ...input.toolPolicy ? { toolPolicy: input.toolPolicy } : {},
3912
+ toolPolicy: input.toolPolicy,
3912
3913
  ...input.variableSet ? { variableSetId: input.variableSet.id, variableSetName: input.variableSet.name } : {},
3913
3914
  ...input.sessionMcpServers?.length ? { mcpServers: input.sessionMcpServers } : {}
3914
3915
  },
@@ -4021,8 +4022,6 @@ async function postUserMessageTurn(input) {
4021
4022
  text: input.text,
4022
4023
  turnInstructions: input.turnInstructions ?? null,
4023
4024
  resources: input.resources,
4024
- tools: input.tools,
4025
- toolsProvided: input.toolsProvided,
4026
4025
  model: requestedModel,
4027
4026
  reasoningEffort: requestedReasoningEffort,
4028
4027
  reasoningEffortFallback: input.reasoningEffortFallback ?? settings.openaiReasoningEffort,
@@ -4255,9 +4254,8 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
4255
4254
  parentSession ? parentSession.firstPartyMcpTools : void 0
4256
4255
  );
4257
4256
  if (payload.goal) {
4258
- const effectiveTools = firstPartyMcpTools ?? [...DEFAULT_FIRST_PARTY_MCP_TOOLS];
4259
4257
  const missingGoalTools = ["goal_update", "goal_complete", "goal_pause"].filter(
4260
- (name) => !effectiveTools.includes(name)
4258
+ (name) => !firstPartyMcpTools.includes(name)
4261
4259
  );
4262
4260
  if (missingGoalTools.length > 0) {
4263
4261
  throw new HTTPException10(422, {
@@ -4458,22 +4456,12 @@ async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
4458
4456
  return session;
4459
4457
  }
4460
4458
  async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, input) {
4461
- if (input.toolsProvided && !deps.settings.sessionTurnToolReplacementEnabled) {
4462
- throw new HTTPException10(503, {
4463
- message: "explicit follow-up tool replacement is temporarily unavailable until provenance-aware turn workers finish rolling out; omit tools to inherit the session policy and retry"
4464
- });
4465
- }
4466
4459
  const { settings, db, bus, workflowClient, objectStorage } = deps;
4467
4460
  await requireSessionAuthorization(deps, grant, {
4468
4461
  sessionId,
4469
4462
  operation: input.delivery === "steer" ? "session.steer" : "session.append",
4470
4463
  surface: "core"
4471
4464
  });
4472
- const capabilityRuntimeSettings = await settingsWithEnabledCapabilityMcpServers(
4473
- db,
4474
- workspaceId,
4475
- settings
4476
- );
4477
4465
  const existingSession = await requireSession2(db, workspaceId, sessionId);
4478
4466
  const requestedModel = canonicalConfiguredModel(settings, input.model ?? null) ?? null;
4479
4467
  const effectiveModel = canonicalConfiguredModel(settings, requestedModel ?? existingSession.model) ?? null;
@@ -4492,27 +4480,7 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
4492
4480
  reasoningEffort: effectiveReasoningEffort,
4493
4481
  reasoningSource: input.reasoningEffort == null ? "session" : "explicit"
4494
4482
  });
4495
- const runtimeSettings = settingsWithSessionMcpServerMetadata(
4496
- capabilityRuntimeSettings,
4497
- existingSession.mcpServers
4498
- );
4499
4483
  const requestedResources = normalizeResources(input.resources ?? []);
4500
- const tracksWorkspaceDefaults = existingSession.toolPolicy?.mode === "workspace_default";
4501
- const sessionPolicyTools = withFirstPartyTools(
4502
- tracksWorkspaceDefaults ? withDefaultEnabledCapabilityMcpTools(
4503
- availableToolRefs(existingSession.tools, runtimeSettings),
4504
- settings,
4505
- capabilityRuntimeSettings
4506
- ) : existingSession.tools,
4507
- runtimeSettings
4508
- );
4509
- const validatedTools = input.toolsProvided ? validateToolRefsForSessionPolicy({
4510
- requested: input.tools ?? [],
4511
- settings: runtimeSettings,
4512
- allowedTools: sessionPolicyTools,
4513
- message: "message tools may only narrow the session tool policy"
4514
- }) : [];
4515
- const requestedTools = input.toolsProvided ? validatedTools : [];
4516
4484
  await requireLimit(deps, {
4517
4485
  accountId: grant.accountId,
4518
4486
  workspaceId,
@@ -4546,8 +4514,6 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
4546
4514
  text: input.text,
4547
4515
  turnInstructions: input.turnInstructions ?? null,
4548
4516
  resources: requestedResources,
4549
- tools: requestedTools,
4550
- toolsProvided: input.toolsProvided,
4551
4517
  model: input.model ?? null,
4552
4518
  reasoningEffort: input.reasoningEffort ?? null,
4553
4519
  reasoningEffortFallback: sessionReasoningEffort,
@@ -4654,7 +4620,7 @@ async function updateSessionMcpApprovalPolicy(deps, grant, sessionId, serverId,
4654
4620
  effectiveFrom: "next_attempt"
4655
4621
  };
4656
4622
  }
4657
- function toolPolicyAuditSnapshot(session, tools, policy = session.toolPolicy ?? { mode: "legacy", inheritedFromSessionId: null }) {
4623
+ function toolPolicyAuditSnapshot(session, tools, firstPartyMcpTools, policy = session.toolPolicy) {
4658
4624
  const allToolRefs = mergeToolRefs([], tools).sort((left, right) => {
4659
4625
  const leftMandatory = left.kind === "mcp" && left.id === "opengeni";
4660
4626
  const rightMandatory = right.kind === "mcp" && right.id === "opengeni";
@@ -4673,6 +4639,8 @@ function toolPolicyAuditSnapshot(session, tools, policy = session.toolPolicy ??
4673
4639
  toolIds: [...toolRefs].sort((left, right) => `${left.kind}:${left.id}`.localeCompare(`${right.kind}:${right.id}`)).map((tool) => tool.id),
4674
4640
  toolRefs,
4675
4641
  toolCount: allToolRefs.length,
4642
+ firstPartyMcpTools: [...firstPartyMcpTools].sort(),
4643
+ firstPartyMcpToolCount: firstPartyMcpTools.length,
4676
4644
  truncated: allToolRefs.length > toolRefs.length
4677
4645
  };
4678
4646
  }
@@ -4706,10 +4674,12 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
4706
4674
  }
4707
4675
  return withFirstPartyTools(validatedTools, runtimeSettings);
4708
4676
  })() : null;
4677
+ const explicitRequestedFirstPartyTools = explicitRequest ? [...explicitRequest.firstPartyMcpTools] : null;
4709
4678
  const workspaceDefaultTools = withFirstPartyTools(
4710
4679
  withDefaultEnabledCapabilityMcpTools([], deps.settings, capabilityRuntimeSettings),
4711
4680
  runtimeSettings
4712
4681
  );
4682
+ const workspaceDefaultFirstPartyTools = [...FIRST_PARTY_MCP_TOOL_NAMES];
4713
4683
  const events = await appendSessionEventsWithLockedSessionUpdate(
4714
4684
  deps.db,
4715
4685
  grant.workspaceId,
@@ -4720,6 +4690,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
4720
4690
  throw new SessionToolPolicyVersionConflictError(currentVersion);
4721
4691
  }
4722
4692
  let nextTools;
4693
+ let nextFirstPartyMcpTools;
4723
4694
  let nextPolicy;
4724
4695
  if (session.parentSessionId) {
4725
4696
  const parent = await context.getLockedSession(session.parentSessionId);
@@ -4735,6 +4706,9 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
4735
4706
  ) : parent.tools,
4736
4707
  runtimeSettings
4737
4708
  );
4709
+ const parentFirstPartyMcpTools = [
4710
+ ...parent.firstPartyMcpTools ?? DEFAULT_FIRST_PARTY_MCP_TOOLS
4711
+ ];
4738
4712
  if (requestedMode === "workspace_default") {
4739
4713
  if (!parentTracksWorkspaceDefaults) {
4740
4714
  throw new HTTPException10(403, {
@@ -4742,6 +4716,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
4742
4716
  });
4743
4717
  }
4744
4718
  nextTools = parentEffective;
4719
+ nextFirstPartyMcpTools = parentFirstPartyMcpTools;
4745
4720
  nextPolicy = {
4746
4721
  mode: "workspace_default",
4747
4722
  inheritedFromSessionId: parent.id
@@ -4753,6 +4728,16 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
4753
4728
  parentEffective,
4754
4729
  "session tools may only narrow the parent session tool policy"
4755
4730
  );
4731
+ const parentFirstPartySet = new Set(parentFirstPartyMcpTools);
4732
+ const widenedFirstPartyTool = explicitRequestedFirstPartyTools.find(
4733
+ (tool) => !parentFirstPartySet.has(tool)
4734
+ );
4735
+ if (widenedFirstPartyTool) {
4736
+ throw new HTTPException10(403, {
4737
+ message: `session OpenGeni tools may only narrow the parent policy: ${widenedFirstPartyTool}`
4738
+ });
4739
+ }
4740
+ nextFirstPartyMcpTools = explicitRequestedFirstPartyTools;
4756
4741
  nextPolicy = {
4757
4742
  mode: "explicit",
4758
4743
  inheritedFromSessionId: parent.id
@@ -4760,13 +4745,19 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
4760
4745
  }
4761
4746
  } else {
4762
4747
  nextTools = requestedMode === "workspace_default" ? workspaceDefaultTools : explicitRequestedTools;
4748
+ nextFirstPartyMcpTools = requestedMode === "workspace_default" ? workspaceDefaultFirstPartyTools : explicitRequestedFirstPartyTools;
4763
4749
  nextPolicy = { mode: requestedMode, inheritedFromSessionId: null };
4764
4750
  }
4765
- const currentPolicy = session.toolPolicy ?? {
4766
- mode: "legacy",
4767
- inheritedFromSessionId: null
4768
- };
4769
- const unchanged = stableJson2({ tools: session.tools, policy: currentPolicy }) === stableJson2({ tools: nextTools, policy: nextPolicy });
4751
+ const currentPolicy = session.toolPolicy;
4752
+ const unchanged = stableJson2({
4753
+ tools: session.tools,
4754
+ firstPartyMcpTools: session.firstPartyMcpTools ?? DEFAULT_FIRST_PARTY_MCP_TOOLS,
4755
+ policy: currentPolicy
4756
+ }) === stableJson2({
4757
+ tools: nextTools,
4758
+ firstPartyMcpTools: nextFirstPartyMcpTools,
4759
+ policy: nextPolicy
4760
+ });
4770
4761
  if (unchanged) {
4771
4762
  return { events: [] };
4772
4763
  }
@@ -4776,8 +4767,18 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
4776
4767
  {
4777
4768
  type: "session.tool_policy.updated",
4778
4769
  payload: {
4779
- before: toolPolicyAuditSnapshot(session, session.tools, currentPolicy),
4780
- after: toolPolicyAuditSnapshot(session, nextTools, nextPolicy),
4770
+ before: toolPolicyAuditSnapshot(
4771
+ session,
4772
+ session.tools,
4773
+ [...session.firstPartyMcpTools ?? DEFAULT_FIRST_PARTY_MCP_TOOLS],
4774
+ currentPolicy
4775
+ ),
4776
+ after: toolPolicyAuditSnapshot(
4777
+ session,
4778
+ nextTools,
4779
+ nextFirstPartyMcpTools,
4780
+ nextPolicy
4781
+ ),
4781
4782
  version: nextVersion,
4782
4783
  effectiveFrom: "next_attempt"
4783
4784
  }
@@ -4785,6 +4786,7 @@ async function updateSessionToolPolicy(deps, grant, sessionId, request) {
4785
4786
  ],
4786
4787
  update: {
4787
4788
  tools: nextTools,
4789
+ firstPartyMcpTools: nextFirstPartyMcpTools,
4788
4790
  toolPolicy: nextPolicy,
4789
4791
  toolPolicyVersion: nextVersion,
4790
4792
  expectedToolPolicyVersion: request.expectedVersion
@@ -5355,14 +5357,14 @@ async function authorizeHumanSessionCommand(deps, context, operation) {
5355
5357
  return await requireSessionAuthorization(deps, humanAccessGrant(context), {
5356
5358
  sessionId: context.sessionId,
5357
5359
  operation,
5358
- surface: "core"
5360
+ surface: context.authorizationSurface ?? "core"
5359
5361
  });
5360
5362
  }
5361
5363
  async function authorizeAgentSessionCommand(deps, context, targetSessionId, operation) {
5362
5364
  return await requireSessionAuthorization(deps, agentAccessGrant(context), {
5363
5365
  sessionId: targetSessionId,
5364
5366
  operation,
5365
- surface: "core"
5367
+ surface: context.authorizationSurface ?? "core"
5366
5368
  });
5367
5369
  }
5368
5370
  function agentActor(context) {
@@ -5493,7 +5495,12 @@ async function steerAgentSession(deps, context, input) {
5493
5495
  return result;
5494
5496
  }
5495
5497
  async function controlAgentSessionWorkstream(deps, context, input) {
5496
- await authorizeAgentSessionCommand(deps, context, input.targetSessionId, "session.control");
5498
+ const authorization = await authorizeAgentSessionCommand(
5499
+ deps,
5500
+ context,
5501
+ input.targetSessionId,
5502
+ "session.control"
5503
+ );
5497
5504
  const result = await withWorkspaceRls(
5498
5505
  deps.db,
5499
5506
  context.workspaceId,
@@ -5514,7 +5521,7 @@ async function controlAgentSessionWorkstream(deps, context, input) {
5514
5521
  ]);
5515
5522
  await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
5516
5523
  await requestControlWakeDispatch(deps, result.wakeCount);
5517
- return result;
5524
+ return { ...result, authorization };
5518
5525
  }
5519
5526
  function receipt(row) {
5520
5527
  return {
@@ -5536,8 +5543,6 @@ function composerDraft(row) {
5536
5543
  revision: row.revision,
5537
5544
  text: row.text,
5538
5545
  resources: row.resources,
5539
- tools: row.tools,
5540
- toolsProvided: row.toolsProvided,
5541
5546
  model: row.model,
5542
5547
  reasoningEffort: row.reasoningEffort,
5543
5548
  sourceTurnId: row.sourceTurnId,
@@ -5755,8 +5760,6 @@ async function getHumanComposerDraft(deps, context) {
5755
5760
  revision: 0,
5756
5761
  text: "",
5757
5762
  resources: [],
5758
- tools: [],
5759
- toolsProvided: false,
5760
5763
  model: session.model,
5761
5764
  reasoningEffort: reasoningEffortForMetadata2(session.metadata, "medium"),
5762
5765
  sourceTurnId: null,