@akira-tl/forgerelay 1.2.3 → 1.2.4

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/capabilities/workspace/managed-worktrees/GUIDE.md +4 -2
  3. package/dist/mcp/server/core/schemas.js +6 -0
  4. package/dist/mcp/server/workspace/runtime/workspace-open-presentation.js +4 -2
  5. package/dist/mcp/server/workspace/runtime/workspace-open-schema.js +6 -1
  6. package/dist/mcp/server/workspace/runtime/workspace-open.js +19 -13
  7. package/dist/runtime/logging/logger.js +19 -19
  8. package/dist/ui/.vite/manifest.json +276 -276
  9. package/dist/ui/activity-panel-app.html +2 -2
  10. package/dist/ui/assets/activity-panel-app-Dq2xLIrs.css +1 -0
  11. package/dist/ui/assets/{activity-panel-app-BeDiwd7D.js → activity-panel-app-hkM8rX3z.js} +1 -1
  12. package/dist/ui/assets/{angular-html-DuoCE7nn.js → angular-html-UD0GEFFl.js} +1 -1
  13. package/dist/ui/assets/{angular-ts-D5tGk0hL.js → angular-ts-B2awh-Q1.js} +1 -1
  14. package/dist/ui/assets/{apl-lciOTPbw.js → apl-CpbhnJc8.js} +1 -1
  15. package/dist/ui/assets/{astro-vJbqfNnJ.js → astro-CkVVuF5B.js} +1 -1
  16. package/dist/ui/assets/{blade-C7655Q4K.js → blade-czG58aop.js} +1 -1
  17. package/dist/ui/assets/{c-DdHp1Jrw.js → c-DSq6h06b.js} +1 -1
  18. package/dist/ui/assets/{cobol-DgvzQxZP.js → cobol-CYnmhT2E.js} +1 -1
  19. package/dist/ui/assets/{coffee-BIjVUOUd.js → coffee-ClLhNqd5.js} +1 -1
  20. package/dist/ui/assets/{cpp-CU8tGzvI.js → cpp-BZDC6U43.js} +1 -1
  21. package/dist/ui/assets/{crystal-CUQAwsg9.js → crystal-BN9jCJyU.js} +1 -1
  22. package/dist/ui/assets/{css-DtMTqD3F.js → css-8dMfiihi.js} +1 -1
  23. package/dist/ui/assets/{edge-DoCX9RnL.js → edge-DemPBRNz.js} +1 -1
  24. package/dist/ui/assets/{elixir-DJxV9_U6.js → elixir-bLdHW8dz.js} +1 -1
  25. package/dist/ui/assets/{elm-DIgWqK4r.js → elm-DlZPcXmu.js} +1 -1
  26. package/dist/ui/assets/{erb-BHxP0c_e.js → erb-wmTqbHwm.js} +1 -1
  27. package/dist/ui/assets/{git-rebase-HbI_Vo8d.js → git-rebase-BqrZJVYO.js} +1 -1
  28. package/dist/ui/assets/{glimmer-js-CthY4Ty_.js → glimmer-js-CkPzdNHI.js} +1 -1
  29. package/dist/ui/assets/{glimmer-ts-B2-_A1Xz.js → glimmer-ts-Bd0vTdJC.js} +1 -1
  30. package/dist/ui/assets/{glsl-D9V9gq98.js → glsl-DKK0Kibz.js} +1 -1
  31. package/dist/ui/assets/{graphql-CddeAaS3.js → graphql-DmFr8ZvB.js} +1 -1
  32. package/dist/ui/assets/{hack-D_loGkOT.js → hack-BQcuk9lx.js} +1 -1
  33. package/dist/ui/assets/{haml-BoWDczd0.js → haml-Dbx0cG5B.js} +1 -1
  34. package/dist/ui/assets/{handlebars-ZoSOvw9y.js → handlebars-D0nD8yLi.js} +1 -1
  35. package/dist/ui/assets/{heavy-payload-DO34LwoZ.js → heavy-payload-CPOM8G8z.js} +3 -3
  36. package/dist/ui/assets/{html-CE-obXVo.js → html-9xkoIci1.js} +1 -1
  37. package/dist/ui/assets/{html-derivative-Cd-smAbW.js → html-derivative-Cg7n4YW0.js} +1 -1
  38. package/dist/ui/assets/{http-CZrkbrSl.js → http-Ckt_4_pP.js} +1 -1
  39. package/dist/ui/assets/{hurl-CxTuxBt1.js → hurl-BxMgqn9u.js} +1 -1
  40. package/dist/ui/assets/{java-BshLjCat.js → java-DxijgO-5.js} +1 -1
  41. package/dist/ui/assets/{javascript-D2pxQTiX.js → javascript-CusLzlRb.js} +1 -1
  42. package/dist/ui/assets/{jinja-kbfPP7-F.js → jinja-DBTRfImq.js} +1 -1
  43. package/dist/ui/assets/{jison-C2mt9l1M.js → jison-oCbVte0g.js} +1 -1
  44. package/dist/ui/assets/{json-DU_rg_AU.js → json-uq2yfoO2.js} +1 -1
  45. package/dist/ui/assets/{jsx-D_8aQMlx.js → jsx-B3AfJWyQ.js} +1 -1
  46. package/dist/ui/assets/{julia-BmefTaVV.js → julia-BFvq-UTm.js} +1 -1
  47. package/dist/ui/assets/{just-DXV8vk1S.js → just-Ck0W9Rcj.js} +1 -1
  48. package/dist/ui/assets/{latex-Cjm2MvGW.js → latex-BjKhTmaO.js} +1 -1
  49. package/dist/ui/assets/{liquid-DyQDlo0u.js → liquid-CsaE0q4b.js} +1 -1
  50. package/dist/ui/assets/{lua-B2A1vzzy.js → lua-D0MSBhv0.js} +1 -1
  51. package/dist/ui/assets/{marko-B9vOkXOU.js → marko-CTLxSPPr.js} +1 -1
  52. package/dist/ui/assets/{mdc-D42m6spU.js → mdc-BwDM_Bct.js} +1 -1
  53. package/dist/ui/assets/{nginx-TpfVxSUY.js → nginx-C5Q5rNaB.js} +1 -1
  54. package/dist/ui/assets/{nim-CJr5T1FK.js → nim-BjKHRt6k.js} +1 -1
  55. package/dist/ui/assets/{perl-cJZcsdm9.js → perl-BrABfniL.js} +1 -1
  56. package/dist/ui/assets/{php-CSZL5KRT.js → php-BWY__Khq.js} +1 -1
  57. package/dist/ui/assets/{pug-B1eMKzuA.js → pug-BD4PlwIK.js} +1 -1
  58. package/dist/ui/assets/{qml-Dl2_7zc9.js → qml-yQtdZNrK.js} +1 -1
  59. package/dist/ui/assets/{r-F0aaO9gG.js → r-D0tlixrf.js} +1 -1
  60. package/dist/ui/assets/{razor-BPnkSMCw.js → razor-CsmyuKJs.js} +1 -1
  61. package/dist/ui/assets/{regexp-BnGHMFbv.js → regexp-BXU4cTCD.js} +1 -1
  62. package/dist/ui/assets/{rst-B3CO7WYO.js → rst-CGlzAlrM.js} +1 -1
  63. package/dist/ui/assets/{ruby-B6VMzT1T.js → ruby-CRKDbfJj.js} +1 -1
  64. package/dist/ui/assets/{sas-Df2lHVuU.js → sas-CKDY6iPG.js} +1 -1
  65. package/dist/ui/assets/{scss-4XxI6sgu.js → scss-BCoXLSqD.js} +1 -1
  66. package/dist/ui/assets/{shellscript-DYASCN4r.js → shellscript-BVuRs_tF.js} +1 -1
  67. package/dist/ui/assets/{shellsession-Br6AGJO_.js → shellsession-alj1b7vN.js} +1 -1
  68. package/dist/ui/assets/{soy-p3ZNq1We.js → soy-BbQDrw9i.js} +1 -1
  69. package/dist/ui/assets/{sql-CAdv2OXL.js → sql-BdcqHaEA.js} +1 -1
  70. package/dist/ui/assets/{stata-Dx7yUbG_.js → stata-5IC5m_XB.js} +1 -1
  71. package/dist/ui/assets/{surrealql-6YsE8HQa.js → surrealql-BfVFLYzV.js} +1 -1
  72. package/dist/ui/assets/{svelte-YgzedwpD.js → svelte-B22aFi25.js} +1 -1
  73. package/dist/ui/assets/{templ-Bahqx3uD.js → templ-BRo7TcBX.js} +1 -1
  74. package/dist/ui/assets/{tex-o6fTQjkH.js → tex-CYhw-JbD.js} +1 -1
  75. package/dist/ui/assets/{ts-tags-BO-Co0Ju.js → ts-tags-C56aGOzq.js} +1 -1
  76. package/dist/ui/assets/{tsx-DtQe5AoH.js → tsx-IkFyQoro.js} +1 -1
  77. package/dist/ui/assets/{twig-BcUs6Kqc.js → twig-NkXaxf7l.js} +1 -1
  78. package/dist/ui/assets/{typescript-BPtM8h4u.js → typescript-AgbZakqy.js} +1 -1
  79. package/dist/ui/assets/{vue-5KkGUzJJ.js → vue-Cxz1Rk-f.js} +1 -1
  80. package/dist/ui/assets/{vue-html-Df_Xfsz9.js → vue-html-DWHLzP-w.js} +1 -1
  81. package/dist/ui/assets/{vue-vine-hUIe_u_Y.js → vue-vine-WR64TKte.js} +1 -1
  82. package/dist/ui/assets/{xml-C3Jy7as5.js → xml-CNdUn7jo.js} +1 -1
  83. package/dist/ui/assets/{xsl-BnF9OuFo.js → xsl-DLRRUZ98.js} +1 -1
  84. package/dist/ui/assets/{yaml-DyrFgCou.js → yaml-gltXViA8.js} +1 -1
  85. package/dist/workspace-colors.js +54 -0
  86. package/dist/workspaces/git/git-worktrees.js +53 -14
  87. package/dist/workspaces/inventory.js +2 -0
  88. package/dist/workspaces/presentation/workspace-presentation.js +13 -0
  89. package/dist/workspaces/relay/workspace-relay.js +3 -0
  90. package/dist/workspaces/sessions.js +28 -8
  91. package/dist/workspaces.js +9 -9
  92. package/docs/chatgpt-coding-workflow.md +16 -8
  93. package/docs/gotchas.md +2 -2
  94. package/package.json +2 -1
  95. package/scripts/ci/cmd-acceptance.mjs +3 -85
  96. package/scripts/ci/gates/acceptance-runtime.mjs +45 -0
  97. package/scripts/ci/gates/platform-tests.mjs +27 -17
  98. package/scripts/ci/gates/prepare-windows-shell-runtime.mjs +45 -0
  99. package/scripts/ci/gates/test-shard.mjs +29 -7
  100. package/scripts/ci/powershell51-acceptance.mjs +10 -91
  101. package/scripts/ci/pwsh-acceptance.mjs +7 -87
  102. package/scripts/ci/windows-product-acceptance.mjs +55 -0
  103. package/scripts/release/release-gate.test.mjs +121 -13
  104. package/scripts/release-parity.mjs +12 -1
  105. package/dist/ui/assets/activity-panel-app-DkfJzTZ3.css +0 -1
@@ -40,7 +40,9 @@ try {
40
40
  console.log(`Using prepared acceptance install: ${prefix}`);
41
41
  }
42
42
  const shim = join(prefix, "forgerelay.cmd");
43
+ const powershellShim = join(prefix, "forgerelay.ps1");
43
44
  assert.ok(existsSync(shim), `npm did not create the Windows launcher shim: ${shim}`);
45
+ assert.ok(existsSync(powershellShim), `npm did not create the PowerShell launcher shim: ${powershellShim}`);
44
46
 
45
47
  const installedRoot = join(prefix, "node_modules", "@akira-tl", "forgerelay");
46
48
  assert.ok(existsSync(join(installedRoot, "dist", "cli.js")), `installed package is missing dist/cli.js: ${installedRoot}`);
@@ -67,6 +69,14 @@ try {
67
69
  installedRoot,
68
70
  workspaceProbe,
69
71
  });
72
+ if (runtime.family !== "cmd") {
73
+ await exercisePackagedPowerShellLauncher({
74
+ ...runtime,
75
+ root,
76
+ projectRoot,
77
+ powershellShim,
78
+ });
79
+ }
70
80
  }
71
81
 
72
82
  await exercisePackagedElevationContract(installedRoot, { root, projectRoot, shim, cmd });
@@ -148,6 +158,51 @@ async function exercisePackagedRuntime({ family, executable, doctorIdentity, roo
148
158
  assert.equal(opened.instructionStatus, "loaded", `${family} shell Instructions were not advertised as loaded`);
149
159
  }
150
160
 
161
+ async function exercisePackagedPowerShellLauncher({ family, executable, doctorIdentity, root, projectRoot, powershellShim }) {
162
+ const configDir = join(root, `config-launcher-${family}`);
163
+ const stateDir = join(root, `state-launcher-${family}`);
164
+ await Promise.all([
165
+ mkdir(configDir, { recursive: true }),
166
+ mkdir(stateDir, { recursive: true }),
167
+ ]);
168
+ await writeFile(
169
+ join(configDir, "config.json"),
170
+ JSON.stringify({
171
+ host: "127.0.0.1",
172
+ port: 7678,
173
+ allowedRoots: [projectRoot],
174
+ stateDir,
175
+ worktreeRoot: join(root, `worktrees-launcher-${family}`),
176
+ commandShell: {
177
+ mode: "follow-launcher",
178
+ family,
179
+ executable,
180
+ },
181
+ shellInstructions: false,
182
+ }, null, 2),
183
+ "utf8",
184
+ );
185
+
186
+ const result = spawnSync(
187
+ executable,
188
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-File", powershellShim, "doctor"],
189
+ {
190
+ cwd: process.cwd(),
191
+ env: cleanAcceptanceEnv(configDir),
192
+ encoding: "utf8",
193
+ windowsHide: true,
194
+ timeout: 10_000,
195
+ },
196
+ );
197
+ if (result.error || result.status !== 0) {
198
+ throw new Error(
199
+ `Packaged PowerShell launcher failed for ${family}: ${result.error?.message ?? result.stderr ?? result.status}`,
200
+ );
201
+ }
202
+ assert.match(result.stdout ?? "", doctorIdentity);
203
+ assert.match(result.stdout ?? "", /Command shell source: launcher/);
204
+ }
205
+
151
206
  async function exercisePackagedElevationContract(installedRoot, { root, projectRoot, shim, cmd }) {
152
207
  const { assertRuntimePrivilegeAllowed, detectRuntimePrivilege, elevatedRuntimeWarning } = await importInstalled(
153
208
  installedRoot,
@@ -66,17 +66,41 @@ test("cross-platform cloud CI splits shared tests from bounded platform acceptan
66
66
  const workflow = await readFile(resolve(repoRoot, ".github/workflows/ci.yml"), "utf8");
67
67
  assert.match(workflow, /core-contract:/);
68
68
  assert.match(workflow, /core-tests:/);
69
- for (const shard of ["runtime-config", "workspace-mcp", "lsp", "subagent-ui-cli"]) {
69
+ for (const shard of [
70
+ "runtime-config",
71
+ "workspace-relay-auth",
72
+ "workspace-relay",
73
+ "workspace-lifecycle",
74
+ "workspace-state",
75
+ "mcp-core",
76
+ "mcp-server-ui",
77
+ "lsp",
78
+ "subagent",
79
+ "ui-cli",
80
+ ]) {
70
81
  assert.match(workflow, new RegExp(`\\b${shard}\\b`));
71
82
  }
72
83
  assert.match(workflow, /run:\s*npm run ci:contract/);
73
84
  assert.match(workflow, /run:\s*npm run ci:test-shard -- \$\{\{ matrix\.shard \}\}/);
74
85
  assert.match(workflow, /macos-platform:[\s\S]*run:\s*npm run ci:platform-tests/);
75
- assert.match(workflow, /windows-platform:[\s\S]*run:\s*npm run ci:platform-tests/);
76
- assert.match(workflow, /windows-platform:[\s\S]*timeout-minutes:\s*25/);
86
+ assert.match(workflow, /windows-platform:[\s\S]*runtime-process[\s\S]*workspace-filesystem/);
87
+ const windowsPlatform = workflow.match(/ windows-platform:[\s\S]*?(?=\n windows-product:)/)?.[0];
88
+ assert.ok(windowsPlatform, "Windows platform job must remain present");
89
+ assert.match(windowsPlatform, /uses:\s*actions\/cache@v4/);
90
+ assert.match(windowsPlatform, /path:\s*node_modules/);
91
+ assert.match(windowsPlatform, /runner\.os/);
92
+ assert.match(windowsPlatform, /runner\.arch/);
93
+ assert.match(windowsPlatform, /hashFiles\('\.nvmrc'\)/);
94
+ assert.match(windowsPlatform, /npm-11\.19\.1/);
95
+ assert.match(windowsPlatform, /hashFiles\('package-lock\.json'\)/);
96
+ assert.match(windowsPlatform, /id:\s*windows-npm-cli[\s\S]*path:\s*\.ci-npm-global/);
97
+ assert.match(windowsPlatform, /if:\s*steps\.windows-npm-cli\.outputs\.cache-hit != 'true'[\s\S]*npm install --global --prefix \.ci-npm-global npm@11\.19\.1/);
98
+ assert.match(windowsPlatform, /if:\s*steps\.windows-platform-deps\.outputs\.cache-hit != 'true'[\s\S]*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js ci --no-audit --no-fund/);
99
+ assert.match(windowsPlatform, /run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run ci:platform-tests -- \$\{\{ matrix\.shard \}\}/);
100
+ assert.doesNotMatch(windowsPlatform, /npx --yes npm@11\.19\.1/);
77
101
  assert.doesNotMatch(workflow, /run:\s*npm run ci:verify/);
78
102
  assert.doesNotMatch(workflow, /run:\s*npm test/);
79
- assert.doesNotMatch(workflow, /shell:/);
103
+ assert.doesNotMatch(workflow, /^\s*shell:/m);
80
104
  assert.doesNotMatch(workflow, /run:\s*\|/);
81
105
  });
82
106
 
@@ -138,7 +162,18 @@ test("release runtime and local parity share the checked-in Node contract", asyn
138
162
  assert.match(ciWorkflow, /npm install --global npm@11\.19\.1/);
139
163
  assert.ok(source.includes('["npm", "ci", "--no-audit", "--no-fund"]'));
140
164
  assert.ok(source.includes('["npm", "run", "ci:contract"]'));
141
- for (const shard of ["runtime-config", "workspace-mcp", "lsp", "subagent-ui-cli"]) {
165
+ for (const shard of [
166
+ "runtime-config",
167
+ "workspace-relay-auth",
168
+ "workspace-relay",
169
+ "workspace-lifecycle",
170
+ "workspace-state",
171
+ "mcp-core",
172
+ "mcp-server-ui",
173
+ "lsp",
174
+ "subagent",
175
+ "ui-cli",
176
+ ]) {
142
177
  assert.match(source, new RegExp(`\\"${shard}\\"`));
143
178
  }
144
179
  assert.ok(source.includes('["npm", "run", "build"]'));
@@ -151,15 +186,27 @@ test("release runtime and local parity share the checked-in Node contract", asyn
151
186
  assert.doesNotMatch(source, /\["npm", "test"\]/);
152
187
  });
153
188
 
154
- test("cloud verification builds one npm artifact and reuses it across platform acceptance", async () => {
189
+ test("cloud verification builds one npm artifact while independent gates start immediately", async () => {
155
190
  const workflow = await readFile(resolve(repoRoot, ".github/workflows/ci.yml"), "utf8");
156
- assert.match(workflow, /package:[\s\S]*needs:\s*core-contract[\s\S]*run:\s*npm run build[\s\S]*run:\s*npm run traffic:audit[\s\S]*run:\s*npm run lsp:interop[\s\S]*run:\s*npm run release:pack/);
191
+ assert.match(workflow, /package:[\s\S]*run:\s*npm run build[\s\S]*run:\s*npm run lsp:interop[\s\S]*run:\s*npm run release:pack/);
192
+ assert.doesNotMatch(workflow, /package:[\s\S]*needs:\s*core-contract/);
193
+ assert.match(workflow, /traffic-audit:[\s\S]*run:\s*npm run build:app[\s\S]*run:\s*npm run traffic:audit/);
194
+ assert.match(workflow, /macos-platform:[\s\S]*run:\s*npm run ci:platform-tests/);
195
+ assert.match(workflow, /macos-product:[\s\S]*needs:\s*package[\s\S]*run:\s*npm run config:product-accept/);
196
+ assert.match(workflow, /windows-platform:[\s\S]*runtime-process[\s\S]*workspace-filesystem/);
197
+ assert.match(workflow, /windows-product:[\s\S]*needs:\s*package[\s\S]*run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run windows:product-accept/);
198
+ assert.match(workflow, /windows-shell:[\s\S]*needs:\s*package[\s\S]*FORGERELAY_ACCEPTANCE_RUNTIME_ROOT:\s*\.release-runtime[\s\S]*run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run \$\{\{ matrix\.script \}\}/);
199
+ assert.match(workflow, /windows-shell:[\s\S]*id:\s*windows-shell-deps[\s\S]*path:\s*node_modules/);
200
+ assert.match(workflow, /if:\s*steps\.windows-shell-deps\.outputs\.cache-hit == 'true'[\s\S]*run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run ci:prepare-windows-shell-runtime/);
201
+ assert.match(workflow, /if:\s*steps\.windows-shell-deps\.outputs\.cache-hit != 'true'[\s\S]*run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run ci:prepare-windows-product/);
157
202
  assert.equal((workflow.match(/run:\s*npm run release:pack/g) ?? []).length, 1);
158
203
  assert.equal((workflow.match(/uses:\s*actions\/upload-artifact@v7/g) ?? []).length, 1);
159
- assert.equal((workflow.match(/uses:\s*actions\/download-artifact@v7/g) ?? []).length, 3);
204
+ assert.equal((workflow.match(/uses:\s*actions\/download-artifact@v7/g) ?? []).length, 4);
160
205
  assert.match(workflow, /FORGERELAY_ACCEPTANCE_ARTIFACT_DIR:\s*\.release-package/);
161
- assert.match(workflow, /windows-platform:[\s\S]*FORGERELAY_ACCEPTANCE_PREFIX:\s*\.release-installed/);
162
- assert.equal((workflow.match(/run:\s*npm run ci:prepare-windows-product/g) ?? []).length, 1);
206
+ assert.equal((workflow.match(/run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run ci:prepare-windows-product/g) ?? []).length, 2);
207
+ assert.equal((workflow.match(/run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run ci:prepare-windows-shell-runtime/g) ?? []).length, 1);
208
+ assert.match(workflow, /key:\s*windows-npm-cli-\$\{\{ runner\.os \}\}-\$\{\{ runner\.arch \}\}-node-\$\{\{ hashFiles\('\.nvmrc'\) \}\}-npm-11\.19\.1/);
209
+ assert.match(workflow, /npm install --global --prefix \.ci-npm-global npm@11\.19\.1 --no-audit --no-fund --prefer-offline/);
163
210
  assert.equal((workflow.match(/run:\s*npm run traffic:audit/g) ?? []).length, 1);
164
211
  assert.equal((workflow.match(/run:\s*npm run lsp:interop/g) ?? []).length, 1);
165
212
  assert.match(workflow, /name:\s*npm-package/);
@@ -180,16 +227,46 @@ test("packaged acceptance scripts can consume the one downloaded release artifac
180
227
  assert.match(prepareWindows, /resolveAcceptancePrefix/);
181
228
  assert.match(prepareWindows, /"install", "--global", "--prefix"/);
182
229
 
230
+ const prepareShellRuntime = await readFile(
231
+ resolve(repoRoot, "scripts/ci/gates/prepare-windows-shell-runtime.mjs"),
232
+ "utf8",
233
+ );
234
+ assert.match(prepareShellRuntime, /resolveAcceptanceTarball/);
235
+ assert.match(prepareShellRuntime, /--strip-components=1/);
236
+ assert.match(prepareShellRuntime, /FORGERELAY_ACCEPTANCE_RUNTIME_ROOT/);
237
+
238
+ const acceptanceRuntime = await readFile(
239
+ resolve(repoRoot, "scripts/ci/gates/acceptance-runtime.mjs"),
240
+ "utf8",
241
+ );
242
+ assert.match(acceptanceRuntime, /FORGERELAY_ACCEPTANCE_RUNTIME_ROOT/);
243
+ assert.match(acceptanceRuntime, /resolveAcceptancePrefix/);
244
+ assert.match(acceptanceRuntime, /node_modules/);
245
+ assert.match(acceptanceRuntime, /dist", "cli\.js"/);
246
+
183
247
  for (const relativePath of [
184
248
  "scripts/ci/config-v2-product-acceptance.mjs",
185
249
  "scripts/ci/windows-product-acceptance.mjs",
250
+ ]) {
251
+ const source = await readFile(resolve(repoRoot, relativePath), "utf8");
252
+ assert.match(source, /resolveAcceptanceTarball/);
253
+ }
254
+
255
+ for (const relativePath of [
186
256
  "scripts/ci/pwsh-acceptance.mjs",
187
257
  "scripts/ci/powershell51-acceptance.mjs",
188
258
  "scripts/ci/cmd-acceptance.mjs",
189
259
  ]) {
190
260
  const source = await readFile(resolve(repoRoot, relativePath), "utf8");
191
- assert.match(source, /resolveAcceptanceTarball/);
261
+ assert.match(source, /acceptanceRuntimeModuleUrl/);
262
+ assert.doesNotMatch(source, /resolveAcceptanceTarball/);
263
+ assert.doesNotMatch(source, /resolveAcceptancePrefix/);
264
+ assert.doesNotMatch(source, /import\("\.\.\/\.\.\/dist\//);
192
265
  }
266
+
267
+ const windowsProduct = await readFile(resolve(repoRoot, "scripts/ci/windows-product-acceptance.mjs"), "utf8");
268
+ assert.match(windowsProduct, /forgerelay\.ps1/);
269
+ assert.match(windowsProduct, /Command shell source: launcher/);
193
270
  });
194
271
 
195
272
  test("Windows product acceptance uses the supported Skills environment seam rather than a removed config key", async () => {
@@ -212,6 +289,20 @@ test("release workflow is tag-only and promotes the verified npm artifact withou
212
289
  assert.doesNotMatch(workflow, /shell:\s*bash/);
213
290
  });
214
291
 
292
+ test("manual Cloud CI verification can never publish a release", async () => {
293
+ const workflow = await readFile(
294
+ resolve(repoRoot, ".github/workflows/cloud-ci-verification.yml"),
295
+ "utf8",
296
+ );
297
+ assert.match(workflow, /workflow_dispatch:/);
298
+ assert.match(workflow, /uses:\s*\.\/\.github\/workflows\/ci\.yml/);
299
+ assert.match(workflow, /permissions:[\s\S]*contents:\s*read/);
300
+ assert.doesNotMatch(workflow, /release:publish/);
301
+ assert.doesNotMatch(workflow, /npm publish/);
302
+ assert.doesNotMatch(workflow, /contents:\s*write/);
303
+ assert.doesNotMatch(workflow, /id-token:\s*write/);
304
+ });
305
+
215
306
  test("manual Windows shell acceptance can never publish a release", async () => {
216
307
  const workflow = await readFile(
217
308
  resolve(repoRoot, ".github/workflows/windows-shell-acceptance.yml"),
@@ -221,9 +312,26 @@ test("manual Windows shell acceptance can never publish a release", async () =>
221
312
  assert.match(workflow, /runs-on:\s*windows-2022/);
222
313
  assert.match(workflow, /FORGERELAY_ACCEPTANCE_ARTIFACT_DIR:\s*\.release-artifacts/);
223
314
  assert.match(workflow, /FORGERELAY_ACCEPTANCE_PREFIX:\s*\.release-installed/);
315
+ assert.match(workflow, /FORGERELAY_ACCEPTANCE_RUNTIME_ROOT:\s*\.release-runtime/);
224
316
  assert.equal((workflow.match(/run:\s*npm run release:pack/g) ?? []).length, 1);
225
- assert.equal((workflow.match(/run:\s*npm run ci:prepare-windows-product/g) ?? []).length, 1);
226
- assert.match(workflow, /run:\s*npm run pwsh:accept/);
317
+ assert.equal((workflow.match(/uses:\s*actions\/upload-artifact@v7/g) ?? []).length, 1);
318
+ assert.equal((workflow.match(/uses:\s*actions\/download-artifact@v7/g) ?? []).length, 2);
319
+ assert.equal((workflow.match(/run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run ci:prepare-windows-product/g) ?? []).length, 2);
320
+ assert.equal((workflow.match(/run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run ci:prepare-windows-shell-runtime/g) ?? []).length, 1);
321
+ assert.match(workflow, /id:\s*windows-shell-deps[\s\S]*path:\s*node_modules/);
322
+ assert.match(workflow, /key:\s*windows-npm-cli-\$\{\{ runner\.os \}\}-\$\{\{ runner\.arch \}\}-node-\$\{\{ hashFiles\('\.nvmrc'\) \}\}-npm-11\.19\.1/);
323
+ const windowsPlatform = workflow.match(/ windows-platform:[\s\S]*?(?=\n windows-product:)/)?.[0];
324
+ assert.ok(windowsPlatform, "manual Windows platform job must remain present");
325
+ assert.match(windowsPlatform, /uses:\s*actions\/cache@v4/);
326
+ assert.match(windowsPlatform, /id:\s*windows-npm-cli[\s\S]*path:\s*\.ci-npm-global/);
327
+ assert.match(windowsPlatform, /if:\s*steps\.windows-npm-cli\.outputs\.cache-hit != 'true'[\s\S]*npm install --global --prefix \.ci-npm-global npm@11\.19\.1/);
328
+ assert.match(windowsPlatform, /if:\s*steps\.windows-platform-deps\.outputs\.cache-hit != 'true'[\s\S]*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js ci --no-audit --no-fund/);
329
+ assert.match(windowsPlatform, /run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run ci:platform-tests -- \$\{\{ matrix\.shard \}\}/);
330
+ assert.doesNotMatch(windowsPlatform, /npx --yes npm@11\.19\.1/);
331
+ assert.match(workflow, /script:\s*pwsh:accept/);
332
+ assert.match(workflow, /script:\s*powershell51:accept/);
333
+ assert.match(workflow, /script:\s*cmd:accept/);
334
+ assert.match(workflow, /run:\s*node \.ci-npm-global\/node_modules\/npm\/bin\/npm-cli\.js run \$\{\{ matrix\.script \}\}/);
227
335
  assert.doesNotMatch(workflow, /release:publish/);
228
336
  assert.doesNotMatch(workflow, /npm publish/);
229
337
  assert.doesNotMatch(workflow, /contents:\s*write/);
@@ -29,7 +29,18 @@ try {
29
29
  `Node ${NODE_VERSION} / npm ${NPM_VERSION} install`,
30
30
  );
31
31
  runNodeNpm(sandbox, env, ["npm", "run", "ci:contract"], "Cloud core contract");
32
- for (const shard of ["runtime-config", "workspace-mcp", "lsp", "subagent-ui-cli"]) {
32
+ for (const shard of [
33
+ "runtime-config",
34
+ "workspace-relay-auth",
35
+ "workspace-relay",
36
+ "workspace-lifecycle",
37
+ "workspace-state",
38
+ "mcp-core",
39
+ "mcp-server-ui",
40
+ "lsp",
41
+ "subagent",
42
+ "ui-cli",
43
+ ]) {
33
44
  runNodeNpm(
34
45
  sandbox,
35
46
  env,
@@ -1 +0,0 @@
1
- .forgerelay-panel{border:1px solid var(--tool-card-border);background:var(--tool-card-header-bg);width:100%;color:var(--color-text-primary,#f5f5f6);border-radius:12px;overflow:hidden}.workspace-panel{background:var(--tool-card-header-bg);width:100%}.workspace-panel-header{grid-template-columns:22px minmax(0,1fr) auto;align-items:center;gap:10px;min-height:58px;padding:9px 12px;display:grid}.workspace-panel-icon{background:color-mix(in srgb, var(--tool-accent) 9%, transparent);width:22px;height:22px;color:color-mix(in srgb, var(--tool-accent) 72%, var(--color-text-tertiary,#a3a3aa));border-radius:6px;place-items:center;display:grid}.workspace-panel-icon-svg{stroke-width:1.8px;width:14px;height:14px}.workspace-panel-title-group{gap:2px;min-width:0;display:grid}.workspace-panel-title{font-size:var(--font-text-sm-size,14px);font-weight:600;line-height:1.3}.workspace-panel-subtitle,.workspace-panel-mode{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,11px);line-height:1.35}.workspace-panel-subtitle{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.workspace-panel-mode{border:1px solid color-mix(in srgb, var(--tool-card-divider) 76%, transparent);white-space:nowrap;border-radius:999px;padding:2px 7px}.workspace-panel .workspace-details{overscroll-behavior:contain;border-top:1px solid var(--tool-card-divider);background:var(--tool-card-body-bg);max-height:420px;overflow:hidden auto}.workspace-panel-pending-dot{background:var(--color-text-info,#38bdf8);width:8px;height:8px;box-shadow:0 0 0 3px color-mix(in srgb, var(--color-text-info,#38bdf8) 12%, transparent);border-radius:999px;justify-self:center}.activity-panel{border:1px solid var(--tool-card-border);background:var(--tool-card-header-bg);width:100%;color:var(--color-text-primary,#f5f5f6);border-radius:12px;overflow:hidden}.forgerelay-panel .activity-panel{border:0;border-top:1px solid var(--tool-card-divider);border-radius:0}.activity-panel-header{width:100%;min-height:58px;color:inherit;cursor:pointer;font:inherit;text-align:left;background:0 0;border:0;grid-template-columns:14px minmax(0,1fr) auto 20px;align-items:center;gap:10px;padding:9px 12px;display:grid}.activity-panel-header:hover{background:var(--tool-card-hover-bg)}.activity-panel-header:focus-visible{outline:2px solid color-mix(in srgb, var(--color-text-info,#38bdf8) 72%, transparent);outline-offset:-2px}.activity-panel-header-pending{cursor:default}.activity-panel-header-pending:hover{background:0 0}.activity-panel-pending-spacer{width:20px}.activity-panel-status{background:var(--color-text-info,#38bdf8);width:8px;height:8px;box-shadow:0 0 0 3px color-mix(in srgb, var(--color-text-info,#38bdf8) 12%, transparent);border-radius:9999px;justify-self:center}.activity-panel-status.state-done{background:var(--color-success-text,#6fda83);box-shadow:0 0 0 3px color-mix(in srgb, var(--color-success-text,#6fda83) 12%, transparent)}.activity-panel-status.state-error{background:var(--color-danger-text,#ee7676);box-shadow:0 0 0 3px color-mix(in srgb, var(--color-danger-text,#ee7676) 12%, transparent)}.activity-panel-title-group{gap:2px;min-width:0;display:grid}.activity-panel-title{font-size:var(--font-text-sm-size,14px);font-weight:600;line-height:1.3}.activity-panel-subtitle,.activity-panel-count{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,11px);line-height:1.35}.activity-panel-count{white-space:nowrap;font-weight:600}.activity-panel-count.state-working{color:var(--color-text-info,#38bdf8)}.activity-panel-count.state-done{color:var(--color-success-text,#6fda83)}.activity-panel-count.state-error{color:var(--color-danger-text,#ee7676)}.activity-panel-body{border-top:1px solid var(--tool-card-divider);background:var(--tool-card-body-bg);display:grid}.activity-viewport{overscroll-behavior:contain;max-height:420px;overflow:hidden auto}.activity-list{display:grid}.activity-group+.activity-group{border-top:1px solid var(--tool-card-divider)}.activity-group.grouped>.activity-row.parent{background:color-mix(in srgb, var(--color-background-secondary,#272727) 54%, transparent)}.activity-children{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 72%, transparent);display:grid}.activity-row{--activity-accent:var(--color-text-secondary,#b6b6bd);--activity-phase:var(--color-text-tertiary,#a3a3aa);width:100%;min-height:46px;color:inherit;font:inherit;text-align:left;background:0 0;border:0;grid-template-columns:28px minmax(0,1fr) auto 16px;align-items:center;gap:10px;padding:7px 12px;display:grid}.activity-row.interactive{cursor:pointer}.activity-row.interactive:hover{background:var(--tool-card-hover-bg)}.activity-row.interactive:focus-visible{outline:2px solid color-mix(in srgb, var(--activity-accent) 68%, transparent);outline-offset:-2px}.activity-row.child{padding-left:34px;position:relative}.activity-row.child:before{background:color-mix(in srgb, var(--activity-accent) 26%, var(--tool-card-divider));content:"";width:1px;position:absolute;top:0;bottom:0;left:20px}.activity-row.child+.activity-row.child{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 52%, transparent)}.activity-row.kind-read{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 32%, #06b6d4 68%)}.activity-row.kind-write{--activity-accent:var(--color-success-text,#6fda83)}.activity-row.kind-edit,.activity-row.kind-rename{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 28%, #d99742 72%)}.activity-row.kind-delete{--activity-accent:var(--color-danger-text,#ee7676)}.activity-row.kind-shell{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 42%, #64748b 58%)}.activity-row.kind-shell-result{--activity-accent:color-mix(in srgb, var(--color-success-text,#6fda83) 72%, var(--color-text-secondary,#b6b6bd))}.activity-row.kind-capability{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 34%, #3b82f6 66%)}.activity-row.kind-batch{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 34%, #8b5cf6 66%)}.activity-row.phase-executing{--activity-phase:var(--color-text-info,#38bdf8)}.activity-row.phase-returned{--activity-phase:var(--color-warning-text,#e6b566)}.activity-row.phase-done{--activity-phase:var(--color-success-text,#6fda83)}.activity-row.phase-error{--activity-phase:var(--color-danger-text,#ee7676)}.activity-row.phase-executing{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--activity-phase) 76%, transparent);background:color-mix(in srgb, var(--activity-phase) 5%, transparent)}.activity-row.phase-returned{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--activity-phase) 68%, transparent)}.activity-row.phase-error{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--activity-phase) 72%, transparent)}.activity-row.shell-result{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--activity-accent) 68%, transparent)}.activity-icon{border:1px solid color-mix(in srgb, var(--activity-accent) 18%, transparent);background:color-mix(in srgb, var(--activity-accent) 9%, transparent);width:28px;height:28px;color:var(--activity-accent);border-radius:7px;place-items:center;display:grid}.activity-icon-svg{stroke-width:1.8px;width:15px;height:15px}.activity-main{gap:4px;min-width:0;display:grid}.activity-title-line{align-items:baseline;gap:8px;min-width:0;display:flex}.activity-title{color:var(--color-text-primary,#f5f5f6);font-size:var(--font-text-sm-size,12px);flex:none;font-weight:600;line-height:1.35}.activity-member{color:var(--color-text-secondary,#d4d4d8);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,10px);flex:none;line-height:1.4}.activity-target{min-width:0;color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,11px);text-overflow:ellipsis;white-space:nowrap;line-height:1.4;overflow:hidden}.activity-meta{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,10px);font-variant-numeric:tabular-nums;white-space:nowrap;justify-content:flex-end;align-items:center;gap:8px;display:inline-flex}.activity-phase{color:var(--activity-phase);align-items:center;gap:5px;display:inline-flex}.activity-phase:before{content:"";background:currentColor;border-radius:9999px;width:6px;height:6px}.activity-progress-wrap{grid-template-columns:auto minmax(48px,110px);align-items:center;gap:8px;max-width:220px;display:grid}.activity-progress-counts{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,10px);font-variant-numeric:tabular-nums;white-space:nowrap}.activity-progress-track{background:color-mix(in srgb, var(--activity-accent) 14%, var(--tool-card-divider));border-radius:9999px;height:3px;display:block;overflow:hidden}.activity-progress-fill{border-radius:inherit;background:var(--activity-accent);height:100%;display:block}.activity-empty,.activity-refresh-error{color:var(--color-text-secondary,#b7b7bf);font-size:var(--font-text-sm-size,12px);padding:12px}.activity-refresh-error{border-top:1px solid var(--tool-card-divider);color:var(--color-danger-text,#ee7676)}.activity-detail-chevron,.activity-detail-spacer,.activity-detail-chevron.chevron{width:16px;height:16px}.activity-detail-chevron .icon-svg{width:13px;height:13px}.activity-entry.expanded>.activity-row{background:color-mix(in srgb, var(--activity-accent) 6%, transparent)}.activity-detail{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 72%, transparent);background:color-mix(in srgb, var(--color-background-primary,#101114) 90%, transparent);display:grid}.activity-detail-section+.activity-detail-section{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 62%, transparent)}.activity-detail-rich{min-width:0;overflow:hidden}.activity-detail-rich .pierre-diff,.activity-detail-rich .pierre-file{border-radius:0}.activity-detail-label{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,10px);letter-spacing:.02em;padding:8px 12px 0;font-weight:600}.activity-detail-section.error .activity-detail-label,.activity-detail-section.error .activity-detail-value{color:var(--color-danger-text,#ee7676)}.activity-detail-value{max-height:260px;color:var(--color-text-secondary,#c7c7ce);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,11px);white-space:pre-wrap;overflow-wrap:break-word;margin:0;padding:6px 12px 10px;line-height:1.5;overflow:auto}.activity-detail-status{color:var(--color-text-secondary,#b7b7bf);font-size:var(--font-text-sm-size,12px);padding:10px 12px}.activity-detail-status.error{color:var(--color-danger-text,#ee7676)}.activity-terminal{background:var(--color-background-primary,#101114)}.activity-terminal-command,.activity-terminal-output{color:var(--color-text-primary,#f5f5f6);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,11px);white-space:pre-wrap;overflow-wrap:break-word;background:0 0;border:0;border-radius:0;margin:0;line-height:1.55}.activity-terminal-command{border-bottom:1px solid color-mix(in srgb, var(--tool-card-divider) 74%, transparent);color:var(--color-text-secondary,#c7c7ce);padding:9px 12px}.activity-terminal-output{max-height:320px;padding:10px 12px;overflow:auto}.activity-terminal-meta{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 74%, transparent);color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,10px);font-variant-numeric:tabular-nums;padding:7px 12px}.activity-terminal-meta.status-running{color:var(--color-text-info,#38bdf8)}.activity-terminal-meta.status-done{color:var(--color-success-text,#6fda83)}.activity-terminal-meta.status-failed{color:var(--color-danger-text,#ee7676)}@media (width<=520px){.activity-panel-header{grid-template-columns:12px minmax(0,1fr) auto 18px;gap:8px;min-height:54px;padding:8px 10px}.activity-panel-subtitle,.activity-panel-count{font-size:10px}.activity-row{grid-template-columns:26px minmax(0,1fr) 18px;gap:6px 8px;min-height:48px;padding:8px 10px}.activity-row.child{padding-left:28px}.activity-row.child:before{left:16px}.activity-icon{align-self:start;width:26px;height:26px}.activity-title-line{gap:2px;display:grid}.activity-meta{grid-column:2;justify-content:flex-start}.activity-detail-chevron,.activity-detail-spacer{grid-area:1/3/span 2}.activity-progress-wrap{grid-template-columns:auto minmax(40px,1fr);max-width:none}}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;font-family:var(--font-sans,ui-sans-serif, system-ui, sans-serif);color:var(--color-text-primary,#f5f5f6);--tool-card-border:color-mix(in srgb, var(--color-border-primary,#414141) 74%, transparent);--tool-card-header-bg:color-mix(in srgb, var(--color-background-secondary,#272727) 88%, transparent);--tool-card-body-bg:color-mix(in srgb, var(--color-background-primary,#181818) 94%, transparent);--tool-card-hover-bg:color-mix(in srgb, var(--color-background-tertiary,#343434) 46%, transparent);--tool-card-divider:color-mix(in srgb, var(--color-border-primary,#414141) 66%, transparent);--tool-accent:var(--color-text-secondary,#b6b6bd);--scrollbar-thumb:color-mix(in srgb, var(--color-text-tertiary,#8a8a8a) 56%, transparent);--scrollbar-thumb-hover:color-mix(in srgb, var(--color-text-secondary,#a8a8a8) 82%, transparent);background:0 0}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}*{box-sizing:border-box}html,body{background:0 0;margin:0;overflow:hidden}.shell{width:100%;padding:0;overflow:hidden}.empty,.tool-card{--tool-accent-soft:color-mix(in srgb, var(--tool-accent) 12%, transparent);border:1px solid var(--tool-card-border);background:var(--tool-card-header-bg);width:100%;box-shadow:none;color:var(--color-text-primary,#f5f5f6);border-radius:12px;overflow:hidden}.tool-card.workspace,.tool-card.directory{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 34%, #3b82f6 66%)}.tool-card.read,.tool-card.search{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 32%, #06b6d4 68%)}.tool-card.write{--tool-accent:var(--color-success-text,#6fda83)}.tool-card.edit,.tool-card.review{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 28%, #d99742 72%)}.tool-card.delete{--tool-accent:var(--color-danger-text,#ee7676)}.tool-card.shell{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 42%, #64748b 58%)}.tool-card.state-success{--tool-accent:var(--color-success-text,#6fda83)}.tool-card.state-error{--tool-accent:var(--color-danger-text,#ee7676)}.tool-card.state-running{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 30%, #38bdf8 70%)}@supports selector(::-webkit-scrollbar){.pretty-scrollbar::-webkit-scrollbar{width:12px;height:12px}.pretty-scrollbar::-webkit-scrollbar-button{width:0;height:0;display:none}.pretty-scrollbar::-webkit-scrollbar-track{background:0 0}.pretty-scrollbar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb);background-clip:content-box;border:4px solid #0000;border-radius:9999px}.pretty-scrollbar::-webkit-scrollbar-thumb:hover{background-color:var(--scrollbar-thumb-hover)}.pretty-scrollbar::-webkit-scrollbar-thumb:active{background-color:var(--scrollbar-thumb-hover)}.pretty-scrollbar::-webkit-scrollbar-corner{background:0 0}}.empty{color:var(--color-text-secondary,#b6b6bd);font-size:var(--font-text-sm-size,13px);padding:14px 16px}.tool-header{width:100%;min-height:64px;color:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:11px;grid-template-columns:40px minmax(0,1fr) auto 20px;align-items:center;gap:12px;padding:10px 12px;display:grid}.tool-header:focus-visible,.review-diff-file-header:focus-visible,.review-more:focus-visible{outline:2px solid color-mix(in srgb, var(--tool-accent) 72%, transparent);outline-offset:-2px}.tool-header:hover:not(:disabled){background:var(--tool-card-hover-bg)}.tool-header:disabled{cursor:default}.tool-icon{border:1px solid color-mix(in srgb, var(--tool-accent) 18%, transparent);background:var(--tool-accent-soft);width:40px;height:40px;color:var(--tool-accent);border-radius:10px;place-items:center;display:grid}.icon-svg{stroke-width:1.8px;width:20px;height:20px;display:block}.tool-main{gap:2px;min-width:0;display:grid}.tool-title{color:var(--color-text-primary,#f5f5f6);font-size:var(--font-text-sm-size,14px);font-weight:550;line-height:1.3}.tool-label{color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,12px);text-overflow:ellipsis;white-space:nowrap;line-height:1.4;overflow:hidden}.stats{font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,12px);font-variant-numeric:tabular-nums;white-space:nowrap;align-items:center;gap:5px;display:inline-flex}.header-meta{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-sm-size,12px);text-align:right;white-space:nowrap;line-height:1.35}.header-meta.empty{width:0}.add{color:var(--color-success-text,#6fda83)}.remove{color:var(--color-danger-text,#ee7676)}.chevron{width:20px;height:20px;color:var(--color-text-tertiary,#a3a3aa);border-radius:7px;place-items:center;transition:background .14s,color .14s,transform .14s;display:grid}.tool-header:hover:not(:disabled) .chevron{color:var(--color-text-primary,#f5f5f6)}.chevron .icon-svg{width:15px;height:15px}.chevron.expanded{transform:rotate(180deg)}.chevron.loading{transform:none}.chevron.loading .icon-svg{fill:none;stroke-linecap:round;stroke-dasharray:38 14;animation:.7s linear infinite payload-spinner}@keyframes payload-spinner{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.chevron.loading .icon-svg{animation:none}}.tool-body{border-top:1px solid var(--tool-card-divider);background:var(--tool-card-body-bg)}.workspace-details{max-height:420px;display:grid;overflow:auto}.workspace-rows{padding:4px 0;display:grid}.workspace-row{--workspace-row-accent:var(--tool-accent);grid-template-columns:22px minmax(116px,.24fr) minmax(0,1fr);align-items:center;gap:10px;min-height:40px;padding:7px 12px;display:grid}.workspace-row-icon{background:color-mix(in srgb, var(--workspace-row-accent) 9%, transparent);width:22px;height:22px;color:color-mix(in srgb, var(--workspace-row-accent) 72%, var(--color-text-tertiary,#a3a3aa));border-radius:6px;place-items:center;display:grid}.workspace-row-icon-svg{stroke-width:1.8px;width:14px;height:14px}.workspace-key{min-height:22px;color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-sm-size,12px);align-items:center;font-weight:500;display:flex}.workspace-value{min-width:0;color:var(--color-text-secondary,#c7c7ce);font-size:var(--font-text-sm-size,12px);text-overflow:ellipsis;white-space:nowrap;line-height:1.45;overflow:hidden}.workspace-value.mono{font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace)}.workspace-base-value{align-items:center;gap:7px;min-width:0;display:flex}.workspace-base-value .workspace-value{flex:0 auto}.workspace-base-warning{width:18px;height:18px;color:var(--color-warning-text,#e6b566);cursor:help;flex:none;place-items:center;display:grid}.workspace-base-warning-svg{stroke-width:2px;width:14px;height:14px}.workspace-chip-list{flex-wrap:nowrap;align-items:center;gap:6px;min-width:0;display:flex;overflow:hidden}.workspace-chip{border:1px solid color-mix(in srgb, var(--tool-accent) 16%, var(--tool-card-divider));background:color-mix(in srgb, var(--tool-accent) 7%, transparent);max-width:100%;min-height:24px;color:var(--color-text-secondary,#c7c7ce);text-overflow:ellipsis;white-space:nowrap;border-radius:9999px;flex:none;align-items:center;gap:5px;padding:3px 8px;font-size:11px;line-height:1.25;display:inline-flex;overflow:hidden}.workspace-chip-logo{object-fit:contain;flex:none;width:13px;height:13px;display:block}.workspace-chip-label{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.workspace-agent-profile{border:0;border-bottom:1px solid color-mix(in srgb, var(--tool-accent) 34%, var(--tool-card-divider));max-width:100%;min-height:24px;color:var(--color-text-secondary,#c7c7ce);white-space:nowrap;background:0 0;border-radius:0;align-items:center;gap:5px;padding:3px 2px 4px;font-size:11px;line-height:1.25;display:inline-flex;overflow:hidden}.workspace-agent-profile-logo{object-fit:contain;flex:none;width:14px;height:14px;display:block}.workspace-agent-profile:hover{border-bottom-color:var(--tool-accent);color:var(--color-text-primary,#f5f5f6)}.workspace-agent-profile.muted{color:var(--color-text-tertiary,#a3a3aa);opacity:.72;border-bottom-style:dashed}.workspace-provider-logo{cursor:help;flex:none;place-items:center;width:20px;height:24px;display:inline-grid}.workspace-provider-logo-image{object-fit:contain;width:16px;height:16px;display:block}.workspace-provider-logo.muted{opacity:.62}.workspace-chip.muted{color:var(--color-text-tertiary,#a3a3aa);opacity:.72;border-style:dashed}.workspace-skills-row,.workspace-instructions-row,.workspace-agents-row{align-items:start}.workspace-instructions-row{--workspace-row-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 28%, #d99742 72%)}.workspace-skills-row{--workspace-row-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 28%, #14b8a6 72%)}.workspace-agents-row{--workspace-row-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 30%, #8b5cf6 70%)}.workspace-skills-row .workspace-chip,.workspace-agents-row .workspace-chip,.workspace-agents-row .workspace-agent-profile{border-color:color-mix(in srgb, var(--workspace-row-accent) 28%, var(--tool-card-divider))}.workspace-skills-row .workspace-chip{background:color-mix(in srgb, var(--workspace-row-accent) 7%, transparent)}.workspace-skills-list,.workspace-agents-list{flex-wrap:wrap;overflow:visible}.workspace-instruction-status{border-radius:5px;flex:none;place-items:center;width:18px;height:18px;display:grid}.workspace-instruction-status.loaded{background:color-mix(in srgb, var(--color-success-text,#6fda83) 12%, transparent);color:var(--color-success-text,#6fda83)}.workspace-instruction-status.available,.workspace-instruction-status.disabled{background:color-mix(in srgb, var(--color-text-tertiary,#a3a3aa) 9%, transparent);color:var(--color-text-tertiary,#a3a3aa)}.workspace-instruction-status.disabled{opacity:.65}.workspace-instruction-status.unavailable{background:color-mix(in srgb, var(--color-warning-text,#e6b566) 12%, transparent);color:var(--color-warning-text,#e6b566)}.workspace-instruction-status-svg{stroke-width:1.9px;width:12px;height:12px}.workspace-instruction-list{border:1px solid var(--tool-card-divider);background:color-mix(in srgb, var(--tool-card-body-bg) 88%, transparent);border-radius:9px;min-width:0;display:grid;overflow:hidden}.workspace-instructions-content{min-width:0;display:block}.workspace-instructions-toggle{border:0;border-top:1px solid var(--tool-card-divider);background:color-mix(in srgb, var(--tool-card-body-bg) 72%, transparent);width:100%;min-height:32px;color:var(--tool-accent);cursor:pointer;font:inherit;font-size:var(--font-text-sm-size,11px);white-space:nowrap;border-radius:0 0 9px 9px;justify-content:center;align-items:center;padding:6px 10px;font-weight:550;line-height:1.25;display:flex}.workspace-instructions-toggle:hover{background:var(--tool-card-hover-bg)}.workspace-instructions-toggle:focus-visible{outline:2px solid color-mix(in srgb, var(--tool-accent) 72%, transparent);outline-offset:2px}.workspace-instruction-item+.workspace-instruction-item{border-top:1px solid var(--tool-card-divider)}.workspace-instruction-header{width:100%;min-width:0;color:inherit;font:inherit;text-align:left;background:0 0;border:0;grid-template-columns:22px minmax(0,1fr) 18px;align-items:center;gap:9px;padding:8px 10px;display:grid}.workspace-instruction-header.interactive{cursor:pointer}.workspace-instruction-header.interactive:hover{background:var(--tool-card-hover-bg)}.workspace-instruction-header.interactive:focus-visible{outline:2px solid color-mix(in srgb, var(--tool-accent) 72%, transparent);outline-offset:-2px}.workspace-instruction-text{gap:2px;min-width:0;display:grid}.workspace-instruction-name{min-width:0;color:var(--color-text-primary,#f5f5f6);font-size:var(--font-text-sm-size,12px);text-overflow:ellipsis;white-space:nowrap;font-weight:550;overflow:hidden}.workspace-instruction-path{min-width:0;color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);text-overflow:ellipsis;white-space:nowrap;font-size:10px;line-height:1.35;overflow:hidden}.workspace-instruction-chevron{width:18px;height:18px;color:var(--color-text-tertiary,#a3a3aa);place-items:center;transition:transform .14s;display:grid}.workspace-instruction-item.expanded .workspace-instruction-chevron{transform:rotate(180deg)}.workspace-instruction-chevron-svg{width:14px;height:14px}.workspace-instruction-preview{border-top:1px solid var(--tool-card-divider);background:color-mix(in srgb, var(--color-background-primary,#101114) 92%, transparent);max-height:300px;color:var(--color-text-secondary,#c7c7ce);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);white-space:pre-wrap;overflow-wrap:break-word;margin:0;padding:10px 12px;font-size:11px;line-height:1.55;overflow:auto}.workspace-instruction-preview[hidden]{display:none}.review-header{grid-template-columns:40px minmax(0,1fr) auto 20px}.review-title-group{gap:3px;min-width:0;display:grid}.review-summary{border-top:1px solid var(--tool-card-divider);background:var(--tool-card-body-bg);display:grid}.review-diff-file-stats{align-items:center;gap:8px;display:flex}.review-empty{color:var(--color-text-secondary,#b7b7bf);font-size:var(--font-text-sm-size,13px)}.review-more{border:0;border-top:1px solid var(--tool-card-divider);width:100%;min-height:40px;color:var(--color-text-tertiary,#a3a3aa);cursor:pointer;font:inherit;font-size:var(--font-text-sm-size,12px);text-align:left;background:0 0;padding:0 12px}.review-more:hover{background:var(--tool-card-hover-bg);color:var(--color-text-primary,#f5f5f6)}.review-diff{max-height:520px;display:grid;overflow:hidden auto}.review-diff-files{gap:0;padding:0;display:grid}.review-diff-file{border:0;border-radius:0;overflow:hidden}.review-diff-file+.review-diff-file{border-top:1px solid var(--tool-card-divider)}.review-diff-file-header{width:100%;min-height:42px;color:var(--color-text-primary,#f5f5f6);cursor:pointer;font:inherit;text-align:left;background:0 0;border:0;grid-template-columns:22px minmax(0,1fr) auto;align-items:center;gap:10px;padding:0 12px;display:grid}.review-file-kind{background:color-mix(in srgb, var(--color-text-tertiary,#a3a3aa) 10%, transparent);width:20px;height:20px;color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);border-radius:6px;align-self:center;place-items:center;font-size:10px;font-weight:700;display:grid}.review-file-kind.added{background:color-mix(in srgb, var(--color-success-text,#6fda83) 12%, transparent);color:var(--color-success-text,#6fda83)}.review-file-kind.edited,.review-file-kind.renamed,.review-file-kind.renamed-edited{background:color-mix(in srgb, var(--color-warning-text,#e6b566) 12%, transparent);color:var(--color-warning-text,#e6b566)}.review-file-kind.deleted{background:color-mix(in srgb, var(--color-danger-text,#ee7676) 12%, transparent);color:var(--color-danger-text,#ee7676)}.review-single-file{overflow:hidden}.review-diff-file-header:hover{background:var(--tool-card-hover-bg)}.review-diff-file-name,.review-diff-file-stats{text-overflow:ellipsis;white-space:nowrap;font-size:13px;line-height:20px;overflow:hidden}.review-diff-file-name{font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace)}.review-diff-file-name.renamed{text-overflow:clip;align-items:center;gap:6px;min-width:0;display:flex}.review-diff-file-path{text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:calc(50% - 10px);overflow:hidden}.review-diff-file-path.previous{color:var(--color-text-tertiary,#a3a3aa)}.review-diff-file-path.current{color:var(--color-text-primary,#f5f5f6)}.review-diff-file-arrow{color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-sans,ui-sans-serif, system-ui, sans-serif);flex:none}.review-diff-file-stats{font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-sm-size,12px);font-variant-numeric:tabular-nums;justify-content:flex-end;overflow:visible}.status{font-size:var(--font-text-sm-size,12px);padding:10px 12px}.status.muted{color:var(--color-text-secondary,#b7b7bf)}.status.error{color:var(--color-danger-text,#ee7676)}.pierre-diff,.pierre-file{--diffs-bg:var(--tool-payload-bg,var(--color-background-primary,#101114));--diffs-light-bg:var(--color-background-primary,#fff);--diffs-dark-bg:var(--tool-payload-bg,var(--color-background-primary,#101114));--diffs-font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);--diffs-header-font-family:var(--font-sans,ui-sans-serif, system-ui, sans-serif);--diffs-font-size:var(--font-text-sm-size,12px);--diffs-line-height:20px;border-bottom-right-radius:8px;border-bottom-left-radius:8px;max-height:420px;display:block;overflow:auto}.text-payload{max-height:420px;color:var(--color-text-secondary,#c7c7ce);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-sm-size,12px);white-space:pre-wrap;overflow-wrap:break-word;margin:0;padding:10px 12px;line-height:1.55;overflow:auto}.text-payload.bash{color:var(--color-text-primary,#f5f5f6);background:var(--color-background-primary,#101114)}@media (width<=520px){.tool-header{grid-template-columns:36px minmax(0,1fr) auto 18px;gap:9px;min-height:58px;padding:9px 10px}.tool-icon{border-radius:9px;width:36px;height:36px}.chevron{width:18px;height:18px}.review-header{grid-template-columns:36px minmax(0,1fr) auto 18px}.workspace-row{grid-template-columns:22px minmax(0,1fr);gap:2px 8px;padding-block:8px}.workspace-row-icon{grid-row:1/span 2;align-self:start}.workspace-row>.workspace-key,.workspace-row>.workspace-value,.workspace-row>.workspace-chip-list,.workspace-row>.workspace-instructions-content{grid-column:2}}