@opengeni/core 0.28.0 → 1.0.1

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.
@@ -40,6 +40,16 @@ export declare function personalConnectionDelegationsFromParent(input: {
40
40
  servers: McpServerConfig[];
41
41
  parentDelegations: McpPersonalConnectionDelegation[];
42
42
  }): McpPersonalConnectionDelegation[];
43
+ /**
44
+ * Freezes Google Drive publishing only when one exact subject-owned connection
45
+ * is eligible. Multiple writable Google accounts are intentionally ambiguous:
46
+ * callers must narrow the connection before a later turn can advertise or use
47
+ * the private publication tool.
48
+ */
49
+ export declare function googleDrivePublicationDelegationFromVisibleConnections(input: {
50
+ subjectId: string;
51
+ connections: ConnectionMetadata[];
52
+ }): McpPersonalConnectionDelegation | null;
43
53
  export declare function personalConnectionDelegationsEqual(left: McpPersonalConnectionDelegation[], right: McpPersonalConnectionDelegation[]): boolean;
44
54
  export declare function personalConnectionDelegationForServer(delegations: McpPersonalConnectionDelegation[], server: Pick<McpServerConfig, "id" | "connectionRef">): McpPersonalConnectionDelegation | null;
45
55
  type ConnectionCredentialResolver = (request: ResolveConnectionCredentialInput) => Promise<ResolveConnectionCredentialResult>;
@@ -28,5 +28,5 @@ export declare function validateGitHubRepositorySelection(db: Database, workspac
28
28
  * leave the result unknown and must not cause draft hydration to delete it.
29
29
  */
30
30
  export declare function isAuthoritativeGitHubRepositorySelectionError(error: unknown): boolean;
31
- export declare function validateFileResources(db: Database, workspaceId: string, resources: ResourceRef[]): Promise<void>;
31
+ export declare function validateFileResources(db: Database, accountId: string, workspaceId: string, subjectId: string, resources: ResourceRef[]): Promise<void>;
32
32
  export { mergeToolRefs, stableJson };
@@ -78,7 +78,7 @@ export declare function createAndStartSessionWithOutcome(input: {
78
78
  initialMessage: string;
79
79
  /** Create the session shell without an initial user event/agent turn. */
80
80
  deferInitialTurn?: boolean;
81
- turnInstructions?: string | null;
81
+ modelContext?: string | null;
82
82
  resources: ResourceRef[];
83
83
  skills?: SessionSkill[];
84
84
  tools: ToolRef[];
@@ -194,7 +194,7 @@ export declare function postUserMessageTurn(input: {
194
194
  sessionId: string;
195
195
  text: string;
196
196
  annotations?: TimelineAnnotation[];
197
- turnInstructions?: string | null;
197
+ modelContext?: string | null;
198
198
  resources: ResourceRef[];
199
199
  model?: string | null;
200
200
  reasoningEffort?: Settings["openaiReasoningEffort"] | null;
@@ -243,7 +243,7 @@ export declare function createSessionForRequest(deps: ApiRouteDeps, grant: Acces
243
243
  export declare function acceptSessionUserMessageWithOutcome(deps: AcceptSessionUserMessageDependencies, grant: AccessGrant, workspaceId: string, sessionId: string, input: {
244
244
  text: string;
245
245
  annotations?: SubmittedTimelineAnnotation[];
246
- turnInstructions?: string | null;
246
+ modelContext?: string | null;
247
247
  resources?: ResourceRef[];
248
248
  model?: string | null;
249
249
  reasoningEffort?: ReasoningEffort | null;
package/dist/index.js CHANGED
@@ -4355,6 +4355,12 @@ async function listRigChangesForApi(deps, workspaceId, rigId, limit) {
4355
4355
  }
4356
4356
 
4357
4357
  // src/domain/personal-connection-delegations.ts
4358
+ import {
4359
+ GOOGLE_DRIVE_PROVIDER_DOMAIN,
4360
+ GOOGLE_DRIVE_PUBLICATION_SERVER_ID,
4361
+ GoogleDriveConnectionMetadata,
4362
+ googleDriveScopesAllowCapability
4363
+ } from "@opengeni/contracts/google-drive";
4358
4364
  import {
4359
4365
  getSessionTurnPersonalConnectionDelegations,
4360
4366
  getConnectionMetadata as getConnectionMetadata2,
@@ -4523,9 +4529,31 @@ function personalConnectionDelegationsFromParent(input) {
4523
4529
  });
4524
4530
  return [
4525
4531
  ...mcp,
4526
- ...input.parentDelegations.filter((item) => item.connectionType === "social" || item.connectionType === "atlassian").map((item) => ({ ...item }))
4532
+ ...input.parentDelegations.filter(
4533
+ (item) => item.connectionType === "social" || item.connectionType === "atlassian" || item.serverId === GOOGLE_DRIVE_PUBLICATION_SERVER_ID
4534
+ ).map((item) => ({ ...item }))
4527
4535
  ];
4528
4536
  }
4537
+ function googleDrivePublicationDelegationFromVisibleConnections(input) {
4538
+ const eligible = input.connections.filter((connection2) => {
4539
+ if (connection2.subjectId !== input.subjectId || connection2.status !== "active" || connection2.kind !== "oauth2" || !sameProviderDomain(connection2.providerDomain, GOOGLE_DRIVE_PROVIDER_DOMAIN) || !googleDriveScopesAllowCapability(connection2.grantedScopes, "publish_file")) {
4540
+ return false;
4541
+ }
4542
+ const metadata = GoogleDriveConnectionMetadata.safeParse(connection2.metadata);
4543
+ return Boolean(
4544
+ metadata.success && metadata.data.outputDestination && metadata.data.lifecycle?.state !== "paused" && (!metadata.data.lifecycle || metadata.data.lifecycle.state === "active")
4545
+ );
4546
+ });
4547
+ if (eligible.length !== 1) return null;
4548
+ const connection = eligible[0];
4549
+ return {
4550
+ serverId: GOOGLE_DRIVE_PUBLICATION_SERVER_ID,
4551
+ connectionId: connection.id,
4552
+ ownerSubjectId: input.subjectId,
4553
+ providerDomain: connection.providerDomain,
4554
+ kind: connection.kind
4555
+ };
4556
+ }
4529
4557
  function personalConnectionDelegationsEqual(left, right) {
4530
4558
  if (left.length !== right.length) return false;
4531
4559
  const byServer = new Map(right.map((delegation) => [delegation.serverId, delegation]));
@@ -4558,7 +4586,10 @@ function withFrozenPersonalConnectionDelegations(input) {
4558
4586
  let effectiveRequest = request;
4559
4587
  if (request.connectionRef.subjectScope === "subject") {
4560
4588
  const config = input.settings.mcpServers.find((server) => server.id === request.serverId);
4561
- const delegation = config ? personalConnectionDelegationForServer(input.personalConnectionDelegations, config) : null;
4589
+ const publicationDelegations = request.serverId === GOOGLE_DRIVE_PUBLICATION_SERVER_ID && sameProviderDomain(request.connectionRef.providerDomain, GOOGLE_DRIVE_PROVIDER_DOMAIN) ? input.personalConnectionDelegations.filter(
4590
+ (candidate) => candidate.serverId === GOOGLE_DRIVE_PUBLICATION_SERVER_ID && sameProviderDomain(candidate.providerDomain, GOOGLE_DRIVE_PROVIDER_DOMAIN) && candidate.kind === "oauth2" && request.connectionRef.kind === "oauth2"
4591
+ ) : [];
4592
+ const delegation = config ? personalConnectionDelegationForServer(input.personalConnectionDelegations, config) : publicationDelegations.length === 1 ? publicationDelegations[0] : null;
4562
4593
  if (!delegation || !await input.ownerHasWorkspaceMembership(delegation.ownerSubjectId)) {
4563
4594
  return personalAuthorityUnavailable(request);
4564
4595
  }
@@ -4597,15 +4628,20 @@ async function freezePersonalConnectionDelegations(input) {
4597
4628
  )
4598
4629
  });
4599
4630
  return includeFirstPartyConnections ? inherited : inherited.filter(
4600
- (item) => item.connectionType !== "social" && item.connectionType !== "atlassian"
4631
+ (item) => item.connectionType !== "social" && item.connectionType !== "atlassian" && item.serverId !== GOOGLE_DRIVE_PUBLICATION_SERVER_ID
4601
4632
  );
4602
4633
  }
4603
4634
  const membership = await getWorkspaceGrant3(input.db, input.source.subjectId, input.workspaceId);
4604
4635
  if (!membership) return [];
4636
+ const visibleConnections = await listConnectionsMetadata2(
4637
+ input.db,
4638
+ input.workspaceId,
4639
+ input.source.subjectId
4640
+ );
4605
4641
  const mcp = personalConnectionDelegationsFromVisibleConnections({
4606
4642
  servers,
4607
4643
  subjectId: input.source.subjectId,
4608
- connections: await listConnectionsMetadata2(input.db, input.workspaceId, input.source.subjectId)
4644
+ connections: visibleConnections
4609
4645
  });
4610
4646
  if (!includeFirstPartyConnections) return mcp;
4611
4647
  const ownerSubjectId = input.source.subjectId;
@@ -4618,11 +4654,16 @@ async function freezePersonalConnectionDelegations(input) {
4618
4654
  if (!prior || connection.updatedAt > prior.updatedAt)
4619
4655
  latest.set(connection.provider, connection);
4620
4656
  }
4621
- const personalAtlassian = (await listConnectionsMetadata2(input.db, input.workspaceId, ownerSubjectId)).filter(
4657
+ const personalAtlassian = visibleConnections.filter(
4622
4658
  (connection) => connection.subjectId === ownerSubjectId && connection.status === "active" && sameProviderDomain(connection.providerDomain, "api.atlassian.com")
4623
4659
  );
4660
+ const googleDrivePublication = googleDrivePublicationDelegationFromVisibleConnections({
4661
+ subjectId: ownerSubjectId,
4662
+ connections: visibleConnections
4663
+ });
4624
4664
  return [
4625
4665
  ...mcp,
4666
+ ...googleDrivePublication ? [googleDrivePublication] : [],
4626
4667
  ...[...latest.values()].map((connection) => ({
4627
4668
  serverId: `social:${connection.provider}`,
4628
4669
  connectionId: connection.id,
@@ -4659,7 +4700,10 @@ import {
4659
4700
  ResourceMountPathError,
4660
4701
  stableJson as stableJson3
4661
4702
  } from "@opengeni/contracts";
4662
- import { areGitHubRepositoriesAllowedForWorkspace, requireFile } from "@opengeni/db";
4703
+ import {
4704
+ areGitHubRepositoriesAllowedForWorkspace,
4705
+ requireFileForSubject
4706
+ } from "@opengeni/db";
4663
4707
  import { HTTPException as HTTPException8 } from "hono/http-exception";
4664
4708
  function validateToolRefs(tools, settings) {
4665
4709
  const mcpServerIds = new Set(settings.mcpServers.map((server) => server.id));
@@ -4864,7 +4908,7 @@ async function validateGitHubRepositorySelection(db, workspaceId, resources) {
4864
4908
  function isAuthoritativeGitHubRepositorySelectionError(error) {
4865
4909
  return error instanceof HTTPException8 && error.status === 422;
4866
4910
  }
4867
- async function validateFileResources(db, workspaceId, resources) {
4911
+ async function validateFileResources(db, accountId, workspaceId, subjectId, resources) {
4868
4912
  const fileIds = /* @__PURE__ */ new Set();
4869
4913
  for (const resource of resources) {
4870
4914
  if (resource.kind !== "file") {
@@ -4874,7 +4918,12 @@ async function validateFileResources(db, workspaceId, resources) {
4874
4918
  throw new HTTPException8(422, { message: `duplicate file resource: ${resource.fileId}` });
4875
4919
  }
4876
4920
  fileIds.add(resource.fileId);
4877
- const file = await requireFile(db, workspaceId, resource.fileId).catch(() => null);
4921
+ const file = await requireFileForSubject(db, {
4922
+ accountId,
4923
+ workspaceId,
4924
+ subjectId,
4925
+ fileId: resource.fileId
4926
+ }).catch(() => null);
4878
4927
  if (!file) {
4879
4928
  throw new HTTPException8(422, { message: `unknown file resource: ${resource.fileId}` });
4880
4929
  }
@@ -5652,7 +5701,7 @@ async function createAndStartSessionWithOutcome(input) {
5652
5701
  accountId: input.accountId,
5653
5702
  workspaceId: input.workspaceId,
5654
5703
  initialMessage: input.initialMessage,
5655
- initialTurnInstructions: input.turnInstructions ?? null,
5704
+ initialModelContext: input.modelContext ?? null,
5656
5705
  resources: input.resources,
5657
5706
  skills: input.skills ?? [],
5658
5707
  tools: input.tools,
@@ -5716,7 +5765,7 @@ async function createAndStartSessionWithOutcome(input) {
5716
5765
  accountId: input.accountId,
5717
5766
  workspaceId: input.workspaceId,
5718
5767
  initialMessage: input.initialMessage,
5719
- initialTurnInstructions: input.turnInstructions ?? null,
5768
+ initialModelContext: input.modelContext ?? null,
5720
5769
  resources: input.resources,
5721
5770
  skills: input.skills ?? [],
5722
5771
  tools: input.tools,
@@ -5966,7 +6015,7 @@ async function postUserMessageTurn(input) {
5966
6015
  expectedDraftRevision: input.expectedDraftRevision ?? null,
5967
6016
  text: input.text,
5968
6017
  annotations: input.annotations ?? [],
5969
- turnInstructions: input.turnInstructions ?? null,
6018
+ modelContext: input.modelContext ?? null,
5970
6019
  resources: input.resources,
5971
6020
  model: requestedModel,
5972
6021
  reasoningEffort: requestedReasoningEffort,
@@ -6182,7 +6231,7 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
6182
6231
  message: "object storage is not configured"
6183
6232
  });
6184
6233
  }
6185
- await validateFileResources(db, workspaceId, resources);
6234
+ await validateFileResources(db, grant.accountId, workspaceId, grant.subjectId, resources);
6186
6235
  const variableSet = payload.variableSetId ? await validateVariableSetAttachment(
6187
6236
  { settings, db },
6188
6237
  grant,
@@ -6401,7 +6450,7 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
6401
6450
  workspaceId,
6402
6451
  initialMessage: payload.initialMessage ?? "",
6403
6452
  deferInitialTurn: payload.startMode === "realtime",
6404
- turnInstructions: payload.turnInstructions ?? null,
6453
+ modelContext: payload.modelContext ?? null,
6405
6454
  resources,
6406
6455
  skills,
6407
6456
  tools,
@@ -6568,7 +6617,13 @@ async function acceptSessionUserMessageWithOutcome(deps, grant, workspaceId, ses
6568
6617
  message: "object storage is not configured"
6569
6618
  });
6570
6619
  }
6571
- await validateFileResources(db, workspaceId, requestedResources);
6620
+ await validateFileResources(
6621
+ db,
6622
+ grant.accountId,
6623
+ workspaceId,
6624
+ grant.subjectId,
6625
+ requestedResources
6626
+ );
6572
6627
  await validateGitHubRepositorySelection(db, workspaceId, [
6573
6628
  ...existingSession.resources,
6574
6629
  ...requestedResources
@@ -6599,7 +6654,7 @@ async function acceptSessionUserMessageWithOutcome(deps, grant, workspaceId, ses
6599
6654
  sessionId,
6600
6655
  text: input.text,
6601
6656
  annotations,
6602
- turnInstructions: input.turnInstructions ?? null,
6657
+ modelContext: input.modelContext ?? null,
6603
6658
  resources: requestedResources,
6604
6659
  model: input.model ?? null,
6605
6660
  reasoningEffort: input.reasoningEffort ?? null,
@@ -7444,7 +7499,13 @@ async function validateScheduledTaskAgentConfig(input) {
7444
7499
  if (resources.some((resource) => resource.kind === "file") && !input.objectStorage) {
7445
7500
  throw new HTTPException12(503, { message: "object storage is not configured" });
7446
7501
  }
7447
- await validateFileResources(input.db, input.workspaceId, resources);
7502
+ await validateFileResources(
7503
+ input.db,
7504
+ input.grant.accountId,
7505
+ input.workspaceId,
7506
+ input.grant.subjectId,
7507
+ resources
7508
+ );
7448
7509
  if (input.payload.agentConfig.slackBotConnectionId) {
7449
7510
  await validateOpenGeniSlackBotConnectionSelection(
7450
7511
  input.db,
@@ -9722,7 +9783,7 @@ import {
9722
9783
  NewSessionDraftAccessError,
9723
9784
  newSessionDraftToolsProvided,
9724
9785
  publicNewSessionDraftOptions,
9725
- requireFile as requireFile2,
9786
+ requireFileForSubject as requireFileForSubject2,
9726
9787
  saveNewSessionDraftInTransaction,
9727
9788
  withWorkspaceSubjectRls
9728
9789
  } from "@opengeni/db";
@@ -9770,7 +9831,12 @@ async function hydrateNewSessionDraft(deps, grant, workspaceId, row) {
9770
9831
  continue;
9771
9832
  }
9772
9833
  try {
9773
- const file = await requireFile2(deps.db, workspaceId, resource.fileId);
9834
+ const file = await requireFileForSubject2(deps.db, {
9835
+ accountId: grant.accountId,
9836
+ workspaceId,
9837
+ subjectId: grant.subjectId,
9838
+ fileId: resource.fileId
9839
+ });
9774
9840
  if (file.status === "ready") resources.push(resource);
9775
9841
  } catch {
9776
9842
  }
@@ -9854,7 +9920,7 @@ async function saveActorNewSessionDraft(deps, grant, workspaceId, rawInput) {
9854
9920
  if (resources.some((resource) => resource.kind === "file") && !deps.objectStorage) {
9855
9921
  throw new HTTPException14(503, { message: "object storage is not configured" });
9856
9922
  }
9857
- await validateFileResources(deps.db, workspaceId, resources);
9923
+ await validateFileResources(deps.db, grant.accountId, workspaceId, grant.subjectId, resources);
9858
9924
  assertConfiguredModel(deps.settings, input.model);
9859
9925
  await assertWorkspaceModelPolicyAllows(deps.db, deps.settings, workspaceId, input.model);
9860
9926
  try {
@@ -10655,6 +10721,7 @@ export {
10655
10721
  getHumanComposerDraft,
10656
10722
  getManagedSession,
10657
10723
  getWorkspaceInsights,
10724
+ googleDrivePublicationDelegationFromVisibleConnections,
10658
10725
  hasLiteralPermission,
10659
10726
  hasPermission,
10660
10727
  hasReservedFikenMetadata,