@opengeni/api-router 3.0.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admission-trace.d.ts +8 -0
- package/dist/app.js +1 -1
- package/dist/auth/managed-auth-attempt-context.d.ts +2 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +2 -0
- package/dist/auth/managed-auth.d.ts +1 -0
- package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
- package/dist/{chunk-AJ62T5QN.js → chunk-E7TZFJ65.js} +22300 -21113
- package/dist/{chunk-AJ62T5QN.js.map → chunk-E7TZFJ65.js.map} +1 -1
- package/dist/connection-ownership.d.ts +1 -2
- package/dist/fatal-process-boundary.d.ts +1 -1
- package/dist/index.js +24 -6
- package/dist/index.js.map +1 -1
- package/dist/integrations/atlassian.d.ts +8 -3
- package/dist/integrations/fiken.d.ts +3 -1
- package/dist/integrations/github-lens-connect.d.ts +5 -65
- package/dist/integrations/google-drive.d.ts +2 -0
- package/dist/integrations/integration-commit-authority.d.ts +13 -0
- package/dist/integrations/oauth-client.d.ts +119 -11
- package/dist/integrations/oauth-profiles.d.ts +8 -13
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/capability-account-readiness.d.ts +7 -0
- package/dist/mcp/first-party-tool-permissions.d.ts +4 -0
- package/dist/mcp-oauth.d.ts +22 -1
- package/dist/routes/api-integrations.d.ts +1 -0
- package/dist/routes/codex.d.ts +28 -0
- package/dist/routes/managed-sign-in-methods.d.ts +6 -0
- package/dist/routes/organization-integration-policy.d.ts +3 -0
- package/dist/routes/sessions.d.ts +24 -2
- package/dist/scheduled-task-deletion.d.ts +3 -4
- package/dist/workspace-tool-gateway.d.ts +3 -1
- package/package.json +15 -15
- package/src/admission-trace.ts +14 -0
- package/src/app.ts +141 -94
- package/src/auth/managed-auth-attempt-context.ts +9 -0
- package/src/auth/managed-auth-session-adapter.ts +11 -0
- package/src/auth/managed-auth.ts +39 -2
- package/src/auth/managed-sign-in-notifications.ts +81 -0
- package/src/connection-ownership.ts +2 -9
- package/src/editable-artifact-production.ts +6 -0
- package/src/fatal-process-boundary.ts +17 -2
- package/src/index.ts +3 -4
- package/src/integrations/atlassian.ts +184 -76
- package/src/integrations/fiken.ts +57 -35
- package/src/integrations/github-app-connect.ts +51 -15
- package/src/integrations/github-lens-connect.ts +7 -28
- package/src/integrations/google-drive.ts +296 -125
- package/src/integrations/integration-commit-authority.ts +69 -0
- package/src/integrations/oauth-client.ts +231 -132
- package/src/integrations/oauth-profiles.ts +10 -45
- package/src/integrations/personal-github.ts +55 -31
- package/src/integrations/provider-oauth.ts +57 -82
- package/src/integrations/social-oauth.ts +83 -56
- package/src/mcp/capability-account-readiness.ts +17 -0
- package/src/mcp/editable-artifacts.ts +7 -2
- package/src/mcp/first-party-tool-permissions.ts +1 -0
- package/src/mcp/knowledge-entries.ts +25 -8
- package/src/mcp/server.ts +135 -43
- package/src/mcp-oauth.ts +336 -34
- package/src/routes/api-integrations.ts +116 -4
- package/src/routes/automations.ts +5 -13
- package/src/routes/billing.ts +52 -0
- package/src/routes/canonical-human-identities.ts +61 -0
- package/src/routes/capabilities.ts +51 -1
- package/src/routes/codex.ts +4 -2
- package/src/routes/connect.ts +90 -31
- package/src/routes/connections.ts +487 -307
- package/src/routes/files.ts +21 -12
- package/src/routes/github.ts +48 -19
- package/src/routes/insights.ts +17 -7
- package/src/routes/integration-facets.ts +66 -25
- package/src/routes/knowledge.ts +48 -1
- package/src/routes/managed-sign-in-methods.ts +433 -0
- package/src/routes/organization-integration-policy.ts +113 -0
- package/src/routes/organization-memberships.ts +22 -2
- package/src/routes/plugins.ts +42 -4
- package/src/routes/pr-review-github.ts +67 -57
- package/src/routes/pr-review.ts +74 -71
- package/src/routes/rigs.ts +32 -13
- package/src/routes/scheduled-tasks.ts +6 -6
- package/src/routes/sessions.ts +233 -11
- package/src/routes/skill-content.ts +19 -3
- package/src/routes/social.ts +24 -13
- package/src/sandbox/channel-a.ts +7 -1
- package/src/sandbox/rematerialize.ts +2 -1
- package/src/scheduled-task-deletion.ts +13 -3
- package/src/workspace-tool-gateway.ts +31 -31
- package/dist/connection-authority-owner.d.ts +0 -59
- package/dist/routes/connection-authorities.d.ts +0 -3
- package/dist/routes/host-mcp-bindings.d.ts +0 -5
- package/dist/routes/host-mcp-resolvers.d.ts +0 -3
- package/dist/routes/packs.d.ts +0 -3
- package/src/connection-authority-owner.ts +0 -61
- package/src/routes/connection-authorities.ts +0 -139
- package/src/routes/host-mcp-bindings.ts +0 -122
- package/src/routes/host-mcp-resolvers.ts +0 -41
- package/src/routes/packs.ts +0 -784
|
@@ -2,12 +2,14 @@ import {
|
|
|
2
2
|
AgentInstructionSaveRequest,
|
|
3
3
|
WorkspaceInstructionPolicyTarget,
|
|
4
4
|
KnowledgeEntryListRequest,
|
|
5
|
+
KnowledgeSavePreparationRequest,
|
|
5
6
|
KnowledgeEntrySaveRequest,
|
|
6
7
|
KnowledgeTaskNotePromotionRequest,
|
|
7
8
|
type AccessGrant,
|
|
8
9
|
} from "@opengeni/contracts";
|
|
9
10
|
import {
|
|
10
11
|
prepareKnowledgeFile,
|
|
12
|
+
prepareKnowledgeSave,
|
|
11
13
|
retainKnowledgeMessage,
|
|
12
14
|
searchKnowledgeEntries,
|
|
13
15
|
requireLiveAgentAttemptAuthorization,
|
|
@@ -131,11 +133,23 @@ export function registerKnowledgeEntryTools(
|
|
|
131
133
|
throw new Error("Pass exactly one existing confirmation target");
|
|
132
134
|
}),
|
|
133
135
|
);
|
|
136
|
+
server.registerTool(
|
|
137
|
+
"knowledge_prepare_save",
|
|
138
|
+
{
|
|
139
|
+
description:
|
|
140
|
+
"Before saving useful lasting Knowledge, find related published entries and unapproved proposals, and fetch the full authorized collection map with descriptions and parent IDs in one read-only call. First choose the destination: future behavior belongs in instruction_policy_save for short always-on workspace rules or skill_save for applicable procedures and preferences, not Knowledge. Supply a concise subject and the proposed fact, decision, requirement or incident. Search spans all authorized collections. Reuse an existing entry when unchanged; read its current content before a correction, preserve evidence, or create only when distinct. Pending matches are unapproved. Choose collections from this map in your writable scope. A collection may have both published and pending versions. If collections.complete is false, continue with collectionCursors=collections.nextCursors; never mistake a partial catalog for the whole map. Long descriptions explicitly report truncation and can be read with knowledge_get. This call saves and approves nothing.",
|
|
141
|
+
inputSchema: KnowledgeSavePreparationRequest.shape,
|
|
142
|
+
},
|
|
143
|
+
(input) =>
|
|
144
|
+
run((context) =>
|
|
145
|
+
prepareKnowledgeSave(deps.db, context, input, () => deps.getDocumentServices().embedder),
|
|
146
|
+
),
|
|
147
|
+
);
|
|
134
148
|
server.registerTool(
|
|
135
149
|
"knowledge_search",
|
|
136
150
|
{
|
|
137
151
|
description:
|
|
138
|
-
"Find Knowledge from retained sources
|
|
152
|
+
"Find Knowledge from useful findings and explicitly retained reference sources. Supporting chat/file evidence is excluded from ordinary published discovery; set includeEvidence=true only when searching that evidence deliberately. Use a concise subject or entity name first (for example Acme); omit scope to search all authorized scopes. If a query returns no entries, retry the key name alone in the same scope or browse groups before concluding the information is absent. Default view=published is accepted knowledge. Also search view=needs_review before creating or updating entries, to reuse pending findings and collections from earlier tasks. Pending revisions are unapproved proposals, not accepted facts or instructions; preserve that status when discussing them. Reading a proposal never approves it. Personal tasks search the verified user's personal and authorized shared Knowledge; shared tasks search shared Knowledge.",
|
|
139
153
|
inputSchema: KnowledgeEntryListRequest.omit({
|
|
140
154
|
view: true,
|
|
141
155
|
sessionId: true,
|
|
@@ -210,16 +224,19 @@ export function registerKnowledgeEntryTools(
|
|
|
210
224
|
"knowledge_retain_file",
|
|
211
225
|
{
|
|
212
226
|
description:
|
|
213
|
-
"Retain
|
|
214
|
-
inputSchema: {
|
|
227
|
+
"Retain source text from an existing uploaded file only when it supports useful lasting Knowledge, or the user wants the file kept as a reusable reference. Ordinary chat attachments are not automatically added to Knowledge. Use purpose=evidence (default) for supporting material, or purpose=reference for a deliberately retained searchable reference. The original stays in Files. Read the image itself before drawing visual conclusions; OCR text is only evidence. Repeated calls reuse the same source and do not change its purpose, duplicate review requests or revive rejected/archived sources. To make previously retained evidence a searchable reference, read its current revision with knowledge_get and use knowledge_save to set source.purpose=reference while preserving content and links. Selected findings are optional separate knowledge_save entries with evidence pointing to the returned revision. A pending receipt never pauses the task.",
|
|
228
|
+
inputSchema: {
|
|
229
|
+
fileId: z.uuid(),
|
|
230
|
+
purpose: z.enum(["evidence", "reference"]).default("evidence"),
|
|
231
|
+
},
|
|
215
232
|
},
|
|
216
|
-
({ fileId }) => run((context) => prepareKnowledgeFile(deps, context, fileId)),
|
|
233
|
+
({ fileId, purpose }) => run((context) => prepareKnowledgeFile(deps, context, fileId, purpose)),
|
|
217
234
|
);
|
|
218
235
|
server.registerTool(
|
|
219
236
|
"knowledge_retain_message",
|
|
220
237
|
{
|
|
221
238
|
description:
|
|
222
|
-
"Retain the exact text of a user message
|
|
239
|
+
"Retain the exact text of a user message as supporting evidence only after identifying useful lasting Knowledge to save. Do not retain routine approvals, acknowledgments, task instructions or status chatter merely because they occurred. The source stays out of ordinary Knowledge discovery and can be read through its evidence link. Use when a user supplies or confirms a durable fact, before saving a finding based on it. Omit messageId for the user message that triggered this turn, or pass an earlier user message event ID from this same conversation. The server copies the actual message, preserves its identity and follows this task's learning policy. Repeated calls reuse the source. Cite the returned entryId/revisionId as evidence in knowledge_save, with location.messageIds=[messageId]. Retaining a message does not prove every statement in it or approve a pending finding.",
|
|
223
240
|
inputSchema: { messageId: z.uuid().optional() },
|
|
224
241
|
},
|
|
225
242
|
({ messageId }) => run((context) => retainKnowledgeMessage(deps.db, context, messageId)),
|
|
@@ -228,7 +245,7 @@ export function registerKnowledgeEntryTools(
|
|
|
228
245
|
"knowledge_save",
|
|
229
246
|
{
|
|
230
247
|
description:
|
|
231
|
-
"Retain
|
|
248
|
+
"Retain retrieval-only facts, decisions, requirements, incidents or notes, or deliberately selected reference sources, not a behavioral preference or instruction for future sessions. Use instruction_policy_save for short always-on workspace rules and skill_save for applicable procedures and behavioral preferences; do not store them here as facts such as 'the user prefers concise replies', even if those destinations are unavailable or require review. First use knowledge_prepare_save when available (otherwise search published and needs_review Knowledge and browse collections). Skip unchanged duplicates; improve an existing entry when the subject is the same. Do not save routine approvals, acknowledgments, temporary instructions, screenshots or OCR merely because they occurred. State the reusable finding with enough context for a future task, preserve uncertainty and evidence, and place it in the relevant existing collections. Do this autonomously when useful for future work. Choose kind by content: source for retained original text; fact for a specific claim (the label is not verification); decision for a choice made and its reasoning; requirement for a need or constraint; incident for a problem with cause, fix and outcome when known; note for other useful context. Choose the closest kind without asking the user to classify it. A group is a collection about a customer, product, system or subject, not a finding type. Search published and needs_review views for an existing entry and relevant group before creating one, use groupIds to link entries across sources, and reuse an entry in multiple groups instead of copying it. Nest collections by setting a group entry's groupIds to its parent collections; keep the hierarchy shallow and useful, and never create circular membership. Use a new entryId and expectedVersion 0 to create; use an existing ID and its current version to correct or reorganize. Source text and selected facts are independent entries, not mandatory duplicate stages. Evidence pins another entry's exact revision. When a user supplies or confirms a fact, retain the actual message with knowledge_retain_message and cite the returned revision; do not leave its provenance only in prose. Preserve existing evidence and relationships on corrections unless they are specifically no longer applicable; a contradictory original remains useful evidence of what changed. Use the same operationId only for an exact retry. Agent learning decides publication: published is available immediately; pending is saved for review and your task continues without an approval prompt. Do not turn Knowledge into instructions or Skills.",
|
|
232
249
|
inputSchema: KnowledgeEntrySaveRequest.omit({ scope: true }).shape,
|
|
233
250
|
},
|
|
234
251
|
(input) => run((context) => saveKnowledgeEntry(deps.db, context, input)),
|
|
@@ -259,7 +276,7 @@ export function registerKnowledgeEntryTools(
|
|
|
259
276
|
"instruction_policy_save",
|
|
260
277
|
{
|
|
261
278
|
description:
|
|
262
|
-
"Change a concise standing workspace instruction through this task's Agent learning policy. Read the current policy first and submit its exact baseline. Use editMode=append by default for a new rule; it preserves the current content and adds one blank-line separator. Use edit with a localized oldText that occurs exactly once and newText to update or remove only that passage. Agents cannot replace the complete instruction; direct the user to the manual workspace-instruction editor for a whole-policy rewrite. Each supplied text and the resulting instruction are limited to 600 characters. Facts and incidents belong in knowledge_save and reusable procedures in skill_save. Review first saves an inactive revision and returns pending; continue the task without an approval question.",
|
|
279
|
+
"Change a concise standing workspace instruction through this task's Agent learning policy. Use for short always-on behavior such as 'Keep replies concise; expand when asked', within the intended workspace scope; active instructions enter applicable prompts without Knowledge retrieval. Read the current policy first and submit its exact baseline. Use editMode=append by default for a new rule; it preserves the current content and adds one blank-line separator. Use edit with a localized oldText that occurs exactly once and newText to update or remove only that passage. Agents cannot replace the complete instruction; direct the user to the manual workspace-instruction editor for a whole-policy rewrite. Each supplied text and the resulting instruction are limited to 600 characters. Facts and incidents belong in knowledge_save and reusable procedures or context-specific preferences in skill_save. Preserve personal scope rather than creating a workspace-wide rule. Review first saves an inactive revision and returns pending; continue the task without an approval question. Report the actual receipt; Knowledge is not a workaround for Off, pending review, unavailable scope or size limits.",
|
|
263
280
|
inputSchema: AgentInstructionSaveRequest.shape,
|
|
264
281
|
},
|
|
265
282
|
(input) => run((context) => saveAgentInstruction(deps.db, context, input), "instruction"),
|
|
@@ -268,7 +285,7 @@ export function registerKnowledgeEntryTools(
|
|
|
268
285
|
"task_note_promote_knowledge",
|
|
269
286
|
{
|
|
270
287
|
description:
|
|
271
|
-
"Retain one active task note as durable Knowledge with its exact text and origin. The note stays temporary and unchanged; this creates one Knowledge entry in this task's scope, governed by the same learning policy. Pending review never pauses the task. Use knowledge_save for a selected or rewritten finding instead.",
|
|
288
|
+
"Retain one active task note as durable Knowledge with its exact text and origin, only for retrieval-only information. Do not promote behavior rules or preferences to Knowledge; use instruction_policy_save for short always-on workspace rules or skill_save for applicable procedures and preferences, under their own scope and learning policy. The note stays temporary and unchanged; this creates one Knowledge entry in this task's scope, governed by the same learning policy. Pending review never pauses the task. Use knowledge_save for a selected or rewritten finding instead.",
|
|
272
289
|
inputSchema: KnowledgeTaskNotePromotionRequest.shape,
|
|
273
290
|
},
|
|
274
291
|
(input) => run((context) => promoteTaskNoteToKnowledge(deps.db, context, input)),
|
package/src/mcp/server.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
setSessionChannel,
|
|
10
10
|
} from "@opengeni/db";
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
|
+
import { capabilityAccountReadiness } from "./capability-account-readiness";
|
|
12
13
|
import {
|
|
13
14
|
prepareWorkspaceArtifactUpload,
|
|
14
15
|
prepareWorkspaceArtifactPublication,
|
|
@@ -18,6 +19,8 @@ import {
|
|
|
18
19
|
CreateScheduledTaskRequest,
|
|
19
20
|
CreateSessionRequest,
|
|
20
21
|
GoalSpec,
|
|
22
|
+
SessionGoalReportRequirements,
|
|
23
|
+
SessionGoalReportDeliveries,
|
|
21
24
|
boundSessionMcpText as capSessionDiscoveryText,
|
|
22
25
|
compactSessionMcpListRow,
|
|
23
26
|
sessionMcpIncludesRelatedWork,
|
|
@@ -92,6 +95,7 @@ import {
|
|
|
92
95
|
getSessionMcpMonitoringSummary,
|
|
93
96
|
getSessionQueueSnapshot,
|
|
94
97
|
getSessionTurn,
|
|
98
|
+
getSessionTurnMcpAccountBindings,
|
|
95
99
|
getOrCreatePreferenceRegistrySnapshot,
|
|
96
100
|
getPreferenceRegistryFullContent,
|
|
97
101
|
getVariableSet,
|
|
@@ -160,6 +164,7 @@ import type { AnySchema, ZodRawShapeCompat } from "@modelcontextprotocol/sdk/ser
|
|
|
160
164
|
import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
161
165
|
import { HTTPException } from "hono/http-exception";
|
|
162
166
|
import * as z4 from "zod/v4";
|
|
167
|
+
import { editableArtifactActorForGrant } from "../routes/editable-artifacts";
|
|
163
168
|
import { registerKnowledgeEntryTools } from "./knowledge-entries";
|
|
164
169
|
import {
|
|
165
170
|
FIRST_PARTY_TOOL_AUTHORIZATION,
|
|
@@ -230,6 +235,7 @@ import {
|
|
|
230
235
|
validateScheduledTaskMachineTarget,
|
|
231
236
|
validateScheduledTaskTarget,
|
|
232
237
|
updateScheduledTaskForApi,
|
|
238
|
+
triggerScheduledTaskForGrant,
|
|
233
239
|
validatedScheduledTaskUpdate,
|
|
234
240
|
} from "@opengeni/core";
|
|
235
241
|
import {
|
|
@@ -797,8 +803,7 @@ export function buildOpenGeniMcpServer(
|
|
|
797
803
|
server.registerTool(
|
|
798
804
|
"social_connections_list",
|
|
799
805
|
{
|
|
800
|
-
description:
|
|
801
|
-
"List connected social media accounts available to social media analysis packs.",
|
|
806
|
+
description: "List connected social media accounts available for analysis.",
|
|
802
807
|
inputSchema: { limit: z4.number().int().positive().optional() },
|
|
803
808
|
},
|
|
804
809
|
async ({ limit }) =>
|
|
@@ -1001,7 +1006,7 @@ export function buildOpenGeniMcpServer(
|
|
|
1001
1006
|
|
|
1002
1007
|
// Provider-scoped aliases are the canonical tools advertised by the X and
|
|
1003
1008
|
// Reddit Integration cards. The legacy social_* names remain available to
|
|
1004
|
-
|
|
1009
|
+
|
|
1005
1010
|
// identity and reject a near-identical Connection from the other adapter.
|
|
1006
1011
|
for (const provider of ["x", "reddit"] as const) {
|
|
1007
1012
|
const providerName = provider === "x" ? "X" : "Reddit";
|
|
@@ -1379,7 +1384,7 @@ export function buildOpenGeniMcpServer(
|
|
|
1379
1384
|
status: z4.string().optional(),
|
|
1380
1385
|
// Explicit credential-free connection authority selections; declared
|
|
1381
1386
|
// so MCP validation doesn't strip them before the contract parse.
|
|
1382
|
-
|
|
1387
|
+
connectionAccounts: z4.array(z4.unknown()).optional(),
|
|
1383
1388
|
variableSetId: z4.string().uuid().optional(),
|
|
1384
1389
|
// Deprecated alias of variableSetId; declared so MCP validation doesn't
|
|
1385
1390
|
// strip it before the contract parse maps it (rename back-compat).
|
|
@@ -1446,7 +1451,7 @@ export function buildOpenGeniMcpServer(
|
|
|
1446
1451
|
status: z4.string().optional(),
|
|
1447
1452
|
// Omitted preserves the frozen selections, [] clears them, and an
|
|
1448
1453
|
// array replaces them; declared so MCP validation doesn't strip it.
|
|
1449
|
-
|
|
1454
|
+
connectionAccounts: z4.array(z4.unknown()).optional(),
|
|
1450
1455
|
variableSetId: z4.string().uuid().nullable().optional(),
|
|
1451
1456
|
// Deprecated alias of variableSetId (rename back-compat); declared so MCP
|
|
1452
1457
|
// validation doesn't strip it before the contract parse maps it.
|
|
@@ -1475,7 +1480,7 @@ export function buildOpenGeniMcpServer(
|
|
|
1475
1480
|
if (!scheduledTaskUpdateChangesState(existing, update)) {
|
|
1476
1481
|
return json(scheduledTaskReceipt("scheduled_tasks_update", existing, "unchanged", false));
|
|
1477
1482
|
}
|
|
1478
|
-
const task = await updateScheduledTaskForApi(deps.db, grant
|
|
1483
|
+
const task = await updateScheduledTaskForApi(deps.db, grant, id, update);
|
|
1479
1484
|
await syncUpdatedScheduledTask({
|
|
1480
1485
|
db: deps.db,
|
|
1481
1486
|
workflowClient: deps.workflowClient,
|
|
@@ -1498,7 +1503,7 @@ export function buildOpenGeniMcpServer(
|
|
|
1498
1503
|
return json(scheduledTaskReceipt("scheduled_tasks_pause", existing, "unchanged", false));
|
|
1499
1504
|
}
|
|
1500
1505
|
const previous = await captureScheduledTaskRestoreState(deps.db, existing);
|
|
1501
|
-
const task = await updateScheduledTaskForApi(deps.db, grant
|
|
1506
|
+
const task = await updateScheduledTaskForApi(deps.db, grant, id, {
|
|
1502
1507
|
status: "paused",
|
|
1503
1508
|
});
|
|
1504
1509
|
await syncUpdatedScheduledTask({
|
|
@@ -1533,7 +1538,7 @@ export function buildOpenGeniMcpServer(
|
|
|
1533
1538
|
sessionAuthorization: deps.sessionAuthorization,
|
|
1534
1539
|
authorizationSurface: "first_party_mcp",
|
|
1535
1540
|
});
|
|
1536
|
-
const task = await updateScheduledTaskForApi(deps.db, grant
|
|
1541
|
+
const task = await updateScheduledTaskForApi(deps.db, grant, id, update);
|
|
1537
1542
|
await syncUpdatedScheduledTask({
|
|
1538
1543
|
db: deps.db,
|
|
1539
1544
|
workflowClient: deps.workflowClient,
|
|
@@ -1599,7 +1604,7 @@ export function buildOpenGeniMcpServer(
|
|
|
1599
1604
|
? manualScheduledTaskTriggerUsageKey(grant.workspaceId, task.id, triggerToken)
|
|
1600
1605
|
: `knowledge-source-sync:manual:${grant.workspaceId}:${task.id}:${triggerToken}`;
|
|
1601
1606
|
const triggerWorkflowId = manualScheduledTaskTriggerWorkflowId(task.id, triggerToken);
|
|
1602
|
-
await deps.workflowClient
|
|
1607
|
+
await triggerScheduledTaskForGrant(deps.db, grant, deps.workflowClient, {
|
|
1603
1608
|
task,
|
|
1604
1609
|
agentRunUsageIdempotencyKey,
|
|
1605
1610
|
triggerWorkflowId,
|
|
@@ -1654,9 +1659,8 @@ export function buildOpenGeniMcpServer(
|
|
|
1654
1659
|
},
|
|
1655
1660
|
async ({ id }) => {
|
|
1656
1661
|
const { task, changed } = await deleteScheduledTaskWithDurableCleanup(deps, {
|
|
1657
|
-
|
|
1662
|
+
grant,
|
|
1658
1663
|
taskId: id,
|
|
1659
|
-
subjectId: grant.subjectId,
|
|
1660
1664
|
});
|
|
1661
1665
|
return json(
|
|
1662
1666
|
mcpMutationReceipt({
|
|
@@ -2254,7 +2258,20 @@ function registerAtlassianTools(
|
|
|
2254
2258
|
const authority = candidates[0]!;
|
|
2255
2259
|
const metadata = AtlassianConnectionMetadata.safeParse(authority.connection.metadata);
|
|
2256
2260
|
if (!metadata.success) throw new Error("Atlassian connection metadata is invalid");
|
|
2261
|
+
const claims = exactAgentAttemptClaims(grant);
|
|
2262
|
+
if (grant.principalKind === "agent_attempt" && !claims) {
|
|
2263
|
+
throw new Error("Atlassian access requires the exact active agent attempt");
|
|
2264
|
+
}
|
|
2257
2265
|
return {
|
|
2266
|
+
...(claims
|
|
2267
|
+
? {
|
|
2268
|
+
connectionUseContext: {
|
|
2269
|
+
...claims,
|
|
2270
|
+
accountId: grant.accountId,
|
|
2271
|
+
workspaceId: grant.workspaceId,
|
|
2272
|
+
},
|
|
2273
|
+
}
|
|
2274
|
+
: {}),
|
|
2258
2275
|
connection: authority.connection,
|
|
2259
2276
|
metadata: metadata.data,
|
|
2260
2277
|
subjectId: authority.subjectId ?? grant.subjectId,
|
|
@@ -2271,7 +2288,10 @@ function registerAtlassianTools(
|
|
|
2271
2288
|
async ({ connectionId }) => {
|
|
2272
2289
|
const authority = await connectionFor(connectionId);
|
|
2273
2290
|
const response = await browseAtlassianSources(deps, {
|
|
2274
|
-
workspaceId:
|
|
2291
|
+
workspaceId: authority.connection.workspaceId,
|
|
2292
|
+
...(authority.connectionUseContext
|
|
2293
|
+
? { connectionUseContext: authority.connectionUseContext }
|
|
2294
|
+
: {}),
|
|
2275
2295
|
subjectId: authority.subjectId,
|
|
2276
2296
|
connectionId: authority.connection.id,
|
|
2277
2297
|
});
|
|
@@ -2304,7 +2324,10 @@ function registerAtlassianTools(
|
|
|
2304
2324
|
return json({
|
|
2305
2325
|
connectionId: authority.connection.id,
|
|
2306
2326
|
results: await searchAtlassianLive(deps, {
|
|
2307
|
-
workspaceId:
|
|
2327
|
+
workspaceId: authority.connection.workspaceId,
|
|
2328
|
+
...(authority.connectionUseContext
|
|
2329
|
+
? { connectionUseContext: authority.connectionUseContext }
|
|
2330
|
+
: {}),
|
|
2308
2331
|
subjectId: authority.subjectId,
|
|
2309
2332
|
connectionId: authority.connection.id,
|
|
2310
2333
|
query,
|
|
@@ -2330,7 +2353,10 @@ function registerAtlassianTools(
|
|
|
2330
2353
|
const authority = await connectionFor(connectionId);
|
|
2331
2354
|
return json(
|
|
2332
2355
|
await getAtlassianLiveItem(deps, {
|
|
2333
|
-
workspaceId:
|
|
2356
|
+
workspaceId: authority.connection.workspaceId,
|
|
2357
|
+
...(authority.connectionUseContext
|
|
2358
|
+
? { connectionUseContext: authority.connectionUseContext }
|
|
2359
|
+
: {}),
|
|
2334
2360
|
subjectId: authority.subjectId,
|
|
2335
2361
|
connectionId: authority.connection.id,
|
|
2336
2362
|
kind,
|
|
@@ -2420,7 +2446,7 @@ function registerGoalTools(
|
|
|
2420
2446
|
"goal_set",
|
|
2421
2447
|
{
|
|
2422
2448
|
description:
|
|
2423
|
-
"Create a goal when this session has none, or replace a completed goal with a new one. While active, idle moments synthesize continuation turns until goal_complete or goal_pause. To change an active or paused goal, use goal_update with its objective revision, a change kind, and rationale.",
|
|
2449
|
+
"Create a goal when this session has none, or replace a completed goal with a new one. Declare user-facing native document reports with reportRequirements before producing them. While active, idle moments synthesize continuation turns until goal_complete or goal_pause. To change an active or paused goal, use goal_update with its objective revision, a change kind, and rationale.",
|
|
2424
2450
|
// `maxAutoContinuations` is deliberately not agent-facing: the ceiling is
|
|
2425
2451
|
// API/scheduled-task pacing configuration, and an agent that set its own
|
|
2426
2452
|
// cap used to silence its orchestration for hours. Continuation pacing is
|
|
@@ -2428,9 +2454,10 @@ function registerGoalTools(
|
|
|
2428
2454
|
inputSchema: {
|
|
2429
2455
|
text: goalText,
|
|
2430
2456
|
successCriteria: successCriteriaSchema.optional(),
|
|
2457
|
+
reportRequirements: SessionGoalReportRequirements.optional(),
|
|
2431
2458
|
},
|
|
2432
2459
|
},
|
|
2433
|
-
async ({ text, successCriteria }) => {
|
|
2460
|
+
async ({ text, successCriteria, reportRequirements }) => {
|
|
2434
2461
|
await authorizeFirstPartySession(deps, grant, sessionId, "session.goal.write");
|
|
2435
2462
|
await requireSession(deps.db, grant.workspaceId, sessionId);
|
|
2436
2463
|
const existing = await getSessionGoal(deps.db, grant.workspaceId, sessionId);
|
|
@@ -2444,15 +2471,24 @@ function registerGoalTools(
|
|
|
2444
2471
|
? (grant.metadata["turnId"] as string)
|
|
2445
2472
|
: null;
|
|
2446
2473
|
await assertGoalReactivationAllowed(deps, grant.workspaceId, sessionId, callerTurnId);
|
|
2474
|
+
const context = exactAgentCommandContext(grant, sessionId);
|
|
2447
2475
|
const { goal, replaced, events } = await upsertSessionGoalWithEvent(deps.db, {
|
|
2448
2476
|
accountId: grant.accountId,
|
|
2449
2477
|
workspaceId: grant.workspaceId,
|
|
2450
2478
|
sessionId,
|
|
2451
2479
|
text,
|
|
2452
2480
|
successCriteria: successCriteria ?? null,
|
|
2481
|
+
...(reportRequirements !== undefined ? { reportRequirements } : {}),
|
|
2453
2482
|
maxAutoContinuations: null,
|
|
2454
2483
|
createdBy: "agent",
|
|
2455
2484
|
actor: "agent",
|
|
2485
|
+
commandActor: {
|
|
2486
|
+
type: "agent_attempt",
|
|
2487
|
+
sessionId: context.callerSessionId,
|
|
2488
|
+
turnId: context.callerTurnId,
|
|
2489
|
+
attemptId: context.callerAttemptId,
|
|
2490
|
+
executionGeneration: context.callerExecutionGeneration,
|
|
2491
|
+
},
|
|
2456
2492
|
});
|
|
2457
2493
|
if (events.length > 0) {
|
|
2458
2494
|
await deps.bus.publish(grant.workspaceId, sessionId, events);
|
|
@@ -2540,13 +2576,14 @@ function registerGoalTools(
|
|
|
2540
2576
|
"goal_progress",
|
|
2541
2577
|
{
|
|
2542
2578
|
description:
|
|
2543
|
-
"Record concrete progress toward the unchanged active goal. This does not change goal text, success criteria, mutation policy, or objective revision. Do not use it merely to keep the continuation loop alive.",
|
|
2579
|
+
"Record concrete progress toward the unchanged active goal. Optionally append reportRequirements for secondary user-facing reports discovered during other work; existing requirement IDs and titles cannot be changed or removed. This does not change goal text, success criteria, mutation policy, or objective revision. Do not use it merely to keep the continuation loop alive.",
|
|
2544
2580
|
inputSchema: {
|
|
2545
2581
|
progressNote: progressNoteSchema,
|
|
2546
2582
|
idempotencyKey: z4.string().uuid(),
|
|
2583
|
+
reportRequirements: SessionGoalReportRequirements.optional(),
|
|
2547
2584
|
},
|
|
2548
2585
|
},
|
|
2549
|
-
async ({ progressNote, idempotencyKey }) => {
|
|
2586
|
+
async ({ progressNote, idempotencyKey, reportRequirements }) => {
|
|
2550
2587
|
await authorizeFirstPartySession(deps, grant, sessionId, "session.goal.write");
|
|
2551
2588
|
const context = exactAgentCommandContext(grant, sessionId);
|
|
2552
2589
|
const { goal, events, operationId, replay } = await recordSessionGoalProgressWithEvent(
|
|
@@ -2555,6 +2592,7 @@ function registerGoalTools(
|
|
|
2555
2592
|
sessionId,
|
|
2556
2593
|
{
|
|
2557
2594
|
progressNote,
|
|
2595
|
+
...(reportRequirements !== undefined ? { reportRequirements } : {}),
|
|
2558
2596
|
command: {
|
|
2559
2597
|
accountId: grant.accountId,
|
|
2560
2598
|
actor: {
|
|
@@ -2635,16 +2673,23 @@ function registerGoalTools(
|
|
|
2635
2673
|
"goal_complete",
|
|
2636
2674
|
{
|
|
2637
2675
|
description:
|
|
2638
|
-
"Mark the session goal as completed
|
|
2639
|
-
inputSchema: {
|
|
2676
|
+
"Mark the session goal as completed with concrete evidence. Every persisted report requirement must have a matching reportDeliveries entry containing a native document artifactId and its server-issued inspectionReceiptId from a post-edit body inspection. Missing, stale, inaccessible or summary-only proof fails; inspect again after an edit. Omit reportDeliveries only when no reports were declared. Successful completion returns report artifact references and prevents further continuation turns.",
|
|
2677
|
+
inputSchema: {
|
|
2678
|
+
evidence: z4.string().min(1),
|
|
2679
|
+
reportDeliveries: SessionGoalReportDeliveries.optional(),
|
|
2680
|
+
},
|
|
2640
2681
|
},
|
|
2641
|
-
async ({ evidence }) => {
|
|
2682
|
+
async ({ evidence, reportDeliveries }) => {
|
|
2642
2683
|
await authorizeFirstPartySession(deps, grant, sessionId, "session.goal.write");
|
|
2643
2684
|
await requireSession(deps.db, grant.workspaceId, sessionId);
|
|
2644
2685
|
const existing = await getSessionGoal(deps.db, grant.workspaceId, sessionId);
|
|
2645
2686
|
if (!existing) {
|
|
2646
2687
|
throw new Error("this session has no goal; use goal_set first");
|
|
2647
2688
|
}
|
|
2689
|
+
const context = exactAgentCommandContext(grant, sessionId);
|
|
2690
|
+
const reportArtifactActor = editableArtifactActorForGrant(grant, "0000000000000001");
|
|
2691
|
+
if (reportArtifactActor.kind !== "agent")
|
|
2692
|
+
throw new Error("Goal completion requires exact agent authority");
|
|
2648
2693
|
const { goal, events } = await setSessionGoalStatusWithEvent(
|
|
2649
2694
|
deps.db,
|
|
2650
2695
|
grant.workspaceId,
|
|
@@ -2652,6 +2697,15 @@ function registerGoalTools(
|
|
|
2652
2697
|
{
|
|
2653
2698
|
status: "completed",
|
|
2654
2699
|
evidence,
|
|
2700
|
+
...(reportDeliveries !== undefined ? { reportDeliveries } : {}),
|
|
2701
|
+
reportArtifactActor,
|
|
2702
|
+
commandActor: {
|
|
2703
|
+
type: "agent_attempt",
|
|
2704
|
+
sessionId: context.callerSessionId,
|
|
2705
|
+
turnId: context.callerTurnId,
|
|
2706
|
+
attemptId: context.callerAttemptId,
|
|
2707
|
+
executionGeneration: context.callerExecutionGeneration,
|
|
2708
|
+
},
|
|
2655
2709
|
event: { type: "goal.completed", evidence },
|
|
2656
2710
|
},
|
|
2657
2711
|
);
|
|
@@ -2659,8 +2713,8 @@ function registerGoalTools(
|
|
|
2659
2713
|
if (events.length > 0) {
|
|
2660
2714
|
await deps.bus.publish(grant.workspaceId, sessionId, events);
|
|
2661
2715
|
}
|
|
2662
|
-
return json(
|
|
2663
|
-
mcpMutationReceipt({
|
|
2716
|
+
return json({
|
|
2717
|
+
...mcpMutationReceipt({
|
|
2664
2718
|
operation: "goal_complete",
|
|
2665
2719
|
committed: true,
|
|
2666
2720
|
outcome: changed ? "updated" : "unchanged",
|
|
@@ -2675,7 +2729,13 @@ function registerGoalTools(
|
|
|
2675
2729
|
idempotency: { status: "not_supported" },
|
|
2676
2730
|
nextAction: { tool: "session_get", arguments: { sessionId } },
|
|
2677
2731
|
}),
|
|
2678
|
-
|
|
2732
|
+
reportDeliveries: SessionGoalReportDeliveries.parse(
|
|
2733
|
+
goal.metadata.reportDeliveriesV1 ?? [],
|
|
2734
|
+
).map((delivery) => ({
|
|
2735
|
+
...delivery,
|
|
2736
|
+
artifactReference: `[Open report](/workspaces/${grant.workspaceId}/artifacts/editable/${delivery.artifactId})`,
|
|
2737
|
+
})),
|
|
2738
|
+
});
|
|
2679
2739
|
},
|
|
2680
2740
|
);
|
|
2681
2741
|
|
|
@@ -3559,10 +3619,6 @@ function scheduledTaskUpdateChangesState(
|
|
|
3559
3619
|
if (update.metadata !== undefined && stableJson(update.metadata) !== stableJson(task.metadata)) {
|
|
3560
3620
|
return true;
|
|
3561
3621
|
}
|
|
3562
|
-
// Personal-connection delegations are recomputed with agentConfig and can
|
|
3563
|
-
// change even when the visible config is byte-identical (for example after a
|
|
3564
|
-
// connection rotation), so preserve that refresh as a real mutation.
|
|
3565
|
-
if (update.personalConnectionDelegations !== undefined) return true;
|
|
3566
3622
|
return false;
|
|
3567
3623
|
}
|
|
3568
3624
|
|
|
@@ -3843,7 +3899,7 @@ function registerRigTools(
|
|
|
3843
3899
|
const rig = await requireRigForApi(deps.db, await resourceGrant(), rigId);
|
|
3844
3900
|
if (rig.scope === "organization") {
|
|
3845
3901
|
throw new Error(
|
|
3846
|
-
"Organization
|
|
3902
|
+
"Organization sandbox environment mutation requires account-admin authority through the authenticated REST surface.",
|
|
3847
3903
|
);
|
|
3848
3904
|
}
|
|
3849
3905
|
return rig;
|
|
@@ -3852,7 +3908,7 @@ function registerRigTools(
|
|
|
3852
3908
|
server.registerTool(
|
|
3853
3909
|
"rig_list",
|
|
3854
3910
|
{
|
|
3855
|
-
description: "List workspace
|
|
3911
|
+
description: "List workspace sandbox environments and their active versions.",
|
|
3856
3912
|
inputSchema: {},
|
|
3857
3913
|
},
|
|
3858
3914
|
async () => json({ rigs: await listRigs(deps.db, await resourceGrant()) }),
|
|
@@ -3862,7 +3918,7 @@ function registerRigTools(
|
|
|
3862
3918
|
"rig_get",
|
|
3863
3919
|
{
|
|
3864
3920
|
description:
|
|
3865
|
-
"Get one
|
|
3921
|
+
"Get one sandbox environment's bounded active definition plus compact historical version/change summaries. Historical setup scripts, checks, payloads, and verification logs are represented by counts/byte facts rather than copied into model context; use the access-controlled REST detail endpoints for exact retained definitions.",
|
|
3866
3922
|
inputSchema: {
|
|
3867
3923
|
rigId: z4.string().uuid(),
|
|
3868
3924
|
versionLimit: z4.number().int().positive().optional(),
|
|
@@ -3893,7 +3949,7 @@ function registerRigTools(
|
|
|
3893
3949
|
"rig_propose_change",
|
|
3894
3950
|
{
|
|
3895
3951
|
description:
|
|
3896
|
-
"Propose an additive
|
|
3952
|
+
"Propose an additive sandbox environment setup command for clean verification. Use the exact command that already worked in this sandbox.",
|
|
3897
3953
|
inputSchema: {
|
|
3898
3954
|
rigId: z4.string().uuid(),
|
|
3899
3955
|
command: z4.string().min(1).max(8192),
|
|
@@ -3942,7 +3998,7 @@ function registerRigTools(
|
|
|
3942
3998
|
retryable: true,
|
|
3943
3999
|
},
|
|
3944
4000
|
warnings: [
|
|
3945
|
-
"The
|
|
4001
|
+
"The sandbox environment change and verifying transition committed, but verification workflow start failed.",
|
|
3946
4002
|
],
|
|
3947
4003
|
facts: { verificationAttempt: attempt },
|
|
3948
4004
|
nextAction: { tool: "rig_get", arguments: { rigId: rig.id } },
|
|
@@ -3975,7 +4031,7 @@ function registerRigTools(
|
|
|
3975
4031
|
"rig_verify",
|
|
3976
4032
|
{
|
|
3977
4033
|
description:
|
|
3978
|
-
"Trigger
|
|
4034
|
+
"Trigger sandbox environment verification. Pass changeId for a proposed change, or omit it to re-verify the active version's checks.",
|
|
3979
4035
|
inputSchema: {
|
|
3980
4036
|
rigId: z4.string().uuid(),
|
|
3981
4037
|
changeId: z4.string().uuid().optional(),
|
|
@@ -4045,7 +4101,7 @@ function registerRigTools(
|
|
|
4045
4101
|
);
|
|
4046
4102
|
}
|
|
4047
4103
|
if (!rig.activeVersion) {
|
|
4048
|
-
throw new Error("
|
|
4104
|
+
throw new Error("sandbox environment has no active version");
|
|
4049
4105
|
}
|
|
4050
4106
|
await deps.workflowClient.startRigVerification({
|
|
4051
4107
|
workspaceId: rig.workspaceId,
|
|
@@ -4078,7 +4134,7 @@ function registerRigTools(
|
|
|
4078
4134
|
"rig_promote",
|
|
4079
4135
|
{
|
|
4080
4136
|
description:
|
|
4081
|
-
"Promote a verified definition_edit
|
|
4137
|
+
"Promote a verified definition_edit sandbox environment change to a new active immutable version. Requires rigs:manage.",
|
|
4082
4138
|
inputSchema: {
|
|
4083
4139
|
rigId: z4.string().uuid(),
|
|
4084
4140
|
changeId: z4.string().uuid(),
|
|
@@ -4885,7 +4941,7 @@ function registerWorkspaceOrchestrationTools(
|
|
|
4885
4941
|
"session_create",
|
|
4886
4942
|
{
|
|
4887
4943
|
description:
|
|
4888
|
-
"Spawn a new agent session (a worker) only for a concrete, bounded subtask that can run independently and has a defined integration point in your current work. Do not delegate work you will also perform yourself; track the child and join its actual result before completing dependent work. Give the child a concise semantic title; if omitted, OpenGeni derives one from its delegated goal or initial message. The child inherits this session's visibility, agent-access scope and end-user label; a private session can only create a same-owner private child, and memoryScope may only narrow this session's selector. Give a goal-bearing child its delegated objective. Its goal.rootConstraints may be an exact applicable subset of this accepted turn's frozen root constraints; omit that field to inherit all of them. Omit sandbox for the safe default: compatible children share the creator's box, while a different Variable Set,
|
|
4944
|
+
"Spawn a new agent session (a worker) only for a concrete, bounded subtask that can run independently and has a defined integration point in your current work. Do not delegate work you will also perform yourself; track the child and join its actual result before completing dependent work. Give the child a concise semantic title; if omitted, OpenGeni derives one from its delegated goal or initial message. The child inherits this session's visibility, agent-access scope and end-user label; a private session can only create a same-owner private child, and memoryScope may only narrow this session's selector. Give a goal-bearing child its delegated objective. Its goal.rootConstraints may be an exact applicable subset of this accepted turn's frozen root constraints; omit that field to inherit all of them. Omit sandbox for the safe default: compatible children share the creator's box, while a different Variable Set, Sandbox Environment, or machineTarget gets its own box. Use 'new' for deliberate isolation or {groupId} for a strict compatible sibling join. Put targetSandboxId and its optional workingDir together inside machineTarget; a machineTarget is always an own-box create even when the parent is backend none. To create a non-delegating leaf, pass a narrowed firstPartyMcpTools list that omits session_create; do not use a child-local depth override. Public REST/SDK callers retain advanced absolute depth and explicit shared-placement controls.",
|
|
4889
4945
|
inputSchema: sessionCreateInput,
|
|
4890
4946
|
},
|
|
4891
4947
|
async (args) => {
|
|
@@ -5724,7 +5780,9 @@ function registerCapabilityDiscoveryTools(
|
|
|
5724
5780
|
// This is a readiness projection, never credential resolution or a new grant.
|
|
5725
5781
|
const setupProjections = async (items: CapabilityCatalogItem[]) => {
|
|
5726
5782
|
const availableServerIds = new Set<string>();
|
|
5727
|
-
|
|
5783
|
+
const acceptedServerIds = new Set<string>();
|
|
5784
|
+
let accountSelectionKnown = false;
|
|
5785
|
+
if (items.some((item) => item.enabled && item.connectionRef)) {
|
|
5728
5786
|
const claims = exactAgentCommandContext(grant, sessionId);
|
|
5729
5787
|
const attemptCatalog = await getAttemptToolCatalog(deps.db, {
|
|
5730
5788
|
accountId: grant.accountId,
|
|
@@ -5737,11 +5795,28 @@ function registerCapabilityDiscoveryTools(
|
|
|
5737
5795
|
attemptCatalog.executionGeneration === claims.callerExecutionGeneration
|
|
5738
5796
|
) {
|
|
5739
5797
|
for (const entry of attemptCatalog.entries) availableServerIds.add(entry.identity.serverId);
|
|
5798
|
+
const bindings = await getSessionTurnMcpAccountBindings(
|
|
5799
|
+
deps.db,
|
|
5800
|
+
grant.workspaceId,
|
|
5801
|
+
sessionId,
|
|
5802
|
+
claims.callerTurnId,
|
|
5803
|
+
);
|
|
5804
|
+
accountSelectionKnown = bindings !== null;
|
|
5805
|
+
const readiness = capabilityAccountReadiness(availableServerIds, bindings);
|
|
5806
|
+
for (const id of readiness.available) availableServerIds.add(id);
|
|
5807
|
+
for (const id of readiness.accepted) acceptedServerIds.add(id);
|
|
5740
5808
|
}
|
|
5741
5809
|
}
|
|
5742
5810
|
return await Promise.all(
|
|
5743
5811
|
items.map((item) =>
|
|
5744
|
-
capabilitySetupProjection(
|
|
5812
|
+
capabilitySetupProjection(
|
|
5813
|
+
deps,
|
|
5814
|
+
grant.workspaceId,
|
|
5815
|
+
item,
|
|
5816
|
+
availableServerIds,
|
|
5817
|
+
acceptedServerIds,
|
|
5818
|
+
accountSelectionKnown,
|
|
5819
|
+
),
|
|
5745
5820
|
),
|
|
5746
5821
|
);
|
|
5747
5822
|
};
|
|
@@ -5750,7 +5825,7 @@ function registerCapabilityDiscoveryTools(
|
|
|
5750
5825
|
"capability_catalog_search",
|
|
5751
5826
|
{
|
|
5752
5827
|
description:
|
|
5753
|
-
"
|
|
5828
|
+
"Find integrations in OpenGeni's reviewed workspace catalog when the user asks to add one or needed access is missing. Search by integration name or task outcome. Results describe setup status and provide setup.nextAction when human setup can be requested. Use available tools directly for ready candidates. This reads metadata only and does not connect or authorize anything.",
|
|
5754
5829
|
inputSchema: {
|
|
5755
5830
|
query: z4.string().min(1).max(500),
|
|
5756
5831
|
limit: z4.number().int().min(1).max(20).optional(),
|
|
@@ -5780,6 +5855,10 @@ function registerCapabilityDiscoveryTools(
|
|
|
5780
5855
|
setup: {
|
|
5781
5856
|
...setups[index]!,
|
|
5782
5857
|
requiredVariables: capabilityRequiredVariables(item),
|
|
5858
|
+
nextAction:
|
|
5859
|
+
setups[index]!.status === "authorization_required"
|
|
5860
|
+
? { toolName: "capability_authorization_request", capabilityId: item.id }
|
|
5861
|
+
: null,
|
|
5783
5862
|
},
|
|
5784
5863
|
}));
|
|
5785
5864
|
return json({ query, matches });
|
|
@@ -5790,7 +5869,7 @@ function registerCapabilityDiscoveryTools(
|
|
|
5790
5869
|
"capability_authorization_request",
|
|
5791
5870
|
{
|
|
5792
5871
|
description:
|
|
5793
|
-
"
|
|
5872
|
+
"Show a Connect card in this chat for a suitable capability returned by capability_catalog_search with setup.nextAction. Supply its capability ID and a brief rationale explaining how it helps the task; no separate confirmation is needed before showing the card. Requesting setup needs no integration-management permission and grants no access. The authenticated human completes setup through the card; never ask them to paste credentials into chat. Do not request another card for the same pending setup, or for a candidate reported ready or unavailable.",
|
|
5794
5873
|
inputSchema: {
|
|
5795
5874
|
capabilityId: z4.string().min(1).max(512),
|
|
5796
5875
|
rationale: z4.string().min(1).max(2000),
|
|
@@ -5869,6 +5948,8 @@ async function capabilitySetupProjection(
|
|
|
5869
5948
|
workspaceId: string,
|
|
5870
5949
|
item: CapabilityCatalogItem,
|
|
5871
5950
|
availableServerIds: ReadonlySet<string>,
|
|
5951
|
+
acceptedServerIds: ReadonlySet<string>,
|
|
5952
|
+
accountSelectionKnown: boolean,
|
|
5872
5953
|
): Promise<CapabilitySetupProjection> {
|
|
5873
5954
|
if (item.id === "api:github-app" || item.surfaceType === "first_party_github") {
|
|
5874
5955
|
const missing = githubAppMissingSettings(deps.settings);
|
|
@@ -5909,7 +5990,7 @@ async function capabilitySetupProjection(
|
|
|
5909
5990
|
detail: "A workspace admin must designate an authorized Codex Apps subscription.",
|
|
5910
5991
|
};
|
|
5911
5992
|
}
|
|
5912
|
-
if (item.enabled && item.connectionRef
|
|
5993
|
+
if (item.enabled && item.connectionRef) {
|
|
5913
5994
|
if (item.runtime.mcpServerId && availableServerIds.has(item.runtime.mcpServerId)) {
|
|
5914
5995
|
return {
|
|
5915
5996
|
status: "ready",
|
|
@@ -5917,11 +5998,22 @@ async function capabilitySetupProjection(
|
|
|
5917
5998
|
detail: "This capability has tools available in this turn.",
|
|
5918
5999
|
};
|
|
5919
6000
|
}
|
|
6001
|
+
if (
|
|
6002
|
+
!accountSelectionKnown ||
|
|
6003
|
+
(item.runtime.mcpServerId && acceptedServerIds.has(item.runtime.mcpServerId))
|
|
6004
|
+
) {
|
|
6005
|
+
return {
|
|
6006
|
+
status: "unavailable",
|
|
6007
|
+
action: null,
|
|
6008
|
+
detail:
|
|
6009
|
+
"This integration's tools are unavailable in this execution. Check its connection or setup failure before requesting reconnection; missing tools alone do not establish an authorization failure.",
|
|
6010
|
+
};
|
|
6011
|
+
}
|
|
5920
6012
|
return {
|
|
5921
6013
|
status: "authorization_required",
|
|
5922
6014
|
action: "connect",
|
|
5923
6015
|
detail:
|
|
5924
|
-
"This
|
|
6016
|
+
"This integration is enabled in the workspace, but no account was selected for this execution. Connect or select an account available to the sender, then send a new message.",
|
|
5925
6017
|
};
|
|
5926
6018
|
}
|
|
5927
6019
|
if (item.enabled) {
|