From d7a1f10582c03bff6a9301209e13625b6951a1e9 Mon Sep 17 00:00:00 2001 From: moonyue-w <300878504+moonyue-w@users.noreply.github.com> Date: Wed, 4 Nov 2026 20:17:00 +0800 Subject: [PATCH] feat: support BYOC tunnel bindings for MCP requests --- CHANGELOG.md | 2 ++ docs/api/forward/interfaces/Template.md | 8 +++++ .../forward/interfaces/TemplateNewParams.md | 8 +++++ .../interfaces/TemplateUpdateParams.md | 8 +++++ .../managed/interfaces/DeploymentNewParams.md | 8 +++++ .../interfaces/DeploymentUpdateParams.md | 8 +++++ .../interfaces/ManagedAgentsDeployment.md | 8 +++++ .../managed/interfaces/SessionNewParams.md | 8 +++++ src/forward/types.ts | 6 ++++ src/managed/types.ts | 8 +++++ tests/byoc-tunnel.test.mjs | 34 +++++++++++++++++++ tests/types-official-alignment.ts | 15 ++++++++ 12 files changed, 121 insertions(+) create mode 100644 tests/byoc-tunnel.test.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 224e865..8a8da6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ existing `0.1.0` release; earlier development prereleases are not listed. ### Added +- Bind a Managed Session to a BYOC MCP tunnel with `tunnel_id` at creation. On Forward, set the tunnel on a Template for its sessions to inherit. Managed Deployments also accept and return the binding. Template and Deployment updates can replace or clear it. + - Add typed Forward Session budgets across create/update responses and events, including omission, explicit null clearing and decimal-string limits. - Add Forward Session `config.model` overrides and model `speed` configuration, plus optional approval evaluation details, Outcome/status event fields and cumulative top-level Credits on `session.usage`. - Add `startup_script` to Forward/Managed Environment models and Managed cloud/self-hosted parameters. It runs on sandbox start/resume; the external worker controls self-hosted execution. diff --git a/docs/api/forward/interfaces/Template.md b/docs/api/forward/interfaces/Template.md index dbdfccb..4231867 100644 --- a/docs/api/forward/interfaces/Template.md +++ b/docs/api/forward/interfaces/Template.md @@ -138,6 +138,14 @@ Template 名称。 *** +### tunnel\_id? + +> `optional` **tunnel\_id?**: `string` + +BYOC tunnel configured for this template, when bound. + +*** + ### type > **type**: `string` diff --git a/docs/api/forward/interfaces/TemplateNewParams.md b/docs/api/forward/interfaces/TemplateNewParams.md index 229bfc5..684238d 100644 --- a/docs/api/forward/interfaces/TemplateNewParams.md +++ b/docs/api/forward/interfaces/TemplateNewParams.md @@ -142,6 +142,14 @@ System Prompt,最多 100,000 个字符。 *** +### tunnel\_id? + +> `optional` **tunnel\_id?**: `string` + +BYOC tunnel inherited by sessions created from this template. + +*** + ### vaults? > `optional` **vaults?**: `Record`\<`string`, [`ResourceBindingParam`](ResourceBindingParam.md)\> \| `null` diff --git a/docs/api/forward/interfaces/TemplateUpdateParams.md b/docs/api/forward/interfaces/TemplateUpdateParams.md index f7d27c8..5158673 100644 --- a/docs/api/forward/interfaces/TemplateUpdateParams.md +++ b/docs/api/forward/interfaces/TemplateUpdateParams.md @@ -142,6 +142,14 @@ Positive maximum tool-call rounds per Turn. Omit to preserve the limit; null cle *** +### tunnel\_id? + +> `optional` **tunnel\_id?**: `string` \| `null` + +Omit to keep the current tunnel, or pass null to clear the binding. + +*** + ### vaults? > `optional` **vaults?**: `Record`\<`string`, [`ResourceBindingParam`](ResourceBindingParam.md)\> \| `null` diff --git a/docs/api/managed/interfaces/DeploymentNewParams.md b/docs/api/managed/interfaces/DeploymentNewParams.md index b9563a7..9e37932 100644 --- a/docs/api/managed/interfaces/DeploymentNewParams.md +++ b/docs/api/managed/interfaces/DeploymentNewParams.md @@ -102,6 +102,14 @@ timezone. *** +### tunnel\_id? + +> `optional` **tunnel\_id?**: `string` + +BYOC tunnel for MCP requests from sessions created by this deployment. + +*** + ### vault\_ids? > `optional` **vault\_ids?**: `string`[] \| `null` diff --git a/docs/api/managed/interfaces/DeploymentUpdateParams.md b/docs/api/managed/interfaces/DeploymentUpdateParams.md index 26a4ae8..57bd842 100644 --- a/docs/api/managed/interfaces/DeploymentUpdateParams.md +++ b/docs/api/managed/interfaces/DeploymentUpdateParams.md @@ -102,6 +102,14 @@ timezone. *** +### tunnel\_id? + +> `optional` **tunnel\_id?**: `string` \| `null` + +Omit to preserve the tunnel binding, set an ID to replace it, or pass null to clear it. + +*** + ### vault\_ids? > `optional` **vault\_ids?**: `string`[] \| `null` diff --git a/docs/api/managed/interfaces/ManagedAgentsDeployment.md b/docs/api/managed/interfaces/ManagedAgentsDeployment.md index 6520629..e578136 100644 --- a/docs/api/managed/interfaces/ManagedAgentsDeployment.md +++ b/docs/api/managed/interfaces/ManagedAgentsDeployment.md @@ -134,6 +134,14 @@ Any of "active", "paused". *** +### tunnel\_id? + +> `optional` **tunnel\_id?**: `string` + +BYOC tunnel bound to this deployment, when configured. + +*** + ### type > **type**: `"deployment"` diff --git a/docs/api/managed/interfaces/SessionNewParams.md b/docs/api/managed/interfaces/SessionNewParams.md index b219bec..0e7a7da 100644 --- a/docs/api/managed/interfaces/SessionNewParams.md +++ b/docs/api/managed/interfaces/SessionNewParams.md @@ -82,6 +82,14 @@ Human-readable session title. *** +### tunnel\_id? + +> `optional` **tunnel\_id?**: `string` + +BYOC tunnel used for MCP requests from this session. + +*** + ### vault\_ids? > `optional` **vault\_ids?**: `string`[] \| `null` diff --git a/src/forward/types.ts b/src/forward/types.ts index c899067..dd35b28 100644 --- a/src/forward/types.ts +++ b/src/forward/types.ts @@ -2929,6 +2929,8 @@ export interface TemplateNewParams { * 创建 Session 时默认使用的 Environment ID。 */ environment_id: string; + /** BYOC tunnel inherited by sessions created from this template. */ + tunnel_id?: string; /** * Template 描述,最多 2048 个字符。 */ @@ -3026,6 +3028,8 @@ export interface TemplateUpdateParams { * 替换默认 Environment ID;`null` 或空字符串表示清空。 */ environment_id?: string | null; + /** Omit to keep the current tunnel, or pass null to clear the binding. */ + tunnel_id?: string | null; /** * 整体替换默认 Vault 配置;按 Vault ID 组织,`null` 表示清空。 */ @@ -3117,6 +3121,8 @@ export interface Template { * 默认 Environment ID。 */ environment_id: string; + /** BYOC tunnel configured for this template, when bound. */ + tunnel_id?: string; vaults: Record; files: Record; github_repositories: Record; diff --git a/src/managed/types.ts b/src/managed/types.ts index aa933f0..fb95d30 100644 --- a/src/managed/types.ts +++ b/src/managed/types.ts @@ -1578,6 +1578,8 @@ export interface ManagedAgentsDeployment { * ID of the `environment` where sessions run. */ environment_id: string; + /** BYOC tunnel bound to this deployment, when configured. */ + tunnel_id?: string; /** * Events sent to each session immediately after creation. */ @@ -2100,6 +2102,8 @@ export interface DeploymentNewParams { * created from this deployment. */ environment_id: string; + /** BYOC tunnel for MCP requests from sessions created by this deployment. */ + tunnel_id?: string; /** * Events to send to each session immediately after creation. At least 1, * maximum 50. @@ -2167,6 +2171,8 @@ export interface DeploymentUpdateParams { * ID of the `environment` where sessions run. Omit to preserve. Cannot be cleared. */ environment_id?: string; + /** Omit to preserve the tunnel binding, set an ID to replace it, or pass null to clear it. */ + tunnel_id?: string | null; /** * Human-readable name. Must be non-empty. Omit to preserve. Cannot be cleared. */ @@ -7262,6 +7268,8 @@ export interface SessionNewParams { * ID of the `environment` defining the container configuration for this session. */ environment_id: string; + /** BYOC tunnel used for MCP requests from this session. */ + tunnel_id?: string; /** * Human-readable session title. */ diff --git a/tests/byoc-tunnel.test.mjs b/tests/byoc-tunnel.test.mjs new file mode 100644 index 0000000..981f3c7 --- /dev/null +++ b/tests/byoc-tunnel.test.mjs @@ -0,0 +1,34 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { testClient, response } from './helpers.mjs'; + +test('BYOC tunnel bindings use a top-level tunnel_id', async () => { + let body; + const capture = async request => { + body = await request.json(); + return response(body.tunnel_id ? { tunnel_id: body.tunnel_id } : {}); + }; + const managed = testClient('managed', capture); + const forward = testClient('forward', capture); + for (const binding of [{}, { tunnel_id: 'tnl_test' }]) { + await managed.sessions.create({ agent: 'agent_test', environment_id: 'env_test', ...binding }); + assert.deepEqual(body, { agent: 'agent_test', environment_id: 'env_test', metadata: body.metadata, ...binding }); + const params = { name: 'demo', model: 'auto', environment_id: 'env_test', ...binding }; + const template = await forward.templates.create(params); + assert.deepEqual(body, params); + assert.equal(template.tunnel_id, binding.tunnel_id); + const deploymentParams = { + name: 'demo', agent: 'agent_test', environment_id: 'env_test', + initial_events: [{ type: 'user.message', content: [{ type: 'text', text: 'hello' }] }], ...binding, + }; + const deployment = await managed.deployments.create(deploymentParams); + assert.deepEqual(body, deploymentParams); + assert.equal(deployment.tunnel_id, binding.tunnel_id); + } + for (const patch of [{}, { tunnel_id: 'tnl_other' }, { tunnel_id: null }]) { + await forward.templates.update('tmpl_test', patch); + assert.deepEqual(body, patch); + await managed.deployments.update('dep_test', patch); + assert.deepEqual(body, patch); + } +}); diff --git a/tests/types-official-alignment.ts b/tests/types-official-alignment.ts index f211b1c..33d406a 100644 --- a/tests/types-official-alignment.ts +++ b/tests/types-official-alignment.ts @@ -52,3 +52,18 @@ const memoryVersion: Pick = {version:2}; const redactedVersion: Pick = {operation:'updated',redacted:true}; const vaultCredentials: Pick = {credentials:[]}; void [forwardBudget,sessionCreate,sessionClear,modelRead,speedRead,badBudget,badCreateModel,approval,permission,approvalReason,usageEvent,credits,outcomeIteration,budgetEvent,startupForward,startupManaged,startupSelfHosted,nestedThreadModel,nestedCoordinatorModel,memoryVersion,redactedVersion,vaultCredentials]; + +declare const managedCreateWithTunnel: M.SessionCreateParams; +declare const templateWithTunnel: F.Template; +const sessionTunnel: string | undefined = managedCreateWithTunnel.tunnel_id; +const templateTunnel: string | undefined = templateWithTunnel.tunnel_id; +// @ts-expect-error A tunnel ID must be a string. +const invalidTunnel: Pick = { tunnel_id: 123 }; +const clearTunnel: Pick = { tunnel_id: null }; +void [sessionTunnel, templateTunnel, invalidTunnel, clearTunnel]; +declare const deploymentWithTunnel: M.ManagedAgentsDeployment; +const deploymentTunnel: string | undefined = deploymentWithTunnel.tunnel_id; +// @ts-expect-error Only updates can clear a deployment tunnel. +const invalidDeploymentTunnel: Pick = { tunnel_id: null }; +const clearDeploymentTunnel: Pick = { tunnel_id: null }; +void [deploymentTunnel, invalidDeploymentTunnel, clearDeploymentTunnel];