@tangle-network/agent-provider-tangle 1.1.11 → 1.2.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/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # @tangle-network/agent-provider-tangle
2
2
 
3
3
  Wraps `@tangle-network/sandbox` as an `AgentEnvironmentProvider`.
4
- The peer range is `>=0.34.6 <1.0.0`, and this package is developed and tested against 0.37.0.
5
- The floor is 0.34.6 because exact interactive attachment needs the host receiver, and workspace branching needs keyed snapshots, durable restores, inventory recovery, and cleanup.
4
+ The peer range is `>=0.39.0 <1.0.0`, and this package is developed and tested against 0.39.0.
5
+ The floor includes runtime MCP attachment transport and preservation across per-turn model credential overrides.
6
+
7
+ Runtime MCP bindings travel through `CreateAgentEnvironmentInput.runtimeAttachments` to Sandbox's `backend.runtimeAttachments`.
8
+ They remain separate from the authored profile and its digest.
9
+ The adapter checks the selected backend catalog before provisioning and refuses missing attachment support.
10
+ Disabled attachments and aliases present in the profile are refused, including disabled profile aliases.
11
+ Per-turn backend overrides accept the same canonical attachment contract.
12
+ Remote managers still require an authenticated coordination URL reachable from the Sandbox.
13
+ Catalog support does not establish successful tool execution against a particular deployment.
6
14
  The provider fails closed when the configured backend or its catalog entry cannot be read.
7
15
  Newer SDKs may also provide `getBackend()` as a lookup over the same catalog.
8
16
 
@@ -21,7 +21,7 @@ export declare function defaultTangleSandboxCapabilities(harness?: HarnessType):
21
21
  * The backend catalog is the authority for harness-specific interactions.
22
22
  * An absent entry means the provider cannot prove any interaction support.
23
23
  */
24
- export declare function narrowTangleCapabilitiesToBackend(declared: AgentEnvironmentCapabilities, backend: BackendRegistryEntry | undefined): AgentEnvironmentCapabilities;
24
+ export declare function narrowTangleCapabilitiesToBackend(declared: AgentEnvironmentCapabilities, backend: BackendRegistryEntry | undefined, backendSelected?: boolean): AgentEnvironmentCapabilities;
25
25
  /**
26
26
  * Adapter-surface facts that gate declared capabilities: which methods this
27
27
  * process can actually call. Every fact defaults to false when it cannot be
@@ -100,6 +100,7 @@ export function defaultTangleSandboxCapabilities(harness) {
100
100
  create: {
101
101
  egress: ["open", "strict", "blocked"],
102
102
  billingOwner: true,
103
+ runtimeAttachments: { mcp: true },
103
104
  },
104
105
  // This is intent only. Narrowing requires both raw TEE evidence and the
105
106
  // caller's external provider-key verifier before the flag survives.
@@ -144,7 +145,11 @@ export function defaultTangleSandboxCapabilities(harness) {
144
145
  * The backend catalog is the authority for harness-specific interactions.
145
146
  * An absent entry means the provider cannot prove any interaction support.
146
147
  */
147
- export function narrowTangleCapabilitiesToBackend(declared, backend) {
148
+ export function narrowTangleCapabilitiesToBackend(declared, backend, backendSelected = true) {
149
+ if (backendSelected && backend?.capabilities.runtimeAttachments?.mcp !== true && declared.create?.runtimeAttachments !== undefined) {
150
+ const { runtimeAttachments: _runtimeAttachments, ...create } = declared.create;
151
+ declared = { ...declared, create };
152
+ }
148
153
  if (declared.interactions === undefined || backend === undefined) {
149
154
  if (declared.interactions === undefined)
150
155
  return declared;
@@ -2,6 +2,7 @@ import { parseBackendType, } from "@tangle-network/sandbox";
2
2
  import { AgentEnvironmentEgressPolicySchema, WorkspaceRequestSchema, workspaceCwdPathForBase, } from "@tangle-network/agent-interface/environment-provider";
3
3
  import { assertBoundedJson, boundedIdentifier, boundedString, MAX_ARRAY_LENGTH, MAX_MAP_ENTRIES, } from "./tangle-contract-safety.js";
4
4
  import { sandboxResourcesFromResourceRequest } from "./tangle-resources.js";
5
+ import { tangleRuntimeAttachments } from "./tangle-runtime-attachments.js";
5
6
  export function sandboxOptionsFromCreateInput(input, defaultBackend, parsedWorkspace) {
6
7
  const workspace = assertCreateInputShape(input, parsedWorkspace) ?? {};
7
8
  const profile = inlineAgentProfile(input.profile);
@@ -70,6 +71,9 @@ export function sandboxOptionsFromCreateInput(input, defaultBackend, parsedWorks
70
71
  ...(base.backend ?? {}),
71
72
  type: backend,
72
73
  profile,
74
+ ...(input.runtimeAttachments === undefined ? {} : {
75
+ runtimeAttachments: tangleRuntimeAttachments(input.runtimeAttachments, profile),
76
+ }),
73
77
  },
74
78
  };
75
79
  return mapped;
@@ -164,10 +168,14 @@ export function assertCreateInputShape(input, parsedWorkspace) {
164
168
  "idempotencyKey",
165
169
  "signal",
166
170
  "providerOptions",
171
+ "runtimeAttachments",
167
172
  ])
168
173
  keys.delete(key);
169
174
  if (keys.size > 0)
170
175
  throw new Error("Tangle create input contains unsupported fields");
176
+ if (input.runtimeAttachments !== undefined) {
177
+ tangleRuntimeAttachments(input.runtimeAttachments, typeof input.profile === "object" ? input.profile : undefined);
178
+ }
171
179
  if (typeof input.profile === "string") {
172
180
  boundedIdentifier(input.profile, "Tangle profile reference");
173
181
  }
@@ -198,6 +206,9 @@ export function assertMappedCreateOptions(options) {
198
206
  throw new Error("Tangle mapped create options must be an object");
199
207
  }
200
208
  assertBoundedJson(options);
209
+ if (options.backend?.runtimeAttachments !== undefined) {
210
+ tangleRuntimeAttachments(options.backend.runtimeAttachments, options.backend.profile);
211
+ }
201
212
  if (Object.hasOwn(options, "providerOptions")) {
202
213
  throw new Error("Tangle mapped create providerOptions are not supported");
203
214
  }
@@ -17,7 +17,7 @@ export declare function promptOptionsFromTurnInput(input: AgentTurnInput, target
17
17
  * anywhere, which is the silent substitution the SDK's own `model` field
18
18
  * exists to prevent.
19
19
  */
20
- declare const SANDBOX_BACKEND_FIELD_LIST: readonly ["type", "profile", "model", "server", "interactions", "metadata"];
20
+ declare const SANDBOX_BACKEND_FIELD_LIST: readonly ["runtimeAttachments", "type", "profile", "model", "server", "interactions", "metadata"];
21
21
  /**
22
22
  * The `BackendConfig["model"]` fields the Sandbox prompt options declare.
23
23
  *
@@ -1,6 +1,7 @@
1
1
  import { agentProfileSchema, CONTRACT_MAX_JSON_BYTES, boundedEventContentRecordSchema, isBoundedEventContentJson, AgentExactRunControlRefSchema, AgentTurnInputSchema, ContextTransferReceiptSchema, contextTransferResultMatchesRequest, } from "@tangle-network/agent-interface";
2
2
  import { tokenUsageFromData } from "./tangle-result-values.js";
3
3
  import { assertBoundedJson, boundedString } from "./tangle-contract-safety.js";
4
+ import { tangleRuntimeAttachments } from "./tangle-runtime-attachments.js";
4
5
  export function promptFromTurnInput(input) {
5
6
  AgentTurnInputSchema.parse(input);
6
7
  if (input.parts)
@@ -67,6 +68,7 @@ export function promptOptionsFromTurnInput(input, target) {
67
68
  * exists to prevent.
68
69
  */
69
70
  const SANDBOX_BACKEND_FIELD_LIST = [
71
+ "runtimeAttachments",
70
72
  "type",
71
73
  "profile",
72
74
  "model",
@@ -138,6 +140,11 @@ function sandboxPromptBackend(value) {
138
140
  ? {}
139
141
  : { metadata: sandboxPromptBackendMetadata(present.metadata) }),
140
142
  };
143
+ if (present.runtimeAttachments !== undefined) {
144
+ Object.assign(backend, {
145
+ runtimeAttachments: tangleRuntimeAttachments(present.runtimeAttachments, backend.profile),
146
+ });
147
+ }
141
148
  assertBoundedJson(backend);
142
149
  return backend;
143
150
  }
@@ -1,5 +1,5 @@
1
1
  import { AgentEnvironmentCapabilitiesSchema, createAgentEnvironmentWithIdempotency, } from "@tangle-network/agent-interface/environment-provider";
2
- import { deepFreeze, harnessTypeSchema } from "@tangle-network/agent-interface";
2
+ import { canonicalCandidateDigest, deepFreeze, harnessTypeSchema } from "@tangle-network/agent-interface";
3
3
  import { createTangleExactProcessProvider, } from "./exact-process.js";
4
4
  import { capabilitiesForClient, defaultTangleSandboxCapabilities, narrowTangleCapabilitiesToBackend, } from "./tangle-capabilities.js";
5
5
  import { sandboxInstanceAsEnvironment } from "./tangle-environment.js";
@@ -34,10 +34,10 @@ export function createTangleProvider(options) {
34
34
  if (!exactProcess && configured.exactProcess) {
35
35
  throw new Error("Tangle capabilities cannot advertise exactProcess without exactProcess configuration");
36
36
  }
37
- const backend = configured.interactions === undefined
37
+ const backend = configured.interactions === undefined && configured.create?.runtimeAttachments === undefined
38
38
  ? undefined
39
39
  : await resolveBackend(options.client, backendType);
40
- const narrowed = narrowTangleCapabilitiesToBackend(configured, backend);
40
+ const narrowed = narrowTangleCapabilitiesToBackend(configured, backend, backendType !== undefined);
41
41
  return exactProcess
42
42
  ? {
43
43
  ...narrowed,
@@ -66,10 +66,20 @@ export function createTangleProvider(options) {
66
66
  throw new Error("Tangle mapped create options must preserve input idempotencyKey");
67
67
  }
68
68
  assertMappedSecretNames(createOptions);
69
+ if (input.runtimeAttachments !== undefined &&
70
+ (createOptions.backend?.runtimeAttachments === undefined ||
71
+ canonicalCandidateDigest(input.runtimeAttachments) !== canonicalCandidateDigest(createOptions.backend.runtimeAttachments))) {
72
+ throw new Error("Tangle mapped create options must preserve runtime attachments");
73
+ }
69
74
  // Backend selection belongs to the create projection. The provider-wide
70
75
  // default cannot describe a sandbox that this projection routes elsewhere.
71
76
  const declaredCapabilities = await resolveDeclaredCapabilities(createOptions.backend?.type);
72
77
  narrowedProviderCapabilities(declaredCapabilities);
78
+ if (createOptions.backend?.runtimeAttachments !== undefined) {
79
+ if (createOptions.backend.type === undefined || declaredCapabilities.create?.runtimeAttachments?.mcp !== true) {
80
+ throw new Error("Tangle runtime attachments are not supported by the selected backend deployment");
81
+ }
82
+ }
73
83
  input.signal?.throwIfAborted();
74
84
  const createPromise = options.client.create(createOptions, input.signal ? { signal: input.signal } : undefined);
75
85
  let box;
@@ -0,0 +1,3 @@
1
+ import { type AgentProfile, type AgentRuntimeAttachments } from "@tangle-network/agent-interface";
2
+ /** Runtime bindings retain their own identity and cannot replace profile tools. */
3
+ export declare function tangleRuntimeAttachments(value: unknown, profile?: AgentProfile): AgentRuntimeAttachments;
@@ -0,0 +1,14 @@
1
+ import { AgentRuntimeAttachmentsSchema, } from "@tangle-network/agent-interface";
2
+ /** Runtime bindings retain their own identity and cannot replace profile tools. */
3
+ export function tangleRuntimeAttachments(value, profile) {
4
+ const attachments = AgentRuntimeAttachmentsSchema.parse(value);
5
+ for (const [alias, server] of Object.entries(attachments.mcp)) {
6
+ if (server.enabled === false) {
7
+ throw new Error(`Tangle runtime MCP attachment cannot be disabled: ${alias}`);
8
+ }
9
+ if (Object.hasOwn(profile?.mcp ?? {}, alias)) {
10
+ throw new Error(`Tangle runtime MCP attachment conflicts with profile: ${alias}`);
11
+ }
12
+ }
13
+ return attachments;
14
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-provider-tangle",
3
- "version": "1.1.11",
3
+ "version": "1.2.0",
4
4
  "description": "AgentEnvironmentProvider adapter for Tangle sandboxes",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,6 +43,8 @@
43
43
  "dist/tangle-deployment-capabilities.js",
44
44
  "dist/tangle-create-options.d.ts",
45
45
  "dist/tangle-create-options.js",
46
+ "dist/tangle-runtime-attachments.d.ts",
47
+ "dist/tangle-runtime-attachments.js",
46
48
  "dist/tangle-environment-values.d.ts",
47
49
  "dist/tangle-environment-values.js",
48
50
  "dist/tangle-environment.d.ts",
@@ -98,7 +100,7 @@
98
100
  "@tangle-network/agent-interface": "^2.6.1"
99
101
  },
100
102
  "peerDependencies": {
101
- "@tangle-network/sandbox": ">=0.34.6 <1.0.0"
103
+ "@tangle-network/sandbox": ">=0.39.0 <1.0.0"
102
104
  },
103
105
  "peerDependenciesMeta": {
104
106
  "@tangle-network/sandbox": {
@@ -108,7 +110,7 @@
108
110
  "devDependencies": {
109
111
  "@tangle-network/agent-eval": "0.173.1",
110
112
  "@tangle-network/agent-runtime": "0.192.2",
111
- "@tangle-network/sandbox": "0.37.0",
113
+ "@tangle-network/sandbox": "0.39.0",
112
114
  "@types/node": "26.4.1",
113
115
  "typescript": "7.0.2",
114
116
  "vitest": "4.1.11",