@opengeni/runtime 2.5.2 → 2.5.3-canary.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.
- package/dist/{agent-Q2VNH5FV.js → agent-RJQLKFIW.js} +2 -2
- package/dist/{chunk-LGLJ5PVD.js → chunk-46BFUGFL.js} +2622 -1368
- package/dist/chunk-46BFUGFL.js.map +1 -0
- package/dist/{chunk-CD22I76N.js → chunk-FHGLYLBL.js} +2 -2
- package/dist/{chunk-25B3EQRW.js → chunk-UNICCRW5.js} +4 -4
- package/dist/{chunk-GJC7H2FC.js → chunk-VUEWS4GI.js} +494 -95
- package/dist/chunk-VUEWS4GI.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +22 -4
- package/dist/{localSources-Z5IJIIRV.js → localSources-PVFCAPN2.js} +3 -3
- package/dist/mcp-operation-dispatch.d.ts +45 -0
- package/dist/mcp-operation-observation.d.ts +137 -0
- package/dist/operational-instructions.d.ts +1 -1
- package/dist/sandbox/archive-spool.d.ts +14 -0
- package/dist/sandbox/host-archive-spool.d.ts +19 -0
- package/dist/sandbox/index.d.ts +5 -1
- package/dist/sandbox/index.js +7 -3
- package/dist/sandbox/routing/backend-resolver.d.ts +1 -0
- package/dist/sandbox/selfhosted/file-transfer.d.ts +20 -0
- package/dist/sandbox/selfhosted/session.d.ts +7 -0
- package/dist/sandbox/workspace-archive.d.ts +7 -0
- package/dist/workspace-skills.d.ts +4 -0
- package/dist/workspace-tool-gateway.js +4 -4
- package/package.json +11 -11
- package/src/bundled_default_skills/document-parsing/SKILL.md +36 -0
- package/src/bundled_site_skills/opengeni-sites/SKILL.md +1 -1
- package/src/index.ts +231 -45
- package/src/lazy-tool-transport.ts +2 -0
- package/src/mcp-operation-dispatch.ts +144 -0
- package/src/mcp-operation-observation.ts +148 -0
- package/src/operational-instructions.ts +1 -1
- package/src/runtime-skills.ts +7 -2
- package/src/sandbox/archive-spool.ts +16 -0
- package/src/sandbox/host-archive-spool.ts +1040 -0
- package/src/sandbox/index.ts +292 -203
- package/src/sandbox/routing/backend-resolver.ts +2 -0
- package/src/sandbox/selfhosted/control-rpc.ts +22 -0
- package/src/sandbox/selfhosted/fault-rendering.ts +9 -0
- package/src/sandbox/selfhosted/file-transfer.ts +160 -0
- package/src/sandbox/selfhosted/op-testing.ts +3 -0
- package/src/sandbox/selfhosted/session.ts +194 -5
- package/src/sandbox/workspace-archive.ts +54 -1
- package/src/workspace-skills.ts +171 -31
- package/dist/chunk-GJC7H2FC.js.map +0 -1
- package/dist/chunk-LGLJ5PVD.js.map +0 -1
- /package/dist/{agent-Q2VNH5FV.js.map → agent-RJQLKFIW.js.map} +0 -0
- /package/dist/{chunk-CD22I76N.js.map → chunk-FHGLYLBL.js.map} +0 -0
- /package/dist/{chunk-25B3EQRW.js.map → chunk-UNICCRW5.js.map} +0 -0
- /package/dist/{localSources-Z5IJIIRV.js.map → localSources-PVFCAPN2.js.map} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type NativeSnapshotDescriptor, type NativeSnapshotRef, type WorkspaceArchiveDescriptor, type WorkspaceTreeFingerprint } from "@opengeni/contracts";
|
|
2
|
+
import type { VerifiedHostWorkspaceArchive } from "./archive-spool.js";
|
|
2
3
|
export { WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION, decodeNativeSnapshotRef, encodeNativeSnapshotRef, parseWorkspaceArchiveDescriptor, type NativeSnapshotDescriptor, type NativeSnapshotProvider, type NativeSnapshotRef, type TarWorkspaceArchiveDescriptor, type WorkspaceArchiveDescriptor, type WorkspaceTreeFingerprint, } from "@opengeni/contracts";
|
|
3
4
|
export type VerifiedWorkspaceArchive = {
|
|
4
5
|
bytes: Uint8Array;
|
|
@@ -7,6 +8,12 @@ export type VerifiedWorkspaceArchive = {
|
|
|
7
8
|
kind: "tar" | "provider_snapshot";
|
|
8
9
|
nativeSnapshot?: NativeSnapshotRef;
|
|
9
10
|
};
|
|
11
|
+
export type VerifiedWorkspaceArchivePayload = VerifiedWorkspaceArchive | VerifiedHostWorkspaceArchive;
|
|
12
|
+
/** Host-backed portable archives can go directly to object storage without an
|
|
13
|
+
* intermediate whole-document string or buffer. Existing inline/provider APIs
|
|
14
|
+
* retain their compatibility path when disk-backed publication is unavailable. */
|
|
15
|
+
export declare function captureWorkspaceArchiveForStorage(session: unknown, capturedAtMs: number, options: WorkspaceArchiveCaptureOptions, objectStorageAvailable: boolean): Promise<VerifiedWorkspaceArchivePayload>;
|
|
16
|
+
export declare function disposeWorkspaceArchive(archive: VerifiedWorkspaceArchivePayload | undefined): Promise<void>;
|
|
10
17
|
export type WorkspaceArchiveIntegrityCode = "archive_metadata_missing" | "archive_metadata_invalid" | "archive_base64_invalid" | "archive_hash_mismatch" | "archive_hydration_failed" | "workspace_fingerprint_unavailable" | "workspace_changed_during_capture" | "workspace_fingerprint_mismatch" | "native_snapshot_reference_invalid" | "native_snapshot_fallback_unverified";
|
|
11
18
|
export declare class WorkspaceArchiveIntegrityError extends Error {
|
|
12
19
|
readonly code: WorkspaceArchiveIntegrityCode;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Tool } from "@openai/agents";
|
|
1
2
|
import { Capability, type SandboxSessionLike } from "@openai/agents/sandbox";
|
|
2
3
|
export type WorkspaceSkillSearchPath = Readonly<{
|
|
3
4
|
path: string;
|
|
@@ -24,6 +25,9 @@ export declare class WorkspaceSkillsCapability extends Capability {
|
|
|
24
25
|
readonly type = "workspace-skills";
|
|
25
26
|
private discovery?;
|
|
26
27
|
constructor(searchPaths: readonly WorkspaceSkillSearchPath[], reservedNames?: ReadonlySet<string>, shadowedNames?: ReadonlySet<string>);
|
|
28
|
+
bind(session: SandboxSessionLike): this;
|
|
29
|
+
private discover;
|
|
30
|
+
tools(): Tool<unknown>[];
|
|
27
31
|
instructions(): Promise<string | null>;
|
|
28
32
|
}
|
|
29
33
|
export declare function workspaceSkills(searchPaths: readonly WorkspaceSkillSearchPath[], reservedNames?: Iterable<string>, shadowedNames?: Iterable<string>): WorkspaceSkillsCapability;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
prepareAgentTools
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-VUEWS4GI.js";
|
|
4
|
+
import "./chunk-46BFUGFL.js";
|
|
5
|
+
import "./chunk-FHGLYLBL.js";
|
|
6
6
|
import "./chunk-GB763TSV.js";
|
|
7
7
|
import "./chunk-UZ66CGC2.js";
|
|
8
8
|
import "./chunk-JHM4OS7C.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-UNICCRW5.js";
|
|
10
10
|
import "./chunk-PZ5AY32C.js";
|
|
11
11
|
export {
|
|
12
12
|
prepareAgentTools as prepareWorkspaceToolGatewayTools
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/runtime",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.3-canary.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -62,16 +62,16 @@
|
|
|
62
62
|
"@openai/agents": "0.14.3",
|
|
63
63
|
"@openai/agents-core": "0.14.3",
|
|
64
64
|
"@openai/agents-extensions": "0.14.3",
|
|
65
|
-
"@opengeni/agent-proto": "^0.5.1",
|
|
66
|
-
"@opengeni/capabilities": "^0.3.3",
|
|
67
|
-
"@opengeni/codemode": "^0.5.
|
|
68
|
-
"@opengeni/codex": "^0.2.22",
|
|
69
|
-
"@opengeni/config": "^1.1.1",
|
|
70
|
-
"@opengeni/contracts": "^3.0.1",
|
|
71
|
-
"@opengeni/network": "^0.3.1",
|
|
72
|
-
"@opengeni/sdk": "^5.0.
|
|
73
|
-
"@opengeni/tool-gateway": "^0.1.
|
|
74
|
-
"@opengeni/xai-subscription": "^0.1.4",
|
|
65
|
+
"@opengeni/agent-proto": "^0.5.1-canary.27",
|
|
66
|
+
"@opengeni/capabilities": "^0.3.3-canary.9",
|
|
67
|
+
"@opengeni/codemode": "^0.5.6-canary.1",
|
|
68
|
+
"@opengeni/codex": "^0.2.22-canary.9",
|
|
69
|
+
"@opengeni/config": "^1.1.2-canary.1",
|
|
70
|
+
"@opengeni/contracts": "^3.0.2-canary.1",
|
|
71
|
+
"@opengeni/network": "^0.3.1-canary.9",
|
|
72
|
+
"@opengeni/sdk": "^5.0.5-canary.1",
|
|
73
|
+
"@opengeni/tool-gateway": "^0.1.7-canary.1",
|
|
74
|
+
"@opengeni/xai-subscription": "^0.1.4-canary.9",
|
|
75
75
|
"debug": "4.4.3",
|
|
76
76
|
"modal": "0.9.0",
|
|
77
77
|
"openai": "6.47.0",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: document-parsing
|
|
3
|
+
description: Extract readable Markdown from local Word, PowerPoint, Excel, OpenDocument, RTF, EPUB, CSV, and text-based PDF files using the preinstalled AnyDoc runtime.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
runtime: anydoc@0.1.8
|
|
7
|
+
upstream: https://github.com/firecrawl/anydoc
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Parse local documents
|
|
11
|
+
|
|
12
|
+
Use the preinstalled `anydoc` CLI when a task needs the contents of a local
|
|
13
|
+
office document, ebook, CSV, or text-based PDF. This is read-only extraction;
|
|
14
|
+
it does not edit the source or replace OpenGeni's durable artifact tools.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
anydoc report.docx
|
|
18
|
+
anydoc slides.pptx -o /tmp/slides.md
|
|
19
|
+
anydoc - --format csv < data-without-extension
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Rules:
|
|
23
|
+
|
|
24
|
+
1. Supported inputs: `.doc`, `.docx`, `.docm`, `.odt`, `.rtf`, `.epub`,
|
|
25
|
+
`.pdf`, `.ppt`, `.pps`, `.pot`, `.pptx`, `.pptm`, `.ppsx`, `.ppsm`,
|
|
26
|
+
`.odp`, `.xls`, `.xlsx`, `.xlsm`, `.xlsb`, `.ods`, and `.csv`.
|
|
27
|
+
2. Let AnyDoc detect the format from file content. Specify `--format` only for
|
|
28
|
+
signature-less input such as CSV on stdin or a file without a useful name.
|
|
29
|
+
3. For large output, use `-o` and inspect only the relevant sections instead
|
|
30
|
+
of streaming the whole document into model context.
|
|
31
|
+
4. Preserve the source file. AnyDoc output is a derived reading aid, not a new
|
|
32
|
+
canonical document or editable artifact.
|
|
33
|
+
5. Scanned/image-only PDFs need OCR and are unsupported. Encrypted, malformed,
|
|
34
|
+
or resource-limit failures must be reported rather than guessed around.
|
|
35
|
+
6. Never install or download AnyDoc at runtime. If `anydoc` is unavailable,
|
|
36
|
+
report that the selected compute image lacks the parsing runtime.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: opengeni-sites
|
|
3
|
-
description: Build,
|
|
3
|
+
description: Build, edit, and publish OpenGeni Sites. Proactively recommend Sites for visual or interactive deliverables such as dashboards, explorable reports, landing pages, demos, trackers, portals, directories, calculators, forms, and custom agent interfaces—even when the user doesn't name Sites. Prefer Sites when the delivery format is open. When working on an existing application, follow its established architecture and workflow; consider Sites only when relevant to the requested outcome, not as an automatic conversion. Respect explicit format and platform choices.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenGeni Sites
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
import type { ModelProviderApi, ResolvedModelProvider, Settings } from "@opengeni/config";
|
|
2
2
|
import { executeCommandReadWithRefresh } from "./command-read-refresh";
|
|
3
|
+
import {
|
|
4
|
+
captureMcpOperationDispatch,
|
|
5
|
+
hasActiveRecoverableMcpOperation,
|
|
6
|
+
runRecoverableMcpOperation,
|
|
7
|
+
type McpOperationPersistence,
|
|
8
|
+
} from "./mcp-operation-dispatch";
|
|
9
|
+
export {
|
|
10
|
+
captureMcpOperationDispatch,
|
|
11
|
+
hasActiveRecoverableMcpOperation,
|
|
12
|
+
McpOperationOutcomeUnknownError,
|
|
13
|
+
runRecoverableMcpOperation,
|
|
14
|
+
type CapturedMcpOperation,
|
|
15
|
+
type McpOperationPersistence,
|
|
16
|
+
type RecoverableMcpOperation,
|
|
17
|
+
} from "./mcp-operation-dispatch";
|
|
18
|
+
import { assertMcpOperationObservationAuthority } from "./mcp-operation-observation";
|
|
19
|
+
export {
|
|
20
|
+
assertMcpOperationObservationAuthority,
|
|
21
|
+
observeMcpOperation,
|
|
22
|
+
runMcpOperationObservationWithAuthority,
|
|
23
|
+
type McpOperationObservationAuthority,
|
|
24
|
+
type McpObservationBinding,
|
|
25
|
+
type McpObservationReceipt,
|
|
26
|
+
} from "./mcp-operation-observation";
|
|
3
27
|
import { formatSkillCatalog, type SkillCatalogDescriptor } from "./skill-catalog";
|
|
4
28
|
export { formatSkillCatalog, type SkillCatalogDescriptor } from "./skill-catalog";
|
|
5
29
|
import {
|
|
@@ -11,6 +35,7 @@ import {
|
|
|
11
35
|
AGENT_INSTRUCTIONS_CORE_PLACEHOLDER,
|
|
12
36
|
collectSandboxEnvironment,
|
|
13
37
|
configuredProviders,
|
|
38
|
+
McpOperationRecoverySchema,
|
|
14
39
|
firstPartyMcpInternalBaseUrl,
|
|
15
40
|
firstPartyMcpInternalWorkspaceUrl,
|
|
16
41
|
resolveFirstPartyDelegationSecret,
|
|
@@ -683,6 +708,8 @@ export type ResolveConnectionCredentialResult =
|
|
|
683
708
|
connectionId: string;
|
|
684
709
|
authoritySource?: "host";
|
|
685
710
|
authorizeProviderRequest?: () => Promise<boolean>;
|
|
711
|
+
/** Metadata-only immutable authority binding supplied by the trusted worker resolver. */
|
|
712
|
+
operationAuthorityDigest?: string;
|
|
686
713
|
expiresAt?: Date | null;
|
|
687
714
|
}
|
|
688
715
|
| {
|
|
@@ -1723,6 +1750,11 @@ type ModelToolInvocation = {
|
|
|
1723
1750
|
};
|
|
1724
1751
|
|
|
1725
1752
|
const modelToolInvocation = new AsyncLocalStorage<ModelToolInvocation>();
|
|
1753
|
+
// Correlation must not activate the separate approval-only invocation guard.
|
|
1754
|
+
const modelMcpCallIdentity = new AsyncLocalStorage<{
|
|
1755
|
+
modelName: string;
|
|
1756
|
+
callId: string;
|
|
1757
|
+
} | null>();
|
|
1726
1758
|
const agentInputWaitYields = new WeakMap<object, InputWaitYield>();
|
|
1727
1759
|
|
|
1728
1760
|
export type BuildAgentOptions = {
|
|
@@ -2657,6 +2689,7 @@ export function buildOpenGeniAgent(
|
|
|
2657
2689
|
agent as unknown as ApprovalCapableAgent,
|
|
2658
2690
|
options.approvedToolCallId,
|
|
2659
2691
|
);
|
|
2692
|
+
installModelMcpCallIdentity(agent as unknown as ApprovalCapableAgent);
|
|
2660
2693
|
return agent;
|
|
2661
2694
|
}
|
|
2662
2695
|
|
|
@@ -2785,6 +2818,7 @@ export function buildOpenGeniAgent(
|
|
|
2785
2818
|
agent as unknown as ApprovalCapableAgent,
|
|
2786
2819
|
options.approvedToolCallId,
|
|
2787
2820
|
);
|
|
2821
|
+
installModelMcpCallIdentity(agent as unknown as ApprovalCapableAgent);
|
|
2788
2822
|
return agent;
|
|
2789
2823
|
}
|
|
2790
2824
|
|
|
@@ -2876,6 +2910,42 @@ type ApprovalCapableAgent = {
|
|
|
2876
2910
|
clone?: (config: unknown) => ApprovalCapableAgent;
|
|
2877
2911
|
};
|
|
2878
2912
|
|
|
2913
|
+
/** Preserve SDK correlation through MCP projection without granting approval.
|
|
2914
|
+
* SandboxAgent clones rebuild instance methods, so reinstall on every clone. */
|
|
2915
|
+
function installModelMcpCallIdentity(agent: ApprovalCapableAgent): void {
|
|
2916
|
+
const listMcpTools = agent.getMcpTools.bind(agent);
|
|
2917
|
+
agent.getMcpTools = async (resolutionContext: unknown) =>
|
|
2918
|
+
(await listMcpTools(resolutionContext)).map((tool) => {
|
|
2919
|
+
if (tool.type !== "function") return tool;
|
|
2920
|
+
const invoke = tool.invoke.bind(tool);
|
|
2921
|
+
return {
|
|
2922
|
+
...tool,
|
|
2923
|
+
invoke: async (runContext, input, details) => {
|
|
2924
|
+
const callId = details?.toolCall?.callId;
|
|
2925
|
+
return await modelMcpCallIdentity.run(
|
|
2926
|
+
typeof callId === "string" && callId.length > 0
|
|
2927
|
+
? { modelName: tool.name, callId }
|
|
2928
|
+
: null,
|
|
2929
|
+
async () => await invoke(runContext, input, details),
|
|
2930
|
+
);
|
|
2931
|
+
},
|
|
2932
|
+
};
|
|
2933
|
+
});
|
|
2934
|
+
const clone = agent.clone?.bind(agent);
|
|
2935
|
+
if (clone) {
|
|
2936
|
+
agent.clone = (config: unknown) => {
|
|
2937
|
+
const cloned = clone(config);
|
|
2938
|
+
installModelMcpCallIdentity(cloned);
|
|
2939
|
+
return cloned;
|
|
2940
|
+
};
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2944
|
+
function modelMcpSourceCallId(modelName: string): string | undefined {
|
|
2945
|
+
const identity = modelMcpCallIdentity.getStore();
|
|
2946
|
+
return identity?.modelName === modelName ? identity.callId : undefined;
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2879
2949
|
/**
|
|
2880
2950
|
* Install the approval wrap on a single agent instance: replace `getMcpTools`
|
|
2881
2951
|
* with one that stamps `needsApproval: () => true` on every MCP tool whose
|
|
@@ -3612,6 +3682,8 @@ export type ToolPreparationPhaseMeasurement = {
|
|
|
3612
3682
|
};
|
|
3613
3683
|
|
|
3614
3684
|
export type PrepareToolsOptions = {
|
|
3685
|
+
/** Opt-in exact-attempt persistence; absence preserves ordinary MCP execution. */
|
|
3686
|
+
mcpOperationPersistence?: McpOperationPersistence;
|
|
3615
3687
|
/** Live exact-owner control refresh; API remains read/observation authority. */
|
|
3616
3688
|
refreshOwnedCommand?: (commandId: string) => Promise<boolean>;
|
|
3617
3689
|
accountId?: string;
|
|
@@ -3986,24 +4058,30 @@ export async function prepareAgentTools(
|
|
|
3986
4058
|
}
|
|
3987
4059
|
const optional = tool.optional === true;
|
|
3988
4060
|
return {
|
|
3989
|
-
server:
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4061
|
+
server: configureMcpOperationRecovery(
|
|
4062
|
+
new PrefixedMcpServer(
|
|
4063
|
+
local.server,
|
|
4064
|
+
config.id,
|
|
4065
|
+
config.allowedTools,
|
|
4066
|
+
optional || Boolean(config.connectionRef),
|
|
4067
|
+
aggregateToolBudget,
|
|
4068
|
+
`${config.id}:${index}`,
|
|
4069
|
+
false,
|
|
4070
|
+
buildConnectorAttachmentAuthority(
|
|
4071
|
+
config,
|
|
4072
|
+
options,
|
|
4073
|
+
resolvedMcpToolConnectionIds,
|
|
4074
|
+
config.url,
|
|
4075
|
+
local.resolvedConnectionId,
|
|
4076
|
+
),
|
|
4077
|
+
tool.eager !== true,
|
|
4078
|
+
local.preflightCall,
|
|
4079
|
+
local.approvalAuthority,
|
|
4003
4080
|
),
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4081
|
+
config,
|
|
4082
|
+
options,
|
|
4083
|
+
config.url,
|
|
4084
|
+
false,
|
|
4007
4085
|
),
|
|
4008
4086
|
bestEffort: optional || Boolean(config.connectionRef),
|
|
4009
4087
|
optional,
|
|
@@ -4121,24 +4199,30 @@ export async function prepareAgentTools(
|
|
|
4121
4199
|
}
|
|
4122
4200
|
: {}),
|
|
4123
4201
|
});
|
|
4124
|
-
const server =
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4202
|
+
const server = configureMcpOperationRecovery(
|
|
4203
|
+
new PrefixedMcpServer(
|
|
4204
|
+
innerServer,
|
|
4205
|
+
config.id,
|
|
4206
|
+
config.allowedTools,
|
|
4207
|
+
bestEffort,
|
|
4208
|
+
aggregateToolBudget,
|
|
4209
|
+
`${config.id}:${index}`,
|
|
4210
|
+
firstParty && !bestEffort,
|
|
4211
|
+
buildConnectorAttachmentAuthority(config, options, resolvedMcpToolConnectionIds, url),
|
|
4212
|
+
tool.eager !== true,
|
|
4213
|
+
undefined,
|
|
4214
|
+
undefined,
|
|
4215
|
+
firstParty && config.id === "opengeni" && !config.connectionRef && !bridge
|
|
4216
|
+
? inputWaitYield
|
|
4217
|
+
: undefined,
|
|
4218
|
+
firstParty && config.id === "opengeni" && !config.connectionRef && !bridge
|
|
4219
|
+
? options.refreshOwnedCommand
|
|
4220
|
+
: undefined,
|
|
4221
|
+
),
|
|
4222
|
+
config,
|
|
4223
|
+
options,
|
|
4224
|
+
url,
|
|
4225
|
+
Boolean(config.connectionRef) && !bridge && !isCodexAppsMcpServer(config),
|
|
4142
4226
|
);
|
|
4143
4227
|
return {
|
|
4144
4228
|
server,
|
|
@@ -4788,18 +4872,42 @@ async function prepareToolGatewayDefinitionsFromServers(
|
|
|
4788
4872
|
}),
|
|
4789
4873
|
}
|
|
4790
4874
|
: {}),
|
|
4791
|
-
execute: async (args, context) =>
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4875
|
+
execute: async (args, context) => {
|
|
4876
|
+
const execute = async () =>
|
|
4877
|
+
await server.executeCatalogTool(
|
|
4878
|
+
toolName,
|
|
4879
|
+
args,
|
|
4880
|
+
{
|
|
4881
|
+
...(context.transportMeta ?? {}),
|
|
4882
|
+
opengeniOperationId: context.operationId,
|
|
4883
|
+
},
|
|
4884
|
+
{
|
|
4885
|
+
...(context.signal ? { signal: context.signal } : {}),
|
|
4886
|
+
},
|
|
4887
|
+
);
|
|
4888
|
+
const recovery = configuredMcpOperationRecovery(server, toolName);
|
|
4889
|
+
if (!recovery) return await execute();
|
|
4890
|
+
if (!recovery.supported || !["model", "codemode"].includes(context.caller.kind)) {
|
|
4891
|
+
throw new Error(
|
|
4892
|
+
"MCP operation recovery requires an exact attempt and remote brokered execution",
|
|
4893
|
+
);
|
|
4894
|
+
}
|
|
4895
|
+
return await runRecoverableMcpOperation(
|
|
4799
4896
|
{
|
|
4800
|
-
|
|
4897
|
+
operationId: context.operationId,
|
|
4898
|
+
...(context.sourceCallId === undefined
|
|
4899
|
+
? {}
|
|
4900
|
+
: { sourceCallId: context.sourceCallId }),
|
|
4901
|
+
serverId: server.registryId,
|
|
4902
|
+
originalTool: toolName,
|
|
4903
|
+
observerTool: recovery.policy[toolName]!.observerTool,
|
|
4904
|
+
destinationDigest: recovery.destinationDigest,
|
|
4905
|
+
argumentDigest: digestCanonicalJson(args),
|
|
4801
4906
|
},
|
|
4802
|
-
|
|
4907
|
+
recovery.persistence,
|
|
4908
|
+
execute,
|
|
4909
|
+
);
|
|
4910
|
+
},
|
|
4803
4911
|
};
|
|
4804
4912
|
});
|
|
4805
4913
|
},
|
|
@@ -4818,6 +4926,38 @@ function attemptToolCodemodePath(serverId: string, toolName: string): readonly s
|
|
|
4818
4926
|
return [serverId, toolName];
|
|
4819
4927
|
}
|
|
4820
4928
|
|
|
4929
|
+
type PreparedMcpOperationRecovery = {
|
|
4930
|
+
policy: NonNullable<Settings["mcpServers"][number]["operationRecovery"]>;
|
|
4931
|
+
persistence: McpOperationPersistence;
|
|
4932
|
+
destinationDigest: string;
|
|
4933
|
+
supported: boolean;
|
|
4934
|
+
};
|
|
4935
|
+
|
|
4936
|
+
const mcpOperationRecoveryByServer = new WeakMap<PrefixedMcpServer, PreparedMcpOperationRecovery>();
|
|
4937
|
+
|
|
4938
|
+
function configureMcpOperationRecovery(
|
|
4939
|
+
server: PrefixedMcpServer,
|
|
4940
|
+
config: Settings["mcpServers"][number],
|
|
4941
|
+
options: PrepareToolsOptions,
|
|
4942
|
+
destinationUrl: string,
|
|
4943
|
+
brokered: boolean,
|
|
4944
|
+
): PrefixedMcpServer {
|
|
4945
|
+
if (config.operationRecovery && options.mcpOperationPersistence) {
|
|
4946
|
+
mcpOperationRecoveryByServer.set(server, {
|
|
4947
|
+
policy: McpOperationRecoverySchema.parse(config.operationRecovery),
|
|
4948
|
+
persistence: options.mcpOperationPersistence,
|
|
4949
|
+
destinationDigest: digestCanonicalJson(new URL(destinationUrl).toString()),
|
|
4950
|
+
supported: brokered && attemptToolScope(options) !== null,
|
|
4951
|
+
});
|
|
4952
|
+
}
|
|
4953
|
+
return server;
|
|
4954
|
+
}
|
|
4955
|
+
|
|
4956
|
+
function configuredMcpOperationRecovery(server: PrefixedMcpServer, toolName: string) {
|
|
4957
|
+
const recovery = mcpOperationRecoveryByServer.get(server);
|
|
4958
|
+
return recovery && Object.hasOwn(recovery.policy, toolName) ? recovery : undefined;
|
|
4959
|
+
}
|
|
4960
|
+
|
|
4821
4961
|
function attemptToolSource(serverId: string): ToolGatewayDefinition["source"] {
|
|
4822
4962
|
if (serverId === "opengeni" || serverId === "files" || serverId === "docs") {
|
|
4823
4963
|
return serverId;
|
|
@@ -4889,6 +5029,35 @@ function connectionBrokerFetch(
|
|
|
4889
5029
|
suppressSetupAuthNeeded,
|
|
4890
5030
|
);
|
|
4891
5031
|
}
|
|
5032
|
+
if (request.method === "tools/call") {
|
|
5033
|
+
assertMcpOperationObservationAuthority({
|
|
5034
|
+
serverId: config.id,
|
|
5035
|
+
...(request.toolName === undefined ? {} : { toolName: request.toolName }),
|
|
5036
|
+
destinationDigest: digestCanonicalJson(destinationUrl),
|
|
5037
|
+
...(first.operationAuthorityDigest === undefined
|
|
5038
|
+
? {}
|
|
5039
|
+
: { authorityDigest: first.operationAuthorityDigest }),
|
|
5040
|
+
});
|
|
5041
|
+
if (
|
|
5042
|
+
hasActiveRecoverableMcpOperation() &&
|
|
5043
|
+
(request.batch ||
|
|
5044
|
+
request.id === undefined ||
|
|
5045
|
+
request.id === null ||
|
|
5046
|
+
!request.operationId ||
|
|
5047
|
+
!request.toolName)
|
|
5048
|
+
) {
|
|
5049
|
+
throw new Error("Recoverable MCP request is missing its exact dispatch identity");
|
|
5050
|
+
}
|
|
5051
|
+
// The helper is a no-op without its private context. Always enter it for
|
|
5052
|
+
// tools/call so a late transport continuation cannot bypass a closed context.
|
|
5053
|
+
await captureMcpOperationDispatch({
|
|
5054
|
+
operationId: request.operationId ?? "",
|
|
5055
|
+
serverId: config.id,
|
|
5056
|
+
toolName: request.toolName ?? "",
|
|
5057
|
+
destinationDigest: digestCanonicalJson(destinationUrl),
|
|
5058
|
+
authorityDigest: first.operationAuthorityDigest ?? "",
|
|
5059
|
+
});
|
|
5060
|
+
}
|
|
4892
5061
|
const response = await baseFetch(
|
|
4893
5062
|
fetchInputForAttempt(input),
|
|
4894
5063
|
withConnectionHeaders(input, init, first.headers),
|
|
@@ -6387,9 +6556,11 @@ class AttemptDefinitionMcpServer implements MCPServer {
|
|
|
6387
6556
|
): Promise<any> {
|
|
6388
6557
|
if (this.closed) throw new Error("local model tool server is closed");
|
|
6389
6558
|
const environment = await this.requiredAttemptToolEnvironment();
|
|
6559
|
+
const sourceCallId = modelMcpSourceCallId(toolName);
|
|
6390
6560
|
return await this.resultCustomDataBridge.captureResult(args, async (cleanArgs) =>
|
|
6391
6561
|
environment.callModel({
|
|
6392
6562
|
modelName: toolName,
|
|
6563
|
+
...(sourceCallId === undefined ? {} : { sourceCallId }),
|
|
6393
6564
|
arguments: cleanArgs ?? {},
|
|
6394
6565
|
subjectId: this.subjectId,
|
|
6395
6566
|
...(meta === undefined ? {} : { transportMeta: meta }),
|
|
@@ -6711,8 +6882,10 @@ export class PrefixedMcpServer implements MCPServer {
|
|
|
6711
6882
|
}
|
|
6712
6883
|
return await this.resultCustomDataBridge.captureResult(args, async (cleanArgs) => {
|
|
6713
6884
|
if (this.attemptToolEnvironment) {
|
|
6885
|
+
const sourceCallId = modelMcpSourceCallId(toolName);
|
|
6714
6886
|
return await this.attemptToolEnvironment.callModel({
|
|
6715
6887
|
modelName: toolName,
|
|
6888
|
+
...(sourceCallId === undefined ? {} : { sourceCallId }),
|
|
6716
6889
|
arguments: cleanArgs ?? {},
|
|
6717
6890
|
subjectId: this.attemptToolSubjectId,
|
|
6718
6891
|
...(meta === undefined ? {} : { transportMeta: meta }),
|
|
@@ -6741,6 +6914,9 @@ export class PrefixedMcpServer implements MCPServer {
|
|
|
6741
6914
|
if (!this.isAllowed(unprefixed)) {
|
|
6742
6915
|
throw new Error(`MCP tool ${unprefixed} is not allowed for server ${this.registryId}`);
|
|
6743
6916
|
}
|
|
6917
|
+
if (configuredMcpOperationRecovery(this, unprefixed) && !hasActiveRecoverableMcpOperation()) {
|
|
6918
|
+
throw new Error("MCP operation recovery requires its trusted attempt execution context");
|
|
6919
|
+
}
|
|
6744
6920
|
// Nested prefix wrappers are a projection seam around one physical
|
|
6745
6921
|
// tools/call. The innermost wrapper owns the single metric observation.
|
|
6746
6922
|
const recordsPhysicalCall = !(this.inner instanceof PrefixedMcpServer);
|
|
@@ -6814,6 +6990,16 @@ export class PrefixedMcpServer implements MCPServer {
|
|
|
6814
6990
|
// state. The broker refreshed credentials for future requests but did not
|
|
6815
6991
|
// replay this call. Preserve that ambiguity as an explicit model-visible
|
|
6816
6992
|
// error for required and best-effort servers alike.
|
|
6993
|
+
if (hasActiveRecoverableMcpOperation()) {
|
|
6994
|
+
recordOutcome(
|
|
6995
|
+
isToolOutcomeUncertainMcpError(error)
|
|
6996
|
+
? "outcome_uncertain"
|
|
6997
|
+
: isAuthNeededMcpError(error)
|
|
6998
|
+
? "auth_needed"
|
|
6999
|
+
: mcpThrownToolCallOutcome(error, options?.signal),
|
|
7000
|
+
);
|
|
7001
|
+
throw error;
|
|
7002
|
+
}
|
|
6817
7003
|
if (isToolOutcomeUncertainMcpError(error)) {
|
|
6818
7004
|
recordOutcome("outcome_uncertain");
|
|
6819
7005
|
return boundedMcpToolResult({
|
|
@@ -33,6 +33,8 @@ const ALWAYS_VISIBLE_BASE_TOOL_NAMES: ReadonlySet<string> = new Set([
|
|
|
33
33
|
"apply_patch",
|
|
34
34
|
// The server-backed reader needs neither sandbox setup nor tool search.
|
|
35
35
|
"skill_read",
|
|
36
|
+
// Repository descriptors route to the live sandbox reader.
|
|
37
|
+
"repository_skill_read",
|
|
36
38
|
"request_human_input",
|
|
37
39
|
"list_models",
|
|
38
40
|
]);
|