@opengeni/api-router 2.13.0-canary.0 → 2.13.0-canary.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +1 -1
- package/dist/{chunk-BJWHVMOL.js → chunk-K2NU4KRK.js} +3165 -3662
- package/dist/{chunk-BJWHVMOL.js.map → chunk-K2NU4KRK.js.map} +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/integrations/github-lens-connect.d.ts +2 -2
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/documents.d.ts +5 -13
- package/dist/mcp/files.d.ts +2 -2
- package/dist/mcp/first-party-tool-permissions.d.ts +36 -0
- package/dist/mcp/knowledge-entries.d.ts +5 -0
- package/dist/mcp/server.d.ts +4 -4
- package/dist/routes/host-mcp-resolvers.d.ts +3 -0
- package/dist/routes/knowledge.d.ts +3 -0
- package/dist/sandbox-file-artifacts.d.ts +6 -2
- package/dist/workspace-state-projection.d.ts +3 -5
- package/dist/workspace-tool-gateway.d.ts +2 -1
- package/package.json +19 -19
- package/src/access-grant-rls.ts +13 -1
- package/src/app.ts +4 -11
- package/src/codemode.ts +4 -8
- package/src/index.ts +4 -3
- package/src/integrations/atlassian.ts +31 -28
- package/src/integrations/google-drive.ts +33 -30
- package/src/integrations/marketplace-plugin.ts +1 -1
- package/src/integrations/provider-oauth.ts +11 -2
- package/src/mcp/documents.ts +57 -428
- package/src/mcp/files.ts +76 -58
- package/src/mcp/first-party-tool-permissions.ts +9 -0
- package/src/mcp/knowledge-entries.ts +276 -0
- package/src/mcp/server.ts +7 -361
- package/src/routes/company-brain.ts +58 -107
- package/src/routes/documents.ts +78 -294
- package/src/routes/files.ts +68 -3
- package/src/routes/host-mcp-resolvers.ts +41 -0
- package/src/routes/knowledge.ts +386 -0
- package/src/routes/organization-memberships.ts +33 -0
- package/src/routes/scheduled-tasks.ts +9 -1
- package/src/routes/sessions.ts +20 -3
- package/src/routes/skill-content.ts +8 -2
- package/src/routes/video-generation.ts +7 -5
- package/src/routes/workspace-learning.ts +12 -82
- package/src/routes/workspace-state.ts +34 -25
- package/src/routes/workspaces.ts +20 -10
- package/src/sandbox-file-artifacts.ts +54 -2
- package/src/scheduled-task-deletion.ts +3 -2
- package/src/workspace-state-projection.ts +18 -148
- package/src/workspace-tool-gateway.ts +14 -25
- package/dist/mcp/company-brain-governed-writes.d.ts +0 -26
- package/dist/mcp/remember.d.ts +0 -28
- package/src/mcp/company-brain-governed-writes.ts +0 -271
- package/src/mcp/remember.ts +0 -191
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
scheduledTaskKnowledgeSource,
|
|
3
|
+
requireScheduledTaskKnowledgeSource,
|
|
4
|
+
knowledgeSourceAgentConfig,
|
|
5
|
+
} from "@opengeni/contracts";
|
|
1
6
|
import type { Settings } from "@opengeni/config";
|
|
2
7
|
import { createHash } from "node:crypto";
|
|
3
8
|
import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
|
|
@@ -840,10 +845,10 @@ export async function preflightAtlassianScheduleAuthorization(
|
|
|
840
845
|
input: { task: ScheduledTask; subjectId: string },
|
|
841
846
|
): Promise<void> {
|
|
842
847
|
const { task } = input;
|
|
843
|
-
if (task
|
|
848
|
+
if (!scheduledTaskKnowledgeSource(task)) return;
|
|
844
849
|
if (
|
|
845
|
-
task.
|
|
846
|
-
task.
|
|
850
|
+
requireScheduledTaskKnowledgeSource(task).initiatingSubjectId !== input.subjectId ||
|
|
851
|
+
requireScheduledTaskKnowledgeSource(task).connection.ownerSubjectId !== input.subjectId
|
|
847
852
|
) {
|
|
848
853
|
throw new HTTPException(403, {
|
|
849
854
|
message: "knowledge source schedule requires the exact initiating subject",
|
|
@@ -862,10 +867,10 @@ export async function revokeAtlassianScheduleAuthorization(
|
|
|
862
867
|
input: { task: ScheduledTask; subjectId: string },
|
|
863
868
|
): Promise<void> {
|
|
864
869
|
const { task } = input;
|
|
865
|
-
if (task
|
|
870
|
+
if (!scheduledTaskKnowledgeSource(task)) return;
|
|
866
871
|
if (
|
|
867
|
-
task.
|
|
868
|
-
task.
|
|
872
|
+
requireScheduledTaskKnowledgeSource(task).initiatingSubjectId !== input.subjectId ||
|
|
873
|
+
requireScheduledTaskKnowledgeSource(task).connection.ownerSubjectId !== input.subjectId
|
|
869
874
|
) {
|
|
870
875
|
throw new HTTPException(403, {
|
|
871
876
|
message: "knowledge source schedule requires the exact initiating subject",
|
|
@@ -879,11 +884,11 @@ export async function revokeAtlassianScheduleAuthorization(
|
|
|
879
884
|
taskId: task.id,
|
|
880
885
|
accountId: task.accountId,
|
|
881
886
|
workspaceId: task.workspaceId,
|
|
882
|
-
connectionId: task.
|
|
883
|
-
connectionVersion: task.
|
|
884
|
-
sourceId: task.
|
|
885
|
-
sourceLifecycleGeneration: task.
|
|
886
|
-
sourceConfigGeneration: task.
|
|
887
|
+
connectionId: requireScheduledTaskKnowledgeSource(task).connection.connectionId,
|
|
888
|
+
connectionVersion: requireScheduledTaskKnowledgeSource(task).connection.connectionVersion,
|
|
889
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
890
|
+
sourceLifecycleGeneration: requireScheduledTaskKnowledgeSource(task).sourceLifecycleGeneration,
|
|
891
|
+
sourceConfigGeneration: requireScheduledTaskKnowledgeSource(task).sourceConfigGeneration,
|
|
887
892
|
externalSourceId,
|
|
888
893
|
subjectId: input.subjectId,
|
|
889
894
|
});
|
|
@@ -1014,11 +1019,11 @@ async function materializeSchedules(
|
|
|
1014
1019
|
let source = null as Awaited<ReturnType<typeof upsertKnowledgeSource>> | null;
|
|
1015
1020
|
let existingTask: (typeof tasks)[number] | null = null;
|
|
1016
1021
|
for (const task of tasks) {
|
|
1017
|
-
if (task
|
|
1022
|
+
if (!scheduledTaskKnowledgeSource(task)) continue;
|
|
1018
1023
|
const resolved = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
1019
1024
|
accountId: input.accountId,
|
|
1020
1025
|
workspaceId: input.workspaceId,
|
|
1021
|
-
sourceId: task.
|
|
1026
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1022
1027
|
initiatingSubjectId: input.subjectId,
|
|
1023
1028
|
});
|
|
1024
1029
|
if (resolved?.source.externalSourceId !== identity.externalSourceId) continue;
|
|
@@ -1125,7 +1130,10 @@ async function materializeSchedules(
|
|
|
1125
1130
|
name: `Sync ${selectedSource.kind === "jira_project" ? "Jira" : "Confluence"}: ${selectedSource.name}`,
|
|
1126
1131
|
schedule: atlassianSchedule(selectedSource.syncCadence),
|
|
1127
1132
|
overlapPolicy: "buffer_one",
|
|
1128
|
-
action,
|
|
1133
|
+
action: { kind: "agent_turn" },
|
|
1134
|
+
agentConfig: knowledgeSourceAgentConfig(action, existingTask.agentConfig),
|
|
1135
|
+
refreshPersonalResourceAuthority: true,
|
|
1136
|
+
authorityUpdatedBy: { kind: "subject", subjectId: input.subjectId },
|
|
1129
1137
|
metadata: {
|
|
1130
1138
|
...existingTask.metadata,
|
|
1131
1139
|
connectorKind: "atlassian",
|
|
@@ -1153,16 +1161,11 @@ async function materializeSchedules(
|
|
|
1153
1161
|
status: "active",
|
|
1154
1162
|
schedule: atlassianSchedule(selectedSource.syncCadence),
|
|
1155
1163
|
overlapPolicy: "buffer_one",
|
|
1156
|
-
action,
|
|
1164
|
+
action: { kind: "agent_turn" },
|
|
1157
1165
|
runMode: "new_session_per_run",
|
|
1158
1166
|
targetSessionId: null,
|
|
1159
1167
|
connectionAuthorities: [],
|
|
1160
|
-
agentConfig:
|
|
1161
|
-
prompt: "Knowledge source synchronization",
|
|
1162
|
-
resources: [],
|
|
1163
|
-
tools: [],
|
|
1164
|
-
metadata: {},
|
|
1165
|
-
},
|
|
1168
|
+
agentConfig: knowledgeSourceAgentConfig(action),
|
|
1166
1169
|
variableSetId: null,
|
|
1167
1170
|
environmentId: null,
|
|
1168
1171
|
rigId: null,
|
|
@@ -1187,11 +1190,11 @@ async function materializeSchedules(
|
|
|
1187
1190
|
}
|
|
1188
1191
|
|
|
1189
1192
|
for (const task of tasks) {
|
|
1190
|
-
if (task
|
|
1193
|
+
if (!scheduledTaskKnowledgeSource(task)) continue;
|
|
1191
1194
|
const resolved = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
1192
1195
|
accountId: input.accountId,
|
|
1193
1196
|
workspaceId: input.workspaceId,
|
|
1194
|
-
sourceId: task.
|
|
1197
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1195
1198
|
initiatingSubjectId: input.subjectId,
|
|
1196
1199
|
});
|
|
1197
1200
|
if (!resolved || enabledIds.has(resolved.source.externalSourceId)) continue;
|
|
@@ -1510,20 +1513,20 @@ async function deauthorizeConnectionSources(
|
|
|
1510
1513
|
connection.id,
|
|
1511
1514
|
);
|
|
1512
1515
|
for (const task of tasks) {
|
|
1513
|
-
if (task
|
|
1516
|
+
if (!scheduledTaskKnowledgeSource(task)) continue;
|
|
1514
1517
|
const resolved = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
1515
1518
|
accountId: task.accountId,
|
|
1516
1519
|
workspaceId: task.workspaceId,
|
|
1517
|
-
sourceId: task.
|
|
1520
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1518
1521
|
initiatingSubjectId: subjectId,
|
|
1519
1522
|
});
|
|
1520
1523
|
if (!resolved || resolved.source.lifecycleState !== "active") continue;
|
|
1521
1524
|
await deauthorizeKnowledgeSourceRetrieval(deps.db, {
|
|
1522
1525
|
accountId: task.accountId,
|
|
1523
1526
|
workspaceId: task.workspaceId,
|
|
1524
|
-
sourceId: task.
|
|
1525
|
-
audience: task.
|
|
1526
|
-
operationId: `atlassian-deauthorize:${connection.id}:${connection.version + 1}:${task.
|
|
1527
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1528
|
+
audience: requireScheduledTaskKnowledgeSource(task).destination,
|
|
1529
|
+
operationId: `atlassian-deauthorize:${connection.id}:${connection.version + 1}:${requireScheduledTaskKnowledgeSource(task).sourceId}:${reasonCode}`,
|
|
1527
1530
|
reasonCode,
|
|
1528
1531
|
actor: { kind: "human", subjectId, initiatingHumanSubjectId: subjectId },
|
|
1529
1532
|
});
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
scheduledTaskKnowledgeSource,
|
|
3
|
+
requireScheduledTaskKnowledgeSource,
|
|
4
|
+
knowledgeSourceAgentConfig,
|
|
5
|
+
} from "@opengeni/contracts";
|
|
1
6
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
7
|
import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
|
|
3
8
|
import { requireConnectOwnerAuthority } from "./connect-authority";
|
|
@@ -188,7 +193,7 @@ export async function wakeGoogleDriveSourcesFromWorkspaceEvent(
|
|
|
188
193
|
);
|
|
189
194
|
let triggered = 0;
|
|
190
195
|
for (const task of tasks) {
|
|
191
|
-
if (task
|
|
196
|
+
if (!scheduledTaskKnowledgeSource(task) || task.status !== "active") continue;
|
|
192
197
|
const externalSourceId =
|
|
193
198
|
typeof task.metadata.externalSourceId === "string" ? task.metadata.externalSourceId : null;
|
|
194
199
|
const selectedSource = externalSourceId ? selectedById.get(externalSourceId) : null;
|
|
@@ -808,20 +813,20 @@ async function deauthorizeGoogleDriveConnectionSources(
|
|
|
808
813
|
connection.workspaceId,
|
|
809
814
|
connection.id,
|
|
810
815
|
async (task) => {
|
|
811
|
-
if (task.
|
|
816
|
+
if (requireScheduledTaskKnowledgeSource(task).initiatingSubjectId !== subjectId) return;
|
|
812
817
|
const resolved = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
813
818
|
accountId: task.accountId,
|
|
814
819
|
workspaceId: task.workspaceId,
|
|
815
|
-
sourceId: task.
|
|
820
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
816
821
|
initiatingSubjectId: subjectId,
|
|
817
822
|
});
|
|
818
823
|
if (!resolved || resolved.source.lifecycleState !== "active") return;
|
|
819
824
|
await deauthorizeKnowledgeSourceRetrieval(deps.db, {
|
|
820
825
|
accountId: task.accountId,
|
|
821
826
|
workspaceId: task.workspaceId,
|
|
822
|
-
sourceId: task.
|
|
823
|
-
audience: task.
|
|
824
|
-
operationId: `google-drive-deauthorize:${connection.id}:${authorityVersion}:${task.
|
|
827
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
828
|
+
audience: requireScheduledTaskKnowledgeSource(task).destination,
|
|
829
|
+
operationId: `google-drive-deauthorize:${connection.id}:${authorityVersion}:${requireScheduledTaskKnowledgeSource(task).sourceId}:${reasonCode}`,
|
|
825
830
|
reasonCode,
|
|
826
831
|
actor: {
|
|
827
832
|
kind: "human",
|
|
@@ -837,7 +842,7 @@ async function forEachGoogleDriveConnectionTask(
|
|
|
837
842
|
deps: ApiRouteDeps,
|
|
838
843
|
workspaceId: string,
|
|
839
844
|
connectionId: string,
|
|
840
|
-
fn: (task: ScheduledTask
|
|
845
|
+
fn: (task: ScheduledTask) => Promise<void>,
|
|
841
846
|
): Promise<void> {
|
|
842
847
|
const tasks = await listKnowledgeSourceSyncTasksForConnection(deps.db, workspaceId, connectionId);
|
|
843
848
|
for (const task of tasks) {
|
|
@@ -1393,11 +1398,11 @@ async function materializeGoogleDriveKnowledgeSchedules(
|
|
|
1393
1398
|
let source = null as Awaited<ReturnType<typeof upsertKnowledgeSource>> | null;
|
|
1394
1399
|
let existingTask = null as (typeof connectionTasks)[number] | null;
|
|
1395
1400
|
for (const task of connectionTasks) {
|
|
1396
|
-
if (task
|
|
1401
|
+
if (!scheduledTaskKnowledgeSource(task)) continue;
|
|
1397
1402
|
const resolved = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
1398
1403
|
accountId: input.accountId,
|
|
1399
1404
|
workspaceId: input.workspaceId,
|
|
1400
|
-
sourceId: task.
|
|
1405
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1401
1406
|
initiatingSubjectId: input.subjectId,
|
|
1402
1407
|
});
|
|
1403
1408
|
if (resolved?.source.externalSourceId !== identity.externalSourceId) continue;
|
|
@@ -1499,7 +1504,10 @@ async function materializeGoogleDriveKnowledgeSchedules(
|
|
|
1499
1504
|
task = await updateScheduledTask(deps.db, input.workspaceId, existingTask.id, {
|
|
1500
1505
|
name: `Sync Google Drive: ${selectedSource.name}`,
|
|
1501
1506
|
overlapPolicy: "buffer_one",
|
|
1502
|
-
action,
|
|
1507
|
+
action: { kind: "agent_turn" },
|
|
1508
|
+
agentConfig: knowledgeSourceAgentConfig(action, existingTask.agentConfig),
|
|
1509
|
+
refreshPersonalResourceAuthority: true,
|
|
1510
|
+
authorityUpdatedBy: { kind: "subject", subjectId: input.subjectId },
|
|
1503
1511
|
metadata: {
|
|
1504
1512
|
...existingTask.metadata,
|
|
1505
1513
|
connectorKind: "google_drive",
|
|
@@ -1531,16 +1539,11 @@ async function materializeGoogleDriveKnowledgeSchedules(
|
|
|
1531
1539
|
status: "active",
|
|
1532
1540
|
schedule,
|
|
1533
1541
|
overlapPolicy: "buffer_one",
|
|
1534
|
-
action,
|
|
1542
|
+
action: { kind: "agent_turn" },
|
|
1535
1543
|
runMode: "new_session_per_run",
|
|
1536
1544
|
targetSessionId: null,
|
|
1537
1545
|
connectionAuthorities: [],
|
|
1538
|
-
agentConfig:
|
|
1539
|
-
prompt: "Knowledge source synchronization",
|
|
1540
|
-
resources: [],
|
|
1541
|
-
tools: [],
|
|
1542
|
-
metadata: {},
|
|
1543
|
-
},
|
|
1546
|
+
agentConfig: knowledgeSourceAgentConfig(action),
|
|
1544
1547
|
variableSetId: null,
|
|
1545
1548
|
environmentId: null,
|
|
1546
1549
|
rigId: null,
|
|
@@ -1565,11 +1568,11 @@ async function materializeGoogleDriveKnowledgeSchedules(
|
|
|
1565
1568
|
}
|
|
1566
1569
|
|
|
1567
1570
|
for (const task of connectionTasks) {
|
|
1568
|
-
if (task
|
|
1571
|
+
if (!scheduledTaskKnowledgeSource(task)) continue;
|
|
1569
1572
|
const resolved = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
1570
1573
|
accountId: input.accountId,
|
|
1571
1574
|
workspaceId: input.workspaceId,
|
|
1572
|
-
sourceId: task.
|
|
1575
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1573
1576
|
initiatingSubjectId: input.subjectId,
|
|
1574
1577
|
});
|
|
1575
1578
|
if (!resolved || enabledIds.has(resolved.source.externalSourceId)) continue;
|
|
@@ -1613,10 +1616,10 @@ export async function preflightKnowledgeSourceScheduleAuthorization(
|
|
|
1613
1616
|
input: { task: ScheduledTask; subjectId: string },
|
|
1614
1617
|
): Promise<void> {
|
|
1615
1618
|
const { task } = input;
|
|
1616
|
-
if (task
|
|
1619
|
+
if (!scheduledTaskKnowledgeSource(task)) return;
|
|
1617
1620
|
if (
|
|
1618
|
-
task.
|
|
1619
|
-
task.
|
|
1621
|
+
requireScheduledTaskKnowledgeSource(task).initiatingSubjectId !== input.subjectId ||
|
|
1622
|
+
requireScheduledTaskKnowledgeSource(task).connection.ownerSubjectId !== input.subjectId
|
|
1620
1623
|
) {
|
|
1621
1624
|
throw new HTTPException(403, {
|
|
1622
1625
|
message: "knowledge source schedule requires the exact initiating subject",
|
|
@@ -1642,10 +1645,10 @@ export async function revokeKnowledgeSourceScheduleAuthorization(
|
|
|
1642
1645
|
input: { task: ScheduledTask; subjectId: string },
|
|
1643
1646
|
): Promise<void> {
|
|
1644
1647
|
const { task } = input;
|
|
1645
|
-
if (task
|
|
1648
|
+
if (!scheduledTaskKnowledgeSource(task)) return;
|
|
1646
1649
|
if (
|
|
1647
|
-
task.
|
|
1648
|
-
task.
|
|
1650
|
+
requireScheduledTaskKnowledgeSource(task).initiatingSubjectId !== input.subjectId ||
|
|
1651
|
+
requireScheduledTaskKnowledgeSource(task).connection.ownerSubjectId !== input.subjectId
|
|
1649
1652
|
) {
|
|
1650
1653
|
throw new HTTPException(403, {
|
|
1651
1654
|
message: "knowledge source schedule requires the exact initiating subject",
|
|
@@ -1665,11 +1668,11 @@ export async function revokeKnowledgeSourceScheduleAuthorization(
|
|
|
1665
1668
|
taskId: task.id,
|
|
1666
1669
|
accountId: task.accountId,
|
|
1667
1670
|
workspaceId: task.workspaceId,
|
|
1668
|
-
connectionId: task.
|
|
1669
|
-
connectionVersion: task.
|
|
1670
|
-
sourceId: task.
|
|
1671
|
-
sourceLifecycleGeneration: task.
|
|
1672
|
-
sourceConfigGeneration: task.
|
|
1671
|
+
connectionId: requireScheduledTaskKnowledgeSource(task).connection.connectionId,
|
|
1672
|
+
connectionVersion: requireScheduledTaskKnowledgeSource(task).connection.connectionVersion,
|
|
1673
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1674
|
+
sourceLifecycleGeneration: requireScheduledTaskKnowledgeSource(task).sourceLifecycleGeneration,
|
|
1675
|
+
sourceConfigGeneration: requireScheduledTaskKnowledgeSource(task).sourceConfigGeneration,
|
|
1673
1676
|
externalSourceId,
|
|
1674
1677
|
subjectId: input.subjectId,
|
|
1675
1678
|
});
|
|
@@ -5,7 +5,7 @@ import { HTTPException } from "hono/http-exception";
|
|
|
5
5
|
/** Adapt only indexed portable components. Never silently drop required components. */
|
|
6
6
|
export function marketplacePlugin(url: string) {
|
|
7
7
|
const source = snapshot.sources.find((candidate) =>
|
|
8
|
-
candidate.entries.some((
|
|
8
|
+
candidate.entries.some((entry) => entry.sourceUrl === url),
|
|
9
9
|
);
|
|
10
10
|
const item = source?.entries.find((candidate) => candidate.sourceUrl === url);
|
|
11
11
|
if (!item || !source) return null;
|
|
@@ -375,7 +375,7 @@ export async function completeApiIntegrationProviderOAuth(
|
|
|
375
375
|
if (!providerScopesInclude(definition, token.scopes, state.authorizeScopes)) {
|
|
376
376
|
throw new ProviderOAuthCallbackError("scope_not_granted");
|
|
377
377
|
}
|
|
378
|
-
|
|
378
|
+
let grantedScopes = token.scopes.length > 0 ? token.scopes : state.authorizeScopes;
|
|
379
379
|
const identity = await verifyProviderIdentity(deps, definition, token);
|
|
380
380
|
if (
|
|
381
381
|
state.expectedProviderPrincipalId &&
|
|
@@ -415,6 +415,11 @@ export async function completeApiIntegrationProviderOAuth(
|
|
|
415
415
|
if (!refreshToken) {
|
|
416
416
|
throw new ProviderOAuthCallbackError("refresh_token_missing");
|
|
417
417
|
}
|
|
418
|
+
// Microsoft omits offline_access from access-token scopes. The new or
|
|
419
|
+
// previously verified refresh token proves this capability instead.
|
|
420
|
+
if (definition.provider.id === "microsoft") {
|
|
421
|
+
grantedScopes = uniqueStrings([...grantedScopes, "offline_access"]);
|
|
422
|
+
}
|
|
418
423
|
const credentialEncrypted = encryptEnvironmentValue(
|
|
419
424
|
key,
|
|
420
425
|
JSON.stringify({
|
|
@@ -926,7 +931,11 @@ function providerScopesInclude(
|
|
|
926
931
|
return value;
|
|
927
932
|
};
|
|
928
933
|
const granted = new Set(effective.map(normalize));
|
|
929
|
-
return definition.authentication.scopes.every(
|
|
934
|
+
return definition.authentication.scopes.every(
|
|
935
|
+
(scope) =>
|
|
936
|
+
(definition.provider.id === "microsoft" && normalize(scope) === "offline_access") ||
|
|
937
|
+
granted.has(normalize(scope)),
|
|
938
|
+
);
|
|
930
939
|
}
|
|
931
940
|
|
|
932
941
|
function providerOAuthReturnUrl(
|