@akira-tl/forgerelay 1.2.0-rc.1 → 1.2.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/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable ForgeRelay changes are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.1] - 2026-09-13
8
+
9
+ ### Fixed
10
+
11
+ - Reconstruct relayed and Composite Workspace Panel presentation from durable Workspace state across modern/stateless MCP requests, preventing the ChatGPT Activity Panel from remaining on `Waiting...` after a relayed `open_workspace`; closed Workspaces remain non-renderable.
12
+
13
+ ### Changed
14
+
15
+ - Added Composite + Relay execution-context coverage to the authoritative `workspace-mcp` test shard, with Shell Instructions enabled explicitly where that opt-in behavior is under test.
16
+
17
+ ## [1.2.0] - 2026-09-13
18
+
19
+ ### Changed
20
+
21
+ - Promoted the accepted Config System v2 release candidate to stable `1.2.0` without changing product behavior from `1.2.0-rc.1`; stable publication reuses the same cross-platform verified-artifact release path and moves the npm stable channel to `latest` only after fresh, upgrade, migration, and packaged-runtime acceptance pass.
22
+
7
23
  ## [1.2.0-rc.1] - 2026-09-13
8
24
 
9
25
  ### Added
package/dist/server.js CHANGED
@@ -30,7 +30,7 @@ import { formatPathForPrompt } from "./workspaces/resources/skills.js";
30
30
  import { WorkspaceTaskReminderTracker } from "./workspaces/tasks/workspace-task-reminders.js";
31
31
  import { WorkspaceTaskStore } from "./workspaces/tasks/workspace-tasks.js";
32
32
  import { WorkspaceCheckpointStore } from "./workspaces/state/workspace-checkpoints.js";
33
- import { compactWorkspacePresentation } from "./workspaces/presentation/workspace-presentation.js";
33
+ import { compactCompositeWorkspacePresentation, compactRelayedWorkspacePresentation, compactWorkspacePresentation, } from "./workspaces/presentation/workspace-presentation.js";
34
34
  import { formatAgentsPath } from "./workspaces.js";
35
35
  import { summarizeSubagentProfile } from "./subagents/profiles.js";
36
36
  import { formatSubagentProviderAvailabilitySummary } from "./subagents/providers/availability.js";
@@ -442,8 +442,26 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
442
442
  };
443
443
  const workspacePanelState = async (workspaceId) => {
444
444
  const remembered = workspacePanelStates.get(workspaceId);
445
- if (remoteWorkspaces.has(workspaceId) || compositeWorkspaces.has(workspaceId)) {
446
- return remembered;
445
+ if (compositeWorkspaces.has(workspaceId)) {
446
+ if (remembered)
447
+ return remembered;
448
+ const composite = compositeWorkspaces.get(workspaceId);
449
+ return composite.status === "active"
450
+ ? compactCompositeWorkspacePresentation(composite)
451
+ : undefined;
452
+ }
453
+ if (remoteWorkspaces.has(workspaceId)) {
454
+ if (remembered)
455
+ return remembered;
456
+ try {
457
+ const inspected = await remoteWorkspaces.inspectWorkspace(workspaceId);
458
+ return inspected.status === "closed" || inspected.state === "closed"
459
+ ? undefined
460
+ : compactRelayedWorkspacePresentation(inspected);
461
+ }
462
+ catch {
463
+ return undefined;
464
+ }
447
465
  }
448
466
  try {
449
467
  const workspace = workspaces.getWorkspace(workspaceId);
@@ -37,6 +37,27 @@ export function compactWorkspacePresentation(card) {
37
37
  presentation.presentationRevision = presentationRevision(card, presentation);
38
38
  return presentation;
39
39
  }
40
+ export function compactCompositeWorkspacePresentation(composite) {
41
+ return compactWorkspacePresentation({
42
+ workspaceId: composite.id,
43
+ kind: "composite",
44
+ name: composite.name,
45
+ path: composite.name,
46
+ members: composite.members,
47
+ summary: { members: composite.members.length, status: composite.status },
48
+ });
49
+ }
50
+ export function compactRelayedWorkspacePresentation(inspected) {
51
+ return compactWorkspacePresentation({
52
+ workspaceId: inspected.workspaceId,
53
+ kind: inspected.kind,
54
+ root: inspected.root,
55
+ path: inspected.root,
56
+ mode: inspected.mode,
57
+ sourceRoot: inspected.sourceRoot,
58
+ summary: { mode: inspected.mode, relay: inspected.relay },
59
+ });
60
+ }
40
61
  function projectExecutionContext(value) {
41
62
  if (!value || typeof value !== "object" || Array.isArray(value))
42
63
  return undefined;
@@ -68,6 +68,7 @@ export async function startForge(t, options) {
68
68
  ...(options.taskReminderInterval !== undefined ? { taskReminderInterval: options.taskReminderInterval } : {}),
69
69
  ...(options.mediaMaxBytes !== undefined ? { mediaMaxBytes: options.mediaMaxBytes } : {}),
70
70
  ...(options.mcpServers ? { mcpServers: options.mcpServers } : {}),
71
+ ...(options.shellInstructions !== undefined ? { shellInstructions: options.shellInstructions } : {}),
71
72
  ...(options.hooks ? { hooks: options.hooks } : {}),
72
73
  }, null, 2));
73
74
  const env = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akira-tl/forgerelay",
3
- "version": "1.2.0-rc.1",
3
+ "version": "1.2.1",
4
4
  "description": "Local development control plane for MCP coding agents.",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/Akira-TL/forgerelay#readme",
@@ -67,7 +67,7 @@
67
67
  "release:push-ready": "node scripts/release/push-ready.mjs",
68
68
  "postinstall": "node scripts/fix-node-pty-permissions.mjs",
69
69
  "start": "node dist/cli.js serve",
70
- "test": "node --test scripts/debug/runtime.test.mjs scripts/config/schema-text.test.mjs scripts/release-proof.test.mjs scripts/release/release-gate.test.mjs scripts/release/push-ready.test.mjs scripts/release/release-version.test.mjs && tsx src/mcp/oauth/router.test.ts && tsx src/workspaces/relay/auth/remote-auth-cli.test.ts && tsx src/workspaces/relay/auth/remote-ssh-auth-cli.test.ts && tsx src/workspaces/relay/tests/lifecycle.test.ts && tsx src/workspaces/relay/tests/routing.test.ts && tsx src/workspaces/relay/tests/ssh.test.ts && tsx src/workspaces/relay/tests/process.test.ts && tsx src/workspaces/relay/tests/recovery.test.ts && tsx src/workspaces/relay/tests/checkpoint.test.ts && tsx src/runtime/config/definition/schema.test.ts && tsx src/runtime/config/resolution/resolver.test.ts && tsx src/runtime/security/project-execution-trust.test.ts && tsx src/runtime/config/resolution/hooks.test.ts && tsx src/workspaces/state/project-context.test.ts && tsx src/runtime/config/config.test.ts && tsx src/runtime/config/external-mcp-registry.test.ts && tsx src/runtime/config/external-mcp-auth-store.test.ts && tsx src/mcp/operations/external-mcp/external-mcp-oauth.test.ts && tsx src/cli/mcp/status.test.ts && tsx src/cli/mcp/diagnostics.test.ts && tsx src/cli/mcp/external-mcp.test.ts && tsx src/runtime/shell/command-shell-runtime.test.ts && tsx src/runtime/instructions/shell-instructions.test.ts && tsx src/runtime/instructions/powershell-skill.test.ts && tsx src/cli/shell/setup.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/runtime/managed-language-servers.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/operations/project-trust.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/runtime/logging/logger.test.ts && tsx src/runtime/logging/proxy-trust.test.ts && tsx src/runtime/state/lock/file-lock.test.ts && tsx src/runtime/state/runtime-lease.test.ts && tsx src/mcp/panel/mcp-app-template.test.ts && tsx src/mcp/hooks/hooks.test.ts && tsx src/mcp/hooks/hooks-trust.test.ts && tsx src/mcp/operations/external-mcp/external-mcp-transform.test.ts && tsx src/mcp/hooks/external-mcp-transform-trust.test.ts && tsx src/mcp/operations/external-mcp/external-mcp-trust.test.ts && tsx src/mcp/server/core/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/mcp/request-context.test.ts && tsx src/mcp/request-meta.test.ts && tsx src/mcp/artifacts/incoming-artifacts.test.ts && tsx src/mcp/artifacts/artifact-download.test.ts && tsx src/ui/core/card-types.test.ts && tsx src/ui/activity/model.test.ts && tsx src/ui/activity/detail-card.test.ts && tsx src/ui/review/patch-display.test.ts && tsx src/ui/core/tool-display.test.ts && tsx src/mcp/filesystem/apply-patch.test.ts && tsx src/mcp/process/process-platform.test.ts && tsx src/mcp/process/process-sessions.test.ts && tsx src/mcp/server/transport/mcp-sessions.test.ts && tsx src/mcp/server/transport/server-shutdown.test.ts && tsx src/mcp/server/operations/mutation-diagnostics.test.ts && tsx src/mcp/server/operations/hooks.test.ts && tsx src/subagents/providers/adapters/codex.test.ts && tsx src/subagents/providers/adapters/pi.test.ts && tsx src/subagents/providers/registry.test.ts && tsx src/subagents/providers/availability.test.ts && tsx src/subagents/profiles.test.ts && tsx src/subagents/cli-target.test.ts && tsx src/subagents/sessions/store.test.ts && tsx src/subagents/sessions/manager.test.ts && tsx src/subagents/sessions/project-trust.test.ts && tsx src/subagents/sessions/mcp/capability.server.test.ts && tsx src/subagents/sessions/mcp/continuation.server.test.ts && tsx src/subagents/sessions/mcp/lifecycle.server.test.ts && tsx src/subagents/sessions/mcp/reconciliation.server.test.ts && tsx src/subagents/sessions/mcp/routing.server.test.ts && tsx src/mcp/filesystem/roots.test.ts && tsx src/mcp/filesystem/file-mutations.test.ts && tsx src/mcp/operations/edit-preflight.test.ts && tsx src/workspaces/resources/skills.test.ts && tsx src/runtime/state/db/migrations.test.ts && tsx src/workspaces/state/workspace-store.test.ts && tsx src/workspaces/tasks/workspace-tasks.test.ts && tsx src/workspaces/tasks/workspace-task-reminders.test.ts && tsx src/activity/history/audit-store.test.ts && tsx src/activity/history/bash-output-store.test.ts && tsx src/activity/runtime/lifecycle.test.ts && tsx src/activity/history/query-service.test.ts && tsx src/mcp/operations/core-operation-executor.test.ts && tsx src/mcp/operations/bulk-mutation.test.ts && tsx src/mcp/operations/batch/scheduler.test.ts && tsx src/mcp/operations/batch/executor-policy.test.ts && tsx src/workspaces.test.ts && tsx src/workspaces/conversation-checkout.test.ts && tsx src/workspaces/conversation-worktree.test.ts && tsx src/workspaces/git/worktree-recovery.test.ts && tsx src/mcp/server/workspace/workspace-inventory.test.ts && tsx src/mcp/server/workspace/workspace-recovery.test.ts && tsx src/mcp/server/workspace/workspace-checkpoint.test.ts && tsx src/workspaces/review/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && npm run build:app && tsx src/mcp/process/server.test.ts && tsx src/mcp/panel/server.test.ts && tsx src/mcp/server/server.test.ts && tsx src/mcp/oauth/oauth-store.test.ts && tsx src/cli/maintenance.test.ts && tsx src/cli/maintenance-prune.test.ts && tsx src/cli/cli.test.ts",
70
+ "test": "node --test scripts/debug/runtime.test.mjs scripts/config/schema-text.test.mjs scripts/release-proof.test.mjs scripts/release/release-gate.test.mjs scripts/release/push-ready.test.mjs scripts/release/release-version.test.mjs && tsx src/mcp/oauth/router.test.ts && tsx src/workspaces/relay/auth/remote-auth-cli.test.ts && tsx src/workspaces/relay/auth/remote-ssh-auth-cli.test.ts && tsx src/workspaces/relay/tests/lifecycle.test.ts && tsx src/workspaces/relay/tests/routing.test.ts && tsx src/workspaces/relay/tests/ssh.test.ts && tsx src/workspaces/relay/tests/process.test.ts && tsx src/workspaces/relay/tests/recovery.test.ts && tsx src/workspaces/relay/tests/checkpoint.test.ts && tsx src/workspaces/relay/tests/composite.test.ts && tsx src/runtime/config/definition/schema.test.ts && tsx src/runtime/config/resolution/resolver.test.ts && tsx src/runtime/security/project-execution-trust.test.ts && tsx src/runtime/config/resolution/hooks.test.ts && tsx src/workspaces/state/project-context.test.ts && tsx src/runtime/config/config.test.ts && tsx src/runtime/config/external-mcp-registry.test.ts && tsx src/runtime/config/external-mcp-auth-store.test.ts && tsx src/mcp/operations/external-mcp/external-mcp-oauth.test.ts && tsx src/cli/mcp/status.test.ts && tsx src/cli/mcp/diagnostics.test.ts && tsx src/cli/mcp/external-mcp.test.ts && tsx src/runtime/shell/command-shell-runtime.test.ts && tsx src/runtime/instructions/shell-instructions.test.ts && tsx src/runtime/instructions/powershell-skill.test.ts && tsx src/cli/shell/setup.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/runtime/managed-language-servers.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/operations/project-trust.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/runtime/logging/logger.test.ts && tsx src/runtime/logging/proxy-trust.test.ts && tsx src/runtime/state/lock/file-lock.test.ts && tsx src/runtime/state/runtime-lease.test.ts && tsx src/mcp/panel/mcp-app-template.test.ts && tsx src/mcp/hooks/hooks.test.ts && tsx src/mcp/hooks/hooks-trust.test.ts && tsx src/mcp/operations/external-mcp/external-mcp-transform.test.ts && tsx src/mcp/hooks/external-mcp-transform-trust.test.ts && tsx src/mcp/operations/external-mcp/external-mcp-trust.test.ts && tsx src/mcp/server/core/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/mcp/request-context.test.ts && tsx src/mcp/request-meta.test.ts && tsx src/mcp/artifacts/incoming-artifacts.test.ts && tsx src/mcp/artifacts/artifact-download.test.ts && tsx src/ui/core/card-types.test.ts && tsx src/ui/activity/model.test.ts && tsx src/ui/activity/detail-card.test.ts && tsx src/ui/review/patch-display.test.ts && tsx src/ui/core/tool-display.test.ts && tsx src/mcp/filesystem/apply-patch.test.ts && tsx src/mcp/process/process-platform.test.ts && tsx src/mcp/process/process-sessions.test.ts && tsx src/mcp/server/transport/mcp-sessions.test.ts && tsx src/mcp/server/transport/server-shutdown.test.ts && tsx src/mcp/server/operations/mutation-diagnostics.test.ts && tsx src/mcp/server/operations/hooks.test.ts && tsx src/subagents/providers/adapters/codex.test.ts && tsx src/subagents/providers/adapters/pi.test.ts && tsx src/subagents/providers/registry.test.ts && tsx src/subagents/providers/availability.test.ts && tsx src/subagents/profiles.test.ts && tsx src/subagents/cli-target.test.ts && tsx src/subagents/sessions/store.test.ts && tsx src/subagents/sessions/manager.test.ts && tsx src/subagents/sessions/project-trust.test.ts && tsx src/subagents/sessions/mcp/capability.server.test.ts && tsx src/subagents/sessions/mcp/continuation.server.test.ts && tsx src/subagents/sessions/mcp/lifecycle.server.test.ts && tsx src/subagents/sessions/mcp/reconciliation.server.test.ts && tsx src/subagents/sessions/mcp/routing.server.test.ts && tsx src/mcp/filesystem/roots.test.ts && tsx src/mcp/filesystem/file-mutations.test.ts && tsx src/mcp/operations/edit-preflight.test.ts && tsx src/workspaces/resources/skills.test.ts && tsx src/runtime/state/db/migrations.test.ts && tsx src/workspaces/state/workspace-store.test.ts && tsx src/workspaces/tasks/workspace-tasks.test.ts && tsx src/workspaces/tasks/workspace-task-reminders.test.ts && tsx src/activity/history/audit-store.test.ts && tsx src/activity/history/bash-output-store.test.ts && tsx src/activity/runtime/lifecycle.test.ts && tsx src/activity/history/query-service.test.ts && tsx src/mcp/operations/core-operation-executor.test.ts && tsx src/mcp/operations/bulk-mutation.test.ts && tsx src/mcp/operations/batch/scheduler.test.ts && tsx src/mcp/operations/batch/executor-policy.test.ts && tsx src/workspaces.test.ts && tsx src/workspaces/conversation-checkout.test.ts && tsx src/workspaces/conversation-worktree.test.ts && tsx src/workspaces/git/worktree-recovery.test.ts && tsx src/mcp/server/workspace/workspace-inventory.test.ts && tsx src/mcp/server/workspace/workspace-recovery.test.ts && tsx src/mcp/server/workspace/workspace-checkpoint.test.ts && tsx src/workspaces/review/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && npm run build:app && tsx src/mcp/process/server.test.ts && tsx src/mcp/panel/server.test.ts && tsx src/mcp/server/server.test.ts && tsx src/mcp/oauth/oauth-store.test.ts && tsx src/cli/maintenance.test.ts && tsx src/cli/maintenance-prune.test.ts && tsx src/cli/cli.test.ts",
71
71
  "typecheck": "tsc -p tsconfig.json --noEmit",
72
72
  "release:check": "node scripts/release-version.mjs check",
73
73
  "release:tag-check": "node scripts/release-version.mjs tag",
@@ -13,6 +13,7 @@ import {
13
13
  } from "node:fs";
14
14
  import { join, resolve } from "node:path";
15
15
  import { setTimeout as delay } from "node:timers/promises";
16
+ import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
16
17
  import { debugRoot, repoRoot } from "../runtime.mjs";
17
18
 
18
19
  export function relayAcceptanceTopology() {
@@ -170,6 +171,37 @@ export function toolText(result) {
170
171
  .join("\n");
171
172
  }
172
173
 
174
+ export async function assertStatelessRelayPanel({ mcpUrl, accessToken, executionCheckout, expectedWorkspaceId }) {
175
+ const transport = new StreamableHTTPClientTransport(new URL(mcpUrl), {
176
+ requestInit: { headers: { authorization: `Bearer ${accessToken}` } },
177
+ });
178
+ const client = new Client(
179
+ { name: "forgerelay-relay-stateless-acceptance", version: "1.0.0" },
180
+ { versionNegotiation: { mode: { pin: "2026-07-28" } } },
181
+ );
182
+ try {
183
+ await client.connect(transport);
184
+ assert.equal(transport.sessionId, undefined);
185
+ const opened = await client.callTool({
186
+ name: "open_workspace",
187
+ arguments: { path: executionCheckout, relay: "execution", context: "none" },
188
+ });
189
+ assertToolOk(opened, "open relayed checkout over stateless HTTP");
190
+ assert.equal(opened.structuredContent.workspaceId, expectedWorkspaceId);
191
+ const panel = await client.callTool({
192
+ name: "activity_panel",
193
+ arguments: { workspaceId: expectedWorkspaceId },
194
+ _meta: { "dev.forgerelay/conversation": "relay-stateless-panel-acceptance" },
195
+ });
196
+ assertToolOk(panel, "render relayed Activity Panel over stateless HTTP");
197
+ assert.match(String(panel.structuredContent.turnId), /^turn_/);
198
+ assert.equal(panel._meta?.["forgerelay/activityPanelWorkspace"]?.workspaceId, expectedWorkspaceId);
199
+ } finally {
200
+ await client.close().catch(() => undefined);
201
+ }
202
+ pass("relay stateless Panel", "relayed open_workspace and activity_panel succeeded across modern stateless MCP requests");
203
+ }
204
+
173
205
  export function pass(label, detail) {
174
206
  console.log(`PASS ${label}: ${detail}`);
175
207
  }
@@ -2,23 +2,19 @@ import assert from "node:assert/strict";
2
2
  import { spawn, spawnSync } from "node:child_process";
3
3
  import { createHash, randomBytes, randomUUID } from "node:crypto";
4
4
  import { once } from "node:events";
5
- import { connect } from "node:net";
6
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
7
- import { join, resolve } from "node:path";
5
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
6
+ import { join } from "node:path";
8
7
  import { setTimeout as delay } from "node:timers/promises";
9
- import { debugRoot, repoRoot } from "./runtime.mjs";
10
- import { relayAcceptanceTopology, assertPortsFree, setupGitProject, runGit, taskStateFiles, findTaskStateFile, assertTaskBodyAbsentFromGateway, assertSafeRelayInspection, installRelayImageFixture, assertRelayedImageResult, assertToolOk, toolText, pass } from "./relay-accept/support.mjs";
11
-
8
+ import { repoRoot } from "./runtime.mjs";
9
+ import { relayAcceptanceTopology, assertPortsFree, setupGitProject, findTaskStateFile, assertTaskBodyAbsentFromGateway, assertSafeRelayInspection, installRelayImageFixture, assertRelayedImageResult, assertStatelessRelayPanel, assertToolOk, toolText, pass } from "./relay-accept/support.mjs";
12
10
  const {
13
- gatewayPort, executionPort, gatewayBaseUrl, gatewayMcpUrl, executionBaseUrl,
14
- acceptanceRoot, gatewayConfigDir, gatewayStateDir, gatewayWorktreeRoot,
15
- gatewayProjects, gatewayLocalProject, executionConfigDir, executionStateDir,
16
- executionWorktreeRoot, executionProjects, executionCheckout, executionWorktreeSource,
17
- bootstrapSecret, checkoutTaskBody, worktreeTaskBody, compositeMemberTaskBody, compositeTaskBody,
11
+ gatewayPort, executionPort, gatewayBaseUrl, gatewayMcpUrl, executionBaseUrl, acceptanceRoot,
12
+ gatewayConfigDir, gatewayStateDir, gatewayWorktreeRoot, gatewayProjects, gatewayLocalProject,
13
+ executionConfigDir, executionStateDir, executionWorktreeRoot, executionProjects, executionCheckout,
14
+ executionWorktreeSource, bootstrapSecret, checkoutTaskBody, worktreeTaskBody, compositeMemberTaskBody, compositeTaskBody,
18
15
  } = relayAcceptanceTopology();
19
16
  const gatewayOwnerToken = randomBytes(32).toString("base64url");
20
17
  const executionOwnerToken = randomBytes(32).toString("base64url");
21
-
22
18
  await assertPortsFree([gatewayPort, executionPort]);
23
19
  rmSync(acceptanceRoot, { recursive: true, force: true });
24
20
  mkdirSync(gatewayLocalProject, { recursive: true });
@@ -30,10 +26,8 @@ writeFileSync(join(gatewayLocalProject, "sentinel.txt"), "gateway-local-content\
30
26
  writeFileSync(join(executionCheckout, "sentinel.txt"), "execution-remote-content\n");
31
27
  writeFileSync(join(executionCheckout, "AGENTS.md"), `${bootstrapSecret}\n`);
32
28
  setupGitProject(executionWorktreeSource);
33
-
34
29
  writeAuthFile(gatewayConfigDir, gatewayOwnerToken, "relay-acceptance-gateway");
35
30
  writeAuthFile(executionConfigDir, executionOwnerToken, "relay-acceptance-execution");
36
-
37
31
  const executionEnv = instanceEnv({
38
32
  port: executionPort,
39
33
  baseUrl: executionBaseUrl,
@@ -106,6 +100,13 @@ try {
106
100
  assert.equal(openedB.structuredContent.workspaceId, checkoutRelayId);
107
101
  pass("relay persistent identity", `two Gateway conversations reused ${checkoutRelayId}`);
108
102
 
103
+ await assertStatelessRelayPanel({
104
+ mcpUrl: gatewayMcpUrl,
105
+ accessToken: oauth.accessToken,
106
+ executionCheckout,
107
+ expectedWorkspaceId: checkoutRelayId,
108
+ });
109
+
109
110
  assertRelayedImageResult(callTool(gatewayMcpUrl, oauth.accessToken, sessionB, nextId(), "read", {
110
111
  workspaceId: checkoutRelayId, path: "relay-image.png",
111
112
  }, conversationB), relayImageBase64, gatewayStateDir);